diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml deleted file mode 100644 index 64febb2058..0000000000 --- a/.azure-pipelines.yml +++ /dev/null @@ -1,148 +0,0 @@ -# Azure Pipelines for Valhalla -# -# TODO: Shallow clone blocked by -# https://developercommunity.visualstudio.com/content/problem/294872/yaml-build-ignores-fetchdepth.html -# TODO: Once CMake 3.13 is available replace -H with -S option -# - -# No wildcards allowed for file patterns... -# skip draft PR's, skip all commits except for PRs to master -trigger: - branches: - include: - - master -pr: - branches: - include: - - master - paths: - exclude: - - README.md - - CHANGELOG.md - - bench/ - - docs/ - - run_route_scripts/ - - test/ - - .circleci/ - - .github/ - drafts: false - -jobs: - - job: VS2022 - displayName: 'Windows 2022 | VS2022' - timeoutInMinutes: 120 - - pool: - vmImage: 'windows-2022' - - variables: - BUILD_CFG: 'Release' - BUILD_DIR: '$(Agent.WorkFolder)\build' - VCPKG_DIR: '$(Build.SourcesDirectory)\vcpkg' - VCPKG_ROOT: '$(Build.SourcesDirectory)\vcpkg' - VCPKG_INSTALLATION_ROOT: '$(Build.SourcesDirectory)\vcpkg' - VCPKG_REF: '45b0e4b' - TRIPLET: 'x64' - CONAN_HOME: '$(Build.SourcesDirectory)/conan' - - steps: - - script: | - git submodule update --init --recursive - echo $(Build.SourceBranch) - displayName: 'Pull submodules' - - - task: Cache@2 - displayName: "Cache vcpkg's packages" - inputs: - key: .\.vcpkg_deps.txt | "$(VCPKG_REF)" | "$(TRIPLET)" | ".v2" - path: "$(VCPKG_DIR)" - cacheHitVar: CACHE_RESTORED - - - task: Cache@2 - displayName: "Cache conan packages" - inputs: - key: '"msvc-v16.10.0" | conan | .\conanfile.txt | "$(Build.SourceBranch)" | "v2"' - path: "$(CONAN_HOME)" - cacheHitVar: BUILD_CACHE_RESTORED - - # TODO: cache build never worked, look into it - # - task: Cache@2 - # displayName: "Cache build" - # inputs: - # key: '"msvc-v16.10.0" | build | "$(Build.SourceBranch)"' - # path: "$(BUILD_DIR)" - # restoreKeys: | - # "msvc-v16.10.0" | build | "$(Build.SourceBranch)" - # "msvc-v16.10.0" | build - # cacheHitVar: BUILD_CACHE_RESTORED - - - task: UsePythonVersion@0 - inputs: - versionSpec: '3.x' - addToPath: true - architecture: "$(TRIPLET)" - - - script: | - git clone https://github.com/microsoft/vcpkg %VCPKG_DIR% - git -C %VCPKG_DIR% checkout %VCPKG_REF% - echo.set(VCPKG_BUILD_TYPE release)>> %VCPKG_DIR%\triplets\%TRIPLET%-windows.cmake - call %VCPKG_DIR%\bootstrap-vcpkg.bat - %VCPKG_DIR%\vcpkg.exe version - displayName: 'Install vcpkg' - condition: ne(variables.CACHE_RESTORED, 'true') - - - script: | - %VCPKG_DIR%\vcpkg.exe install --triplet %TRIPLET%-windows "@.vcpkg_deps.txt" - %VCPKG_DIR%\vcpkg.exe list - if exist %VCPKG_DIR%\downloads rmdir /S /Q %VCPKG_DIR%\downloads - if exist %VCPKG_DIR%\packages rmdir /S /Q %VCPKG_DIR%\packages - displayName: 'Install vcpkg packages' - condition: ne(variables.CACHE_RESTORED, 'true') - - - script: | - move /Y third_party\OSM-binary\src\fileformat.proto third_party\OSM-binary\src\fileformat.proto.orig - move /Y third_party\OSM-binary\src\osmformat.proto third_party\OSM-binary\src\osmformat.proto.orig - echo syntax = "proto2"; > third_party\OSM-binary\src\fileformat.proto - type third_party\OSM-binary\src\fileformat.proto.orig >> third_party\OSM-binary\src\fileformat.proto - echo syntax = "proto2"; > third_party\OSM-binary\src\osmformat.proto - type third_party\OSM-binary\src\osmformat.proto.orig >> third_party\OSM-binary\src\osmformat.proto - del /Q third_party\OSM-binary\src\fileformat.proto.orig - del /Q third_party\OSM-binary\src\osmformat.proto.orig - displayName: 'Patch .proto files of OSMPBF with syntax=proto2' - - - powershell: | - wget https://github.com/ninja-build/ninja/releases/download/v1.11.0/ninja-win.zip -o $(Build.SourcesDirectory)\ninja_win.zip - Expand-Archive $(Build.SourcesDirectory)\ninja_win.zip -DestinationPath $(Build.SourcesDirectory)\ninja - displayName: 'Download Ninja' - - - script: | - pip install "conan<2.0.0" - displayName: Install conan - - - script: | - SET PATH=%PATH%;$(Build.SourcesDirectory)\ninja - pushd "C:\Program Files (x86)\Microsoft Visual Studio\Installer\" - for /f "delims=" %%x in ('.\vswhere.exe -latest -property InstallationPath') do set VSPATH=%%x - popd - call "%VSPATH%\VC\Auxiliary\Build\vcvars64.bat" - cmake --version - cmake -G "Ninja" -H$(Build.SourcesDirectory) -B%BUILD_DIR% -DCMAKE_BUILD_TYPE=%BUILD_CFG% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%\scripts\buildsystems\vcpkg.cmake -DVCPKG_APPLOCAL_DEPS=ON -DENABLE_DATA_TOOLS=ON -DENABLE_TOOLS=ON -DENABLE_PYTHON_BINDINGS=ON -DENABLE_TESTS=OFF -DENABLE_CCACHE=OFF -DENABLE_HTTP=OFF -DENABLE_SERVICES=OFF -DENABLE_BENCHMARKS=OFF - env: - CONAN_USER_HOME: $(CONAN_HOME) - displayName: 'Run CMake to configure build' - - - script: | - SET PATH=%PATH%;$(Build.SourcesDirectory)\ninja - pushd "C:\Program Files (x86)\Microsoft Visual Studio\Installer\" - for /f "delims=" %%x in ('.\vswhere.exe -latest -property InstallationPath') do set VSPATH=%%x - popd - call "%VSPATH%\VC\Auxiliary\Build\vcvars64.bat" - cmake --build %BUILD_DIR% --config %BUILD_CFG% - displayName: 'Run CMake to build' - - - script: | - SET PATH=%PATH%;%VCPKG_DIR%\installed\%TRIPLET%-windows\bin;%VCPKG_DIR%\installed\%TRIPLET%-windows\debug\bin - %BUILD_DIR%\valhalla_build_tiles.exe -c .\test\win\valhalla.json .\test\data\utrecht_netherlands.osm.pbf - %BUILD_DIR%\valhalla_run_route.exe --config .\test\win\valhalla.json -j "{\"locations\": [{\"lat\": 52.10205, \"lon\": 5.114651}, {\"lat\": 52.093113, \"lon\": 5.100918}], \"costing\": \"auto\"}" - %BUILD_DIR%\valhalla_run_isochrone.exe --config .\test\win\valhalla.json -j "{\"locations\": [{\"lat\": 52.10205, \"lon\": 5.114651}], \"costing\": \"auto\", \"contours\":[{\"time\":15,\"color\":\"ff0000\"}]}" - displayName: 'Test some executables' diff --git a/.circleci/ciignore b/.circleci/ciignore deleted file mode 100644 index 0418c2723e..0000000000 --- a/.circleci/ciignore +++ /dev/null @@ -1,6 +0,0 @@ -*.md -.circleci/ciignore -run_route_scripts/* -.azure-pipelines.yml -.vcpkg_deps.txt -.github/* diff --git a/.circleci/config.yml b/.circleci/config.yml index d8b378b17d..68f9e8b288 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,66 +1,51 @@ version: 2.1 -executors: - macos: - macos: - xcode: 13.4.1 - environment: - HOMEBREW_NO_AUTO_UPDATE: 1 - CXXFLAGS: -DGEOS_INLINE - commands: - install_macos_dependencies: + linux_deps: + steps: + - run: ./scripts/install-linux-deps.sh + linux_lint_deps: steps: - - run: brew install protobuf cmake ccache libtool libspatialite pkg-config luajit curl wget czmq lz4 spatialite-tools unzip - - run: /usr/bin/python3 -m pip install --user requests - - run: pip3 install shapely "conan<2.0.0" - - run: git clone https://github.com/kevinkreiser/prime_server --recurse-submodules && cd prime_server && ./autogen.sh && ./configure && make -j8 && make install - install_linux_dependencies: + - run: | + apt-get update --assume-yes + env DEBIAN_FRONTEND=noninteractive apt install --yes --quiet sudo python3-pip python3-requests git curl file + check_ci_lint: steps: - - run: sudo bash ./scripts/install-linux-deps.sh + - run: ./scripts/format.sh && ./scripts/error_on_dirty.sh jobs: lint-build-debug: - machine: - image: ubuntu-2204:current + docker: + - image: ubuntu:23.04 resource_class: xlarge steps: - checkout - - run: | - pip3 install requests - if [[ $(./scripts/needs_ci_run) == "skip CI" ]]; then - echo "Changes in last commit do not need CI. Skipping step" - circleci-agent step halt - fi - - run: sudo python3 -m pip install pre-commit && pre-commit run --all-files && ./scripts/error_on_dirty.sh - - install_linux_dependencies + - linux_lint_deps + - check_ci_lint + - linux_deps - run: git submodule sync && git submodule update --init - restore_cache: keys: - - ccache-debug-linux-x86_64-v3-{{ .Branch }}-{{ checksum "conanfile.txt" }} - - ccache-debug-linux-x86_64-v3-{{ checksum "conanfile.txt" }} - - run: mkdir build + - ccache-debug-linux-x86_64-v3-{{ .Branch }} + - ccache-debug-linux-x86_64-v3-master + - ccache-debug-linux-x86_64-v3- - run: | + mkdir build # NOTE: -Werror disabled in CI, as we currently have >4k warnings. - cd build \ - && cmake .. -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=On -DCPACK_GENERATOR=DEB \ - -DENABLE_COMPILER_WARNINGS=On -DENABLE_WERROR=Off -DCMAKE_EXPORT_COMPILE_COMMANDS=On \ - -DCMAKE_CXX_FLAGS="-fuse-ld=lld" \ - -DLOGGING_LEVEL=INFO \ - -DENABLE_PYTHON_BINDINGS=On + cd build + cmake .. -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=On -DCPACK_GENERATOR=DEB \ + -DENABLE_COMPILER_WARNINGS=On -DENABLE_WERROR=Off -DCMAKE_EXPORT_COMPILE_COMMANDS=On \ + -DCMAKE_CXX_FLAGS="-fuse-ld=lld" -DLOGGING_LEVEL=INFO -DENABLE_PYTHON_BINDINGS=On -DENABLE_GDAL=On - run: python3 ./scripts/valhalla_build_config - run: make -C build -j8 - run: make -C build utrecht_tiles - run: make -C build -j8 tests - - run: make -C build -j8 benchmarks - - run: make -C build -j8 run-benchmarks # Note: we save the cache here before doing linting so that if linting fails, we can rebuild quickly # for follow-up fixes - save_cache: - key: ccache-debug-linux-x86_64-v3-{{ .Branch }}-{{ checksum "conanfile.txt" }}-{{ epoch }} + key: ccache-debug-linux-x86_64-v3-{{ .Branch }}-{{ epoch }} paths: - - ~/.ccache - - ~/.conan + - ~/.cache/ccache - run: scripts/clang-tidy-only-diff.sh 4 - run: sudo make -C build install - run: make -C build package @@ -70,75 +55,69 @@ jobs: - run: .circleci/vendored-codecov.sh || echo "Codecov did not collect coverage reports" build-release: - machine: - image: ubuntu-2204:current + docker: + - image: ubuntu:23.04 resource_class: xlarge steps: - checkout - - run: | - pip3 install requests - if [[ $(./scripts/needs_ci_run) == "skip CI" ]]; then - echo "Changes in last commit do not need CI. Skipping step" - circleci-agent step halt - fi - - run: sudo python3 -m pip install pre-commit && pre-commit run --all-files && ./scripts/error_on_dirty.sh - - install_linux_dependencies + - linux_lint_deps + - check_ci_lint + - linux_deps - run: git submodule sync && git submodule update --init - restore_cache: keys: - - ccache-release-linux-x86_64-v3-{{ .Branch }}-{{ checksum "conanfile.txt" }} - - ccache-release-linux-x86_64-v3-{{ checksum "conanfile.txt" }} - - run: mkdir build + - ccache-release-linux-x86_64-v3-{{ .Branch }} + - ccache-release-linux-x86_64-v3-master + - ccache-release-linux-x86_64-v3 - run: | - cd build && cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=On -DENABLE_PYTHON_BINDINGS=On \ - -DLOGGING_LEVEL=TRACE \ - -DCPACK_GENERATOR=DEB -DCPACK_PACKAGE_VERSION_SUFFIX="-0ubuntu1-$(lsb_release -sc)" -DENABLE_SANITIZERS=ON + mkdir build + cd build + cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=On -DENABLE_PYTHON_BINDINGS=On -DLOGGING_LEVEL=TRACE \ + -DCPACK_GENERATOR=DEB -DCPACK_PACKAGE_VERSION_SUFFIX="-0ubuntu1-$(lsb_release -sc)" -DENABLE_SANITIZERS=ON \ + -DENABLE_SINGLE_FILES_WERROR=Off -DENABLE_GDAL=On - run: make -C build -j8 - run: make -C build utrecht_tiles - run: make -C build -j8 tests # leaks in glibc we cant control for - run: export ASAN_OPTIONS=detect_leaks=0 && make -C build -j8 check - - run: make -C build -j8 benchmarks - - run: make -C build -j8 run-benchmarks - save_cache: - key: ccache-release-linux-x86_64-v3-{{ .Branch }}-{{ checksum "conanfile.txt" }}-{{ epoch }} + key: ccache-release-linux-x86_64-v3-{{ .Branch }}-{{ epoch }} paths: - - ~/.ccache - - ~/.conan + - ~/.cache/ccache - run: sudo make -C build install - run: make -C build package - build-osx: - executor: macos - resource_class: large + build-arm-release: + docker: + - image: arm64v8/ubuntu:23.04 + resource_class: arm.xlarge steps: - checkout - - run: | - pip3 install requests - if [[ $(./scripts/needs_ci_run) == "skip CI" ]]; then - echo "Changes in last commit do not need CI. Skipping step" - circleci-agent step halt - fi - - run: sudo python3 -m pip install pre-commit && pre-commit run --all-files && ./scripts/error_on_dirty.sh - - install_macos_dependencies + - linux_lint_deps + - check_ci_lint + - linux_deps - run: git submodule sync && git submodule update --init - restore_cache: keys: - - ccache-release-macos-{{ .Branch }}-{{ checksum "conanfile.txt" }} - - ccache-release-macos-{{ checksum "conanfile.txt" }} - - run: mkdir -p build - - run: cd build && cmake .. + - ccache-release-linux-arm_64-v3-{{ .Branch }} + - ccache-release-linux-arm_64-v3-master + - ccache-release-linux-arm_64-v3 + - run: | + mkdir build + cd build + cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=On -DENABLE_PYTHON_BINDINGS=On -DCPACK_GENERATOR=DEB \ + -DCPACK_PACKAGE_VERSION_SUFFIX="-0ubuntu1-$(lsb_release -sc)" -DENABLE_SINGLE_FILES_WERROR=Off -DENABLE_GDAL=On - run: make -C build -j8 - run: make -C build utrecht_tiles - run: make -C build -j8 tests - - run: make -C build -j8 check - - run: make -C build -j8 benchmarks - - run: make -C build run-benchmarks + # leaks in glibc we cant control for + - run: export ASAN_OPTIONS=detect_leaks=0 && make -C build -j8 check - save_cache: - key: ccache-release-macos-{{ .Branch }}-{{ checksum "conanfile.txt" }}-{{ epoch }} + key: ccache-release-linux-arm_64-v3-{{ .Branch }} paths: - - ~/.ccache - - ~/.conan + - ~/.cache/ccache + - run: sudo make -C build install + - run: make -C build package workflows: version: 2 @@ -152,7 +131,7 @@ workflows: filters: tags: ignore: /.*/ - - build-osx: + - build-arm-release: filters: tags: ignore: /.*/ diff --git a/.dockerignore b/.dockerignore index 11fc8893d6..b6ab5b052f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -59,6 +59,7 @@ CMakeLists.txt.user /CMakeSettings.json .idea /.tidytmp +vcpkg*/ # documentation site/ diff --git a/.github/workflows/check_tz_releases.yml b/.github/workflows/check_tz_releases.yml new file mode 100644 index 0000000000..f7535ec39b --- /dev/null +++ b/.github/workflows/check_tz_releases.yml @@ -0,0 +1,76 @@ +name: Check if tz git submodule is up to date + +on: + schedule: + - cron: "0 0 * * 0" + workflow_dispatch: + +jobs: + check_tz: + name: Check if latest tag commit differs from the commit the submodule currently points to + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: "recursive" + fetch-depth: 0 + + - name: Run script + shell: bash + run: | + set -e + + git config user.name 'github-actions[bot]' + git config user.email 'github-actions[bot]@users.noreply.github.com' + + latest_tag=$(git -C third_party/tz ls-remote --tags --refs --quiet origin | tail --lines 1 | awk '{print $2}' | cut --delimiter='/' --fields=3 ) + latest_tag_commit=$(git -C third_party/tz rev-list -n 1 $latest_tag) + current_commit=$(git -C ./third_party/tz rev-parse HEAD) + + if [[ $latest_tag_commit != $current_commit ]]; then + echo "New tz release available: ${latest_tag}" + new_branch="gha-bump-tz-${latest_tag}" + git checkout -b $new_branch + + # update the submodule + git -C third_party/tz checkout "${latest_tag}" + git add third_party/tz + + # update the url in valhalla_build_timezones + url_old=$(grep -oP "url=\"\Khttps://github.com/.*(?=\")" scripts/valhalla_build_timezones) + url_new=$(echo ${url_old} | sed -E "s/download\/[0-9]{4}[a-z]/download\/${latest_tag}/") + + # first make sure the file is actually available + + set +e + curl -Is --fail-with-body ${url_new} >/dev/null + + if [[ $? -eq 0 ]]; then + # only replace if it's available + url_old_escaped=$(echo "${url_old}" | sed 's/[\&/]/\\&/g') + url_new_escaped=$(echo "${url_new}" | sed 's/[\&/]/\\&/g') + sed -i -e "s/${url_old_escaped}/${url_new_escaped}/" scripts/valhalla_build_timezones + git add scripts/valhalla_build_timezones + + echo "Updated timezone shapefile download URL:" + git diff scripts/valhalla_build_timezones + fi + + set -e + # commit and push + git commit -m "bumped tz to ${latest_tag}" + git push origin "${new_branch}" + + # open new PR + body=$(echo "See [here](https://github.com/eggert/tz/blob/main/NEWS) for the latest changes made to tz.\ + Created by workflow run [#${WORKFLOW_RUN_ID}](https://github.com/valhalla/valhalla/actions/runs/${WORKFLOW_RUN_ID})." | xargs) + gh pr create --base master --head $new_branch --title "Bump tz to ${latest_tag}" --body "${body}" + + exit 1 + fi + + echo "tz up to date" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + WORKFLOW_RUN_ID: ${{ github.run_id }} + WORKFLOW_JOB_ID: ${{ github.job }} diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 5630740c66..1d308515b6 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -9,10 +9,10 @@ on: - '.gitignore' - '**.md' - 'test/' - jobs: build_and_publish: runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'valhalla' }} steps: - name: Check out the repo uses: actions/checkout@v3 @@ -29,13 +29,23 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build tagged image + - name: Build tag image if: startsWith(github.ref, 'refs/tags/') run: | - docker build -t ghcr.io/valhalla/valhalla:${{ steps.extract_tag.outputs.tag }} . - docker push ghcr.io/valhalla/valhalla:${{ steps.extract_tag.outputs.tag }} + sudo apt update --yes + sudo apt install --yes --quiet binfmt-support qemu-user-static qemu-system + docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + docker buildx create --name mybuilder + docker buildx use mybuilder + docker buildx inspect --bootstrap + docker buildx build --push --platform linux/amd64,linux/arm64 --tag ghcr.io/valhalla/valhalla:${{ steps.extract_tag.outputs.tag }} . - name: Build latest image if: github.ref == 'refs/heads/master' run: | - docker build -t ghcr.io/valhalla/valhalla:latest . - docker push ghcr.io/valhalla/valhalla:latest + sudo apt update --yes + sudo apt install --yes --quiet binfmt-support qemu-user-static qemu-system + docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + docker buildx create --name mybuilder + docker buildx use mybuilder + docker buildx inspect --bootstrap + docker buildx build --push --platform linux/amd64,linux/arm64 --tag ghcr.io/valhalla/valhalla:latest . diff --git a/.github/workflows/docker_build_arm_manual.yml b/.github/workflows/docker_build_arm_manual.yml new file mode 100644 index 0000000000..cca5e5e3ac --- /dev/null +++ b/.github/workflows/docker_build_arm_manual.yml @@ -0,0 +1,31 @@ +name: Build Manual Arm64 Docker Image + +on: + workflow_dispatch: + +jobs: + build_image: + name: Manual docker image build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: 'recursive' + fetch-depth: 0 + + - name: Log in to GitHub Docker Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build latest image + run: | + sudo apt update --yes + sudo apt install --yes --quiet binfmt-support qemu-user-static qemu-system + docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + docker buildx create --name mybuilder + docker buildx use mybuilder + docker buildx inspect --bootstrap + docker buildx build --push --platform linux/arm64 --tag ghcr.io/valhalla/valhalla:manually_triggered_build_arm64 . diff --git a/.github/workflows/docker_build_manual.yml b/.github/workflows/docker_build_manual.yml index ec30f398c7..edf75a7663 100644 --- a/.github/workflows/docker_build_manual.yml +++ b/.github/workflows/docker_build_manual.yml @@ -1,4 +1,4 @@ -name: Build manual Docker image +name: Build Manual Amd64 Docker Image on: workflow_dispatch: @@ -22,5 +22,4 @@ jobs: - name: Build latest image run: | - docker build -t ghcr.io/valhalla/valhalla:manually_triggered_build . - docker push ghcr.io/valhalla/valhalla:manually_triggered_build + docker buildx build --push --platform linux/amd64 --tag ghcr.io/valhalla/valhalla:manually_triggered_build_amd64 . diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 983796a169..50ca2b0b8a 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Python 3 uses: actions/setup-python@v4 diff --git a/.github/workflows/changelog.yml b/.github/workflows/lint.yml similarity index 82% rename from .github/workflows/changelog.yml rename to .github/workflows/lint.yml index 88b850be65..2fe16a7453 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/lint.yml @@ -1,13 +1,12 @@ -name: Valhalla Changelog Validation +name: lint on: [pull_request] jobs: - build: - name: Changelog Validation + changelog: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Validate + - name: Validate changelog shell: bash run: | # we dont enforce this on a draft @@ -32,3 +31,13 @@ jobs: fi #TODO: validate changelog line format + + spell-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Spell-check + uses: crate-ci/typos@v1.15.7 + with: + config: ./.github/workflows/typos.toml diff --git a/.github/workflows/mingw-build.yml b/.github/workflows/mingw-build.yml index 2f28bdbfb4..e3dfd581ea 100644 --- a/.github/workflows/mingw-build.yml +++ b/.github/workflows/mingw-build.yml @@ -18,6 +18,7 @@ jobs: mingw64-boost \ mingw64-protobuf \ mingw64-geos \ + mingw64-gdal \ mingw64-python3 \ protobuf-compiler - uses: actions/checkout@v2 @@ -39,7 +40,7 @@ jobs: -DENABLE_PYTHON_BINDINGS=ON \ -DENABLE_CCACHE=OFF \ -DENABLE_TESTS=OFF \ - -DENABLE_BENCHMARKS=OFF \ + -DENABLE_GDAL=ON \ -DLOGGING_LEVEL=DEBUG \ -DBoost_PROGRAM_OPTIONS_LIBRARY=/usr/x86_64-w64-mingw32/sys-root/mingw/lib/libboost_program_options-mt-x64.dll.a \ -DPYTHON_EXECUTABLE=/usr/x86_64-w64-mingw32/bin/python3 \ diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml new file mode 100644 index 0000000000..100c22dd4d --- /dev/null +++ b/.github/workflows/osx.yml @@ -0,0 +1,85 @@ +name: OSX CI +on: + push: + paths-ignore: + - '*.md' + - .circleci/ + - docs/ + - run_route_scripts/ + - test_requests/ + branches: + - master + pull_request: + paths-ignore: + - '*.md' + - .circleci/ + - docs/ + - run_route_scripts/ + - test_requests/ + branches: + - master + workflow_dispatch: + inputs: + debug_enabled: + type: boolean + description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' + required: false + default: false + +jobs: + build_osx: + runs-on: macos-14 + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install dependencies + run: | + HOMEBREW_NO_AUTO_UPDATE=1 brew install autoconf automake protobuf cmake ccache libtool sqlite3 libspatialite luajit curl wget czmq lz4 spatialite-tools unzip boost gdal + sudo python -m pip install --break-system-packages requests shapely + git clone https://github.com/kevinkreiser/prime_server --recurse-submodules && cd prime_server && ./autogen.sh && ./configure && make -j$(sysctl -n hw.logicalcpu) && sudo make install + + - name: Get branch name + run: echo "VALHALLA_BRANCH=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_ENV + + - name: Restore ccache + id: cache-ccache-restore + uses: actions/cache/restore@v4 + with: + path: ~/Library/Caches/ccache + key: ccache-osx-${{ env.VALHALLA_BRANCH }} + restore-keys: | + ccache-osx-master + ccache-osx- + + - name: Configure CMake + run: cmake -B build -DENABLE_SINGLE_FILES_WERROR=OFF -DENABLE_GDAL=ON + + - name: Build Valhalla + run: make -C build -j$(sysctl -n hw.logicalcpu) + + - name: Build Tests + run: make -C build -j$(sysctl -n hw.logicalcpu) tests + + - name: Save ccache + uses: actions/cache/save@v4 + with: + path: ~/Library/Caches/ccache + key: ${{ steps.cache-ccache-restore.outputs.cache-primary-key }} + + - name: Run Tests + run: make -C build -j$(sysctl -n hw.logicalcpu) check + + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + # only run this if manually invoked or a previous job failed + if: ${{ (github.event_name == 'workflow_dispatch' && inputs.debug_enabled) || failure() }} + with: + detached: true + timeout-minutes: 15 + diff --git a/.github/workflows/typos.toml b/.github/workflows/typos.toml new file mode 100644 index 0000000000..1c1f41b804 --- /dev/null +++ b/.github/workflows/typos.toml @@ -0,0 +1,58 @@ +# See https://github.com/crate-ci/typos/blob/master/docs/reference.md +# for an explanation of the available settings. + +[default] +extend-ignore-identifiers-re = [ + "OTSs", # mentioned in CHANGELOG ... ??? +] + +extend-ignore-re = [ + '"shape": ?"[^"]+"', + '"encoded_polyline": ?"[^"]+"', + '"id": ?"[^"]+"', + 'R"\([^)]+\)"', +] + +[default.extend-words] +segway = "segway" +subtiles = "subtiles" +nd = "nd" # node +Thur = "Thur" # short for Thursday, found in timeparsing.cc + +# places +Pont = "Pont" # Rue du Pont +Filles = "Filles" # Rue des Filles +Unter = "Unter" # Unter den Linden +Calle = "Calle" # street in Spanish + +# wrong but fixing it via substitution might make comments more confusing e.g. "index for the informations" +informations = "informations" + +[default.extend-identifiers] +O_WRONLY = "O_WRONLY" +countr_zero = "countr_zero" +skip_opps = "skip_opps" + +FO_DELETE = "FO_DELETE" +FOF_NO_UI = "FOF_NO_UI" + +[files] +extend-exclude = [ + "*.osm", + + "third_party/", + "date_time/", + "locales/", + + # test files + "test_requests/", + "url_de_benchmark_routes.txt", + + "test/gurka/", # contains a bunch of two-letter names to refer to edges + "src/baldr/admin.cc", # contains a bunch of two-letter country-codes + + # the following files are excluded because they contain some split up + # polyline strings that cannot be easily detected with `extend-ignore-re` + "test/skadi_service.cc", + "test/util_midgard.cc", +] diff --git a/.github/workflows/update_public_servers.yml b/.github/workflows/update_public_servers.yml new file mode 100644 index 0000000000..c17eb27c61 --- /dev/null +++ b/.github/workflows/update_public_servers.yml @@ -0,0 +1,32 @@ +name: Update public FOSSGIS servers + +on: + push: + branches: + - master + paths: + - .github/workflows + - 'src' + - 'valhalla' + - 'proto' + - 'scripts' + - 'lua' + workflow_dispatch: + +jobs: + update_servers: + name: Update servers + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Run script + run: | + eval $(ssh-agent -s) + ssh-add - <<< "${{ secrets.SSH_SECRET }}" + # first the build server + ssh -p 23432 -o StrictHostKeyChecking=no valhalla@162.55.103.19 'sudo bash -s builder' < scripts/update_public_server.sh + # then the services + ssh -p 23432 -o StrictHostKeyChecking=no valhalla@162.55.2.221 'sudo bash -s service' < scripts/update_public_server.sh diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml new file mode 100644 index 0000000000..8de02d329c --- /dev/null +++ b/.github/workflows/win.yml @@ -0,0 +1,140 @@ +name: Windows CI +on: + push: + paths-ignore: + - '*.md' + - .circleci/ + - docs/ + - run_route_scripts/ + - test/ + - test_requests/ + branches: + - master + pull_request: + paths-ignore: + - '*.md' + - .circleci/ + - docs/ + - run_route_scripts/ + - test/ + - test_requests/ + branches: + - master + workflow_dispatch: + inputs: + debug_enabled: + type: boolean + description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' + required: false + default: false + +env: + BUILD_TYPE: Release + MSVC_VERSION: '2022' + VCPKG_VERSION: '8040303' + VCPKG_INSTALL_OPTIONS: --x-abi-tools-use-exact-versions + VCPKG_DISABLE_COMPILER_TRACKING: ON + +jobs: + build_win: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + # we add a custom triplet to avoid cache misses as much as possible + # https://github.com/microsoft/vcpkg/issues/26346#issuecomment-1319244766 + - name: Configure vckpg + shell: bash + run: | + echo "VCPKG_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake" >> $GITHUB_ENV + echo "VCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/vcpkg/custom-triplets" >> $GITHUB_ENV + echo "VCPKG_DEFAULT_TRIPLET=custom-x64-windows" >> $GITHUB_ENV + echo "VCPKG_DEFAULT_BINARY_CACHE=${{ github.workspace }}/vcpkg/archives" >> $GITHUB_ENV + + - name: Install GNU make & awk + run: choco install gawk make + + - name: Install vcpkg + shell: bash + run: | + git clone https://github.com/microsoft/vcpkg.git + cd vcpkg + git checkout $VCPKG_VERSION + mkdir archives + mkdir "$VCPKG_OVERLAY_TRIPLETS" + TRIPLET_FILE="$VCPKG_OVERLAY_TRIPLETS/$VCPKG_DEFAULT_TRIPLET.cmake" + cp triplets/x64-windows.cmake "$TRIPLET_FILE" + echo "set(VCPKG_BUILD_TYPE release)" >> "$TRIPLET_FILE" + echo "set(VCPKG_DISABLE_COMPILER_TRACKING $VCPKG_DISABLE_COMPILER_TRACKING)" >> "$TRIPLET_FILE" + cmd.exe /c bootstrap-vcpkg.bat + + # make sure we save vcpkg packages even if build fails + # note, we don't use vcpkg "command line" mode, but "cmake manifest" mode + - name: Restore vcpkg packages + id: vcpkg-restore + uses: actions/cache/restore@v3 + with: + key: vcpkg=${{ env.VCPKG_VERSION }}-msvc=${{ env.MSVC_VERSION }}-json=${{ hashFiles('vcpkg.json') }} + path: | + vcpkg/* + !vcpkg/downloads + !vcpkg/docs + !vcpkg/buildtrees + vcpkg/downloads/tools + + - name: Setup Developer Command Prompt for VS + uses: ilammy/msvc-dev-cmd@v1 + + - name: Configure CMake + shell: bash + run: | + cmake --version + cmake -B build \ + -G "Visual Studio 17 2022" \ + -A x64 \ + -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ + -DCMAKE_TOOLCHAIN_FILE="$VCPKG_TOOLCHAIN_FILE" \ + -DVCPKG_OVERLAY_TRIPLETS="$VCPKG_OVERLAY_TRIPLETS" \ + -DVCPKG_TARGET_TRIPLET="$VCPKG_DEFAULT_TRIPLET" \ + -DVCPKG_INSTALL_OPTIONS="$VCPKG_INSTALL_OPTIONS" \ + -DENABLE_DATA_TOOLS=ON \ + -DENABLE_TOOLS=ON \ + -DENABLE_PYTHON_BINDINGS=ON \ + -DENABLE_HTTP=ON \ + -DENABLE_TESTS=OFF \ + -DENABLE_CCACHE=OFF \ + -DENABLE_SERVICES=OFF \ + -DPREFER_EXTERNAL_DEPS=ON \ + -DENABLE_GDAL=ON + + - name: Save vcpkg packages (always, to avoid redundant rebuilds) + uses: actions/cache/save@v3 + with: + key: ${{ steps.vcpkg-restore.outputs.cache-primary-key }} + path: | + vcpkg/* + !vcpkg/downloads + !vcpkg/docs + !vcpkg/buildtrees + vcpkg/downloads/tools + + - name: Build Valhalla + run: | + cmake --build build --config Release -- /clp:ErrorsOnly /p:BuildInParallel=true /m:4 + + - name: Test Executable + shell: bash + run: | + set PATH=$PATH:${{ github.workspace }}/build/vcpkg_installed/$BUILD_TYPE/bin + ./build/$BUILD_TYPE/valhalla_build_tiles.exe -c ./test/win/valhalla.json ./test/data/utrecht_netherlands.osm.pbf + ./build/$BUILD_TYPE/valhalla_run_isochrone.exe --config ./test/win/valhalla.json -j "{\"locations\": [{\"lat\": 52.10205, \"lon\": 5.114651}], \"costing\": \"auto\", \"contours\":[{\"time\":15,\"color\":\"ff0000\"}]}" + + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + # only run this if manually invoked or a previous job failed + if: ${{ (github.event_name == 'workflow_dispatch' && inputs.debug_enabled) || failure() }} + with: + detached: true + diff --git a/.gitignore b/.gitignore index efbe467dc0..50020889ae 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,8 @@ CMakeLists.txt.user /CMakeSettings.json .idea /.tidytmp +vcpkg*/ +*.log # python .venv @@ -69,5 +71,6 @@ CMakeLists.txt.user # documentation site/ +*.gph .DS_Store diff --git a/.gitmodules b/.gitmodules index da2ec3b035..862b33e230 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,9 +22,6 @@ [submodule "third_party/microtar"] path = third_party/microtar url = https://github.com/rxi/microtar.git -[submodule "third_party/benchmark"] - path = third_party/benchmark - url = https://github.com/google/benchmark [submodule "third_party/robin-hood-hashing"] path = third_party/robin-hood-hashing url = https://github.com/martinus/robin-hood-hashing.git @@ -37,12 +34,12 @@ [submodule "third_party/cpp-statsd-client"] path = third_party/cpp-statsd-client url = https://github.com/vthiery/cpp-statsd-client -[submodule "third_party/lz4"] - path = third_party/lz4 - url = https://github.com/lz4/lz4 [submodule "third_party/cxxopts"] path = third_party/cxxopts url = https://github.com/jarro2783/cxxopts.git [submodule "third_party/just_gtfs"] path = third_party/just_gtfs - url = https://github.com/mesozoic-drones/just_gtfs.git + url = https://github.com/valhalla/just_gtfs +[submodule "third_party/tz"] + path = third_party/tz + url = https://github.com/eggert/tz.git diff --git a/.vcpkg_deps.txt b/.vcpkg_deps.txt deleted file mode 100644 index 7178909c6d..0000000000 --- a/.vcpkg_deps.txt +++ /dev/null @@ -1,8 +0,0 @@ -curl -protobuf -zlib -sqlite3 -proj4 -luajit -libspatialite -geos \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dfa8b0e26..9ebd1fd45a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,153 @@ -## Release Date: 2022-??-?? Valhalla 3.4.1 +## UNRELEASED * **Removed** +* **Bug Fix** +* **Enhancement** + +## Release Date: 2024-08-21 Valhalla 3.5.0 +* **Removed** + * REMOVED: needs_ci_run script [#4423](https://github.com/valhalla/valhalla/pull/4423) + * REMOVED: unused vehicle types in AutoCost and segway; renamed kTruck to "truck" instead of "tractor_trailer" [#4430](https://github.com/valhalla/valhalla/pull/4430) + * REMOVED: ./bench and related files/code [#4560](https://github.com/valhalla/valhalla/pull/4560) + * REMOVED: unused headers [#4829](https://github.com/valhalla/valhalla/pull/4829) * **Bug Fix** * FIXED: gcc13 was missing some std header includes [#4154](https://github.com/valhalla/valhalla/pull/4154) + * FIXED: when reclassifying ferry edges, remove destonly from ways only if the connecting way was destonly [#4118](https://github.com/valhalla/valhalla/pull/4118) + * FIXED: typo in use value of map matching API (`platform_connection` was misspelled) [#4174](https://github.com/valhalla/valhalla/pull/4174) + * FIXED: fix crash in timedistancebssmatrix.cc [#4244](https://github.com/valhalla/valhalla/pull/4244) + * FIXED: missing protobuf CMake configuration to link abseil for protobuf >= 3.22.0 [#4207](https://github.com/valhalla/valhalla/pull/4207) + * FIXED: broken links on the optimized route API page [#4260](https://github.com/valhalla/valhalla/pull/4260) + * FIXED: remove clearing of headings while calculating a matrix [#4288](https://github.com/valhalla/valhalla/pull/4288) + * FIXED: only recost matrix pairs which have connections found [#4344](https://github.com/valhalla/valhalla/pull/4344) + * FIXED: arm builds. tons of errors due to floating point issues mostly [#4213](https://github.com/valhalla/valhalla/pull/4213) + * FIXED: respond with correlated edges for format=valhalla and matrix [#4335](https://github.com/valhalla/valhalla/pull/4335) + * FIXED: `sources` & `targets` for verbose matrix response was kinda broken due to #4335 above [#4366](https://github.com/valhalla/valhalla/pull/4366) + * FIXED: recover proper shortest path to ferry connections (when multiple edges exist between node pair) [#4361](https://github.com/valhalla/valhalla/pull/4361) + * FIXED: recover proper shortest path to ferry connections (make sure correct label index is used) [#4378](https://github.com/valhalla/valhalla/pull/4378) + * FIXED: Allow all roads for motorcycles [#4348](https://github.com/valhalla/valhalla/pull/4348) + * FIXED: motorcar:conditional should not apply to motorcycle and moped [#4359](https://github.com/valhalla/valhalla/pull/4359) + * FIXED: break shortcuts when there are different restrictions on base edges [#4326](https://github.com/valhalla/valhalla/pull/4326) + * FIXED: Incorrect `edge_index` assignment in `thor_worker_t::build_trace` [#4413](https://github.com/valhalla/valhalla/pull/4413) + * FIXED: lots of issues with CostMatrix (primarily deadend logic) with a complete refactor modeling things very close to bidir A\*, also to prepare for a unification of the two [#4372](https://github.com/valhalla/valhalla/pull/4372) + * FIXED: diff_names check was missing for Graphfilter and Shortcutbuilder for AddEdgeInfo call. [#4436](https://github.com/valhalla/valhalla/pull/4436) + * FIXED: updated timezone database and added code to keep compatibility with old servers/new data and vice versa [#4446](https://github.com/valhalla/valhalla/pull/4446) + * FIXED: retry elevation tile download if the download failed for some reason or the downloaded tile was corrupt [#4461](https://github.com/valhalla/valhalla/pull/4461) + * FIXED: base transition costs were getting overridden by osrm car turn duration [#4463](https://github.com/valhalla/valhalla/pull/4463) + * FIXED: insane ETAs for `motor_scooter` on `track`s [#4468](https://github.com/valhalla/valhalla/pull/4468) + * FIXED: -j wasn't taken into account anymore [#4483](https://github.com/valhalla/valhalla/pull/4483) + * FIXED: time distance matrix was always using time zone of last settled edge id [#4494](https://github.com/valhalla/valhalla/pull/4494) + * FIXED: log to stderr in valhalla_export_edges [#4498](https://github.com/valhalla/valhalla/pull/4498) + * FIXED: set capped speed for truck at 90 KPH [#4493](https://github.com/valhalla/valhalla/pull/4493) + * FIXED: Config singleton multiple instantiation issue [#4521](https://github.com/valhalla/valhalla/pull/4521) + * FIXED: Prevent GetShortcut to run into an infinite loop [#4532](https://github.com/valhalla/valhalla/pull/4532) + * FIXED: fix config generator with thor.costmatrix_allow_second_pass [#4567](https://github.com/valhalla/valhalla/pull/4567) + * FIXED: infinite loop or other random corruption in isochrones when retrieving partial shape of an edge [#4547](https://github.com/valhalla/valhalla/pull/4547) + * FIXED: Aggregation updates: update opposing local idx after aggregating the edges, added classification check for aggregation, and shortcut length changes [#4570](https://github.com/valhalla/valhalla/pull/4570) + * FIXED: Use helper function for only parsing out names from DirectedEdge when populating intersecting edges [#4604](https://github.com/valhalla/valhalla/pull/4604) + * FIXED: Osmnode size reduction: Fixed excessive disk space for planet build [#4605](https://github.com/valhalla/valhalla/pull/4605) + * FIXED: Conflict with signinfo's temporary linguistic node sequence file caused test failures. [#4625](https://github.com/valhalla/valhalla/pull/4625) + * FIXED: CostMatrix for trivial routes with oneways [#4626](https://github.com/valhalla/valhalla/pull/4626) + * FIXED: some entry points to creating geotiff isochrones output did not register the geotiff driver before attempting to use it [#4628](https://github.com/valhalla/valhalla/pull/4628) + * FIXED: libgdal wasn't installed in docker image, so it never worked in docker [#4629](https://github.com/valhalla/valhalla/pull/4629) + * FIXED: CostMatrix shapes for routes against trivial oneways [#4633](https://github.com/valhalla/valhalla/pull/4633) + * FIXED: unidirectional_astar.cc doesn't work for date_time type = 2 #4652(https://github.com/valhalla/valhalla/issues/4652) + * FIXED: a few fixes around the routing algorithms [#4626](https://github.com/valhalla/valhalla/pull/4642) + * FIXED: no need to search for GDAL when building data [#4651](https://github.com/valhalla/valhalla/pull/4651) + * FIXED: Fix segfault in OSRM serializer with bannerInstructions when destination is on roundabout [#4480](https://github.com/valhalla/valhalla/pull/4481) + * FIXED: Fix segfault in costmatrix (date_time and time zone always added). [#4530](https://github.com/valhalla/valhalla/pull/4530) + * FIXED: Fixed roundoff issue in Tiles Row and Col methods [#4585](https://github.com/valhalla/valhalla/pull/4585) + * FIXED: Fix for assigning attributes has_(highway, ferry, toll) if directions_type is none [#4465](https://github.com/valhalla/valhalla/issues/4465) + * FIXED: Have the `valhalla_add_predicted_speeds` summary always be created from `mjolnir.tile_dir` [#4722](https://github.com/valhalla/valhalla/pull/4722) + * FIXED: Fix inconsistency in graph.lua for motor_vehicle_node [#4723](https://github.com/valhalla/valhalla/issues/4723) + * FIXED: Missing algorithm include in `baldr/admin.h` [#4766](https://github.com/valhalla/valhalla/pull/4766) + * FIXED: remove old code that allows bicycle access on hiking trails. [#4781](https://github.com/valhalla/valhalla/pull/4781) + * FIXED: Handle list type arguments correctly when overriding config with valhalla_build_config [#4799](https://github.com/valhalla/valhalla/pull/4799) + * FIXED: `top_speed` range not fully allowed for trucks [#4793](https://github.com/valhalla/valhalla/pull/4793) + * FIXED: Trivial routes for CostMatrix [#4634](https://github.com/valhalla/valhalla/pull/4634) + * FIXED: Reset `not_thru_pruning` in CostMatrix after second pass was used [#4817](https://github.com/valhalla/valhalla/pull/4817) + * FIXED: wrong index used in CostMatrix expansion callback inside reverse connection check [#4821](https://github.com/valhalla/valhalla/pull/4821) + * FIXED: oneway ferry connections classification [#4828](https://github.com/valhalla/valhalla/pull/4828) + * FIXED: location search_filter ignored in certain cases [#4835](https://github.com/valhalla/valhalla/pull/4835) + * FIXED: Ferry reclassification finds shortest path that is blocked by inaccessible node [#4854](https://github.com/valhalla/valhalla/pull/4854) + * FIXED: `(Nov - Mar)` (and similar, months with spaces) condition parsing [#4857](https://github.com/valhalla/valhalla/pull/4857) * **Enhancement** + * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) + * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) + * CHANGED: moved the argparse boilerplate code to a private header which all programs can share [#4169](https://github.com/valhalla/valhalla/pull/4169) + * ADDED: CI runs a spell check on the PR to detect spelling mistakes [#4179](https://github.com/valhalla/valhalla/pull/4179) + * ADDED: `preferred_side_cutoff` parameter for locations [#4182](https://github.com/valhalla/valhalla/pull/4182) + * ADDED: PBF output for matrix endpoint [#4121](https://github.com/valhalla/valhalla/pull/4121) + * CHANGED: sped up the transit gtfs ingestion process by sorting the feeds before querying them and avoiding copying their structures. forked just_gtfs into the valhalla org to accomplish it [#4167](https://github.com/valhalla/valhalla/pull/4167) + * CHANGED: write traffic tile headers in `valhalla_build_extract` [#4195](https://github.com/valhalla/valhalla/pull/4195) + * ADDED: `source_percent_along` & `target_percent_along` to /trace_attributes JSON response [#4199](https://github.com/valhalla/valhalla/pull/4199) + * ADDED: sqlite database to store landmarks along with interfaces of insert and bounding box queries [#4189](https://github.com/valhalla/valhalla/pull/4189) + * CHANGED: refactor landmark database interface to use a pimpl [#4202](https://github.com/valhalla/valhalla/pull/4202) + * ADDED: support for `:forward` and `:backward` for `motor_vehicle`, `vehicle`, `foot` and `bicycle` tag prefixes [#4204](https://github.com/valhalla/valhalla/pull/4204) + * ADDED: add `valhalla_build_landmarks` to parse POIs from osm pbfs and store them as landmarks in the landmark sqlite database [#4201](https://github.com/valhalla/valhalla/pull/4201) + * ADDED: add primary key in the landmark sqlite database and a method to retrieve landmarks via their primary keys [#4224](https://github.com/valhalla/valhalla/pull/4224) + * ADDED: update graph tile to allow adding landmarks to edge info, and refactor edgeinfo.cc [#4233](https://github.com/valhalla/valhalla/pull/4233) + * ADDED: `sources_to_targets` action for `/expansion` [#4263](https://github.com/valhalla/valhalla/pull/4263) + * ADDED: option `--extract-tar` to `valhalla_build_extract` to create extracts from .tar files instead of tile directory [#4255](https://github.com/valhalla/valhalla/pull/4255) + * ADDED: Support for `bannerInstructions` attribute in OSRM serializer via `banner_instructions` request parameter [#4093](https://github.com/valhalla/valhalla/pull/4093) + * UPDATED: submodules which had new releases, unless it was a major version change [#4231](https://github.com/valhalla/valhalla/pull/4231) + * ADDED: Support for elevation along a route. Add elevation to EdgeInfo within Valhalla tiles [#4279](https://github.com/valhalla/valhalla/pull/4279) + * ADDED: the workflow to find landmarks in a graph tile, associate them with nearby edges, and update the graph tile to store the associations [#4278](https://github.com/valhalla/valhalla/pull/4278) + * ADDED: update maneuver generation to add nearby landmarks to maneuvers as direction support [#4293](https://github.com/valhalla/valhalla/pull/4293) + * CHANGED: the boost property tree config is now read into a singleton that doesn't need to be passed around anymore [#4220](https://github.com/valhalla/valhalla/pull/4220) + * ADDED: Update the street name and sign data processing include language and pronunciations [#4268](https://github.com/valhalla/valhalla/pull/4268) + * CHANGED: more sustainable way to work with protobuf in cmake [#4334](https://github.com/valhalla/valhalla/pull/4334) + * CHANGED: use date_time API to retrieve timezone aliases instead of our own curated list [#4382](https://github.com/valhalla/valhalla/pull/4382) + * CHANGED: less aggressive logging for nodes' headings & ferry connections [#4420][https://github.com/valhalla/valhalla/pull/4420] + * ADDED: add documentation about historical traffic [#4259](https://github.com/valhalla/valhalla/pull/4259) + * ADDED: config option to control how much memory we'll reserve for CostMatrix locations [#4424](https://github.com/valhalla/valhalla/pull/4424) + * CHANGED: refactor EdgeLabel (and derived classes) to reduce memory use. [#4439](https://github.com/valhalla/valhalla/pull/4439) + * ADDED: "shape" field to matrix response for CostMatrix only [#4432](https://github.com/valhalla/valhalla/pull/4432) + * CHANGED: `/expansion`: add field `prev_edge_id`, make the GeoJSON features `LineString`s [#4275](https://github.com/valhalla/valhalla/issues/4275) + * ADDED: --optimize & --log-details to valhalla_run_matrix [#4355](https://github.com/valhalla/valhalla/pull/4334) + * ADDED: most access restrictions to /locate response [#4431](https://github.com/valhalla/valhalla/pull/4431) + * ADDED: hgv=destination and friends for truck-specific "destination_only" logic [#4450](https://github.com/valhalla/valhalla/issues/4450) + * UPDATED: updated country access overrides [#4460](https://github.com/valhalla/valhalla/pull/4460) + * CHANGED: date_time refactor as a preparation to return DST/timezone related offset in the response [#4365](https://github.com/valhalla/valhalla/pull/4365) + * ADDED: find connection on backward search for bidir matrix algo [#4329](https://github.com/valhalla/valhalla/pull/4329) + * CHANGED: Adjustment of walk speed when walking on slight downhill [#4302](https://github.com/valhalla/valhalla/pull/4302) + * CHANGED: Do not reclassify ferry connections when no hierarchies are to be generated [#4487](https://github.com/valhalla/valhalla/pull/4487) + * ADDED: Added a config option to sort nodes spatially during graph building [#4455](https://github.com/valhalla/valhalla/pull/4455) + * ADDED: Timezone info in route and matrix responses [#4491](https://github.com/valhalla/valhalla/pull/4491) + * ADDED: Support for `voiceInstructions` attribute in OSRM serializer via `voice_instructions` request parameter [#4506](https://github.com/valhalla/valhalla/pull/4506) + * CHANGED: use pkg-config to find spatialite & geos and remove our cmake modules; upgraded conan's boost to 1.83.0 in the process [#4253](https://github.com/valhalla/valhalla/pull/4253) + * ADDED: Added aggregation logic to filter stage of tile building [#4512](https://github.com/valhalla/valhalla/pull/4512) + * UPDATED: tz to 2023d [#4519](https://github.com/valhalla/valhalla/pull/4519) + * CHANGED: libvalhalla.pc generation to have finer controls; install third_party public headers; overhaul lots of CMake; remove conan support [#4516](https://github.com/valhalla/valhalla/pull/4516) + * CHANGED: refactored matrix code to include a base class for all matrix algorithms to prepare for second passes on matrix [#4535](https://github.com/valhalla/valhalla/pull/4535) + * ADDED: matrix second pass for connections not found in the first pass, analogous to /route [#4536](https://github.com/valhalla/valhalla/pull/4536) + * UPDATED: cxxopts to 3.1.1 [#4541](https://github.com/valhalla/valhalla/pull/4541) + * CHANGED: make use of vendored libraries optional (other than libraries which are not commonly in package managers or only used for testing) [#4544](https://github.com/valhalla/valhalla/pull/4544) + * ADDED: Improved instructions for blind users [#3694](https://github.com/valhalla/valhalla/pull/3694) + * ADDED: isochrone proper polygon support & pbf output for isochrone [#4575](https://github.com/valhalla/valhalla/pull/4575) + * ADDED: return isotile grid as geotiff [#4594](https://github.com/valhalla/valhalla/pull/4594) + * ADDED: `ignore_non_vehicular_restrictions` parameter for truck costing [#4606](https://github.com/valhalla/valhalla/pull/4606) + * UPDATED: tz database to 2024a [#4643](https://github.com/valhalla/valhalla/pull/4643) + * ADDED: `hgv_no_penalty` costing option to allow penalized truck access to `hgv=no` edges [#4650](https://github.com/valhalla/valhalla/pull/4650) + * CHANGED: Significantly improve performance of graphbuilder [#4669](https://github.com/valhalla/valhalla/pull/4669) + * UPDATED: Improved turn by turn api reference documentation [#4675](https://github.com/valhalla/valhalla/pull/4675) + * CHANGED: contract nodes if connecting edges have different names or speed or non-conditional access restrictions [#4613](https://github.com/valhalla/valhalla/pull/4613) + * CHANGED: CostMatrix switched from Dijkstra to A* [#4650](https://github.com/valhalla/valhalla/pull/4650) + * ADDED: some missing documentation about request parameters [#4687](https://github.com/valhalla/valhalla/pull/4687) + * ADDED: Consider more forward/backward tags for access restrictions and speeds [#4686](https://github.com/valhalla/valhalla/pull/4686) + * CHANGED: change costmatrix max_distance threshold to a distance threshold instead of duration [#4672](https://github.com/valhalla/valhalla/pull/4672) + * ADDED: PBF support for expansion [#4614](https://github.com/valhalla/valhalla/pull/4614/) + * ADDED: elapsed_cost field to map matching json response [#4709](https://github.com/valhalla/valhalla/pull/4709) + * ADDED: error if we fail to find any matrix connection [#4718](https://github.com/valhalla/valhalla/pull/4718) + * ADDED: Fail early in valhalla_ingest_transit if there's no valid GTFS feeds [#4710](https://github.com/valhalla/valhalla/pull/4710/) + * ADDED: Support for `voiceLocale` attribute in OSRM serializer via `voice_instructions` request parameter [#4677](https://github.com/valhalla/valhalla/pull/4742) + * ADDED: Added ssmlAnnouncements for voice instructions and removed voice and banner instructions from last step. [#4644](https://github.com/valhalla/valhalla/pull/4644) + * ADDED: deadend information in directed edge JSON for `/locate` [#4751](https://github.com/valhalla/valhalla/pull/4751) + * ADDED: Dedupe option for expansion, significantly reducing the response size. [#4601](https://github.com/valhalla/valhalla/issues/4601) + * ADDED: `expansion_type` property to `/expansion` [#4784](https://github.com/valhalla/valhalla/pull/4784) + * ADDED: inline config arg for `valhalla_build_elevation` script [#4787](https://github.com/valhalla/valhalla/pull/4787) + * ADDED: `use_truck_route` [#4809](https://github.com/valhalla/valhalla/pull/4809) + * ADDED: Add option `edge.country_crossing` to trace attributes[4825](https://github.com/valhalla/valhalla/pull/4825) + * CHANGED: Unification of turn costs for ramps and roundabouts[4827](https://github.com/valhalla/valhalla/pull/4827) + * CHANGED: updated dockerfile to use ubuntu 24.04 [#4805](https://github.com/valhalla/valhalla/pull/4805) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** @@ -86,10 +231,10 @@ * FIXED: valhalla_ways_to_edges missed trimming the cache when overcommitted [#3872](https://github.com/valhalla/valhalla/pull/3864) * FIXED: Strange detours with multi-origin/destination unidirectional A* [#3585](https://github.com/valhalla/valhalla/pull/3585) * **Enhancement** - * ADDED: Added has_toll, has_higway, has_ferry tags to summary field of a leg and route and a highway tag to a maneuver if it includes a highway. [#3815](https://github.com/valhalla/valhalla/issues/3815) + * ADDED: Added has_toll, has_highway, has_ferry tags to summary field of a leg and route and a highway tag to a maneuver if it includes a highway. [#3815](https://github.com/valhalla/valhalla/issues/3815) * ADDED: Add time info to sources_to_targets [#3795](https://github.com/valhalla/valhalla/pull/3795) * ADDED: "available_actions" to the /status response [#3836](https://github.com/valhalla/valhalla/pull/3836) - * ADDED: "waiting" field on input/output intermediate break(_through) locations to respect services times [#3849](https://github.com/valhalla/valhalla/pull/3849) + * ADDED: "waiting" field on input/output intermediate break(\_through) locations to respect services times [#3849](https://github.com/valhalla/valhalla/pull/3849) * ADDED: --bbox & --geojson-dir options to valhalla_build_extract to only archive a subset of tiles [#3856](https://github.com/valhalla/valhalla/pull/3856) * CHANGED: Replace unstable c++ geos API with a mix of geos' c api and boost::geometry for admin building [#3683](https://github.com/valhalla/valhalla/pull/3683) * ADDED: optional write-access to traffic extract from GraphReader [#3876](https://github.com/valhalla/valhalla/pull/3876) @@ -163,6 +308,7 @@ * FIXED: Building Valhalla as a submodule [#3781](https://github.com/valhalla/valhalla/issues/3781) * FIXED: Fixed invalid time detection in GetSpeed [#3800](https://github.com/valhalla/valhalla/pull/3800) * FIXED: Osmway struct update: added up to 33 and not 32 [#3808](https://github.com/valhalla/valhalla/pull/3808) + * FIXED: Fix out-of-range linestrings in expansion [#4603](https://github.com/valhalla/valhalla/pull/4603) * **Enhancement** * CHANGED: Pronunciation for names and destinations [#3132](https://github.com/valhalla/valhalla/pull/3132) @@ -266,7 +412,7 @@ * ADDED: Added info to /status endpoint [#3008](https://github.com/valhalla/valhalla/pull/3008) * ADDED: Added stop and give_way/yield signs to the data and traffic signal fixes [#3251](https://github.com/valhalla/valhalla/pull/3251) * ADDED: use_hills for pedestrian costing, which also affects the walking speed [#3234](https://github.com/valhalla/valhalla/pull/3234) - * CHANGED: Fixed cost threshold fot bidirectional astar. Implemented reach-based pruning for suboptimal branches [#3257](https://github.com/valhalla/valhalla/pull/3257) + * CHANGED: Fixed cost threshold for bidirectional astar. Implemented reach-based pruning for suboptimal branches [#3257](https://github.com/valhalla/valhalla/pull/3257) * ADDED: Added `exclude_unpaved` request parameter [#3240](https://github.com/valhalla/valhalla/pull/3240) * ADDED: Added support for routing onto HOV/HOT lanes via request parameters `include_hot`, `include_hov2`, and `include_hov3` [#3273](https://github.com/valhalla/valhalla/pull/3273) * ADDED: Add Z-level field to `EdgeInfo`. [#3261](https://github.com/valhalla/valhalla/pull/3261) @@ -305,7 +451,7 @@ * FIXED: googletest wasn't really updated in #3166 [#3187](https://github.com/valhalla/valhalla/pull/3187) * FIXED: Minor fix of benchmark code [#3190](https://github.com/valhalla/valhalla/pull/3190) * FIXED: avoid_polygons intersected edges as polygons instead of linestrings [#3194]((https://github.com/valhalla/valhalla/pull/3194) - * FIXED: when binning horizontal edge shapes using single precision floats (converted from not double precision floats) allowed for the possiblity of marking many many tiles no where near the shape [#3204](https://github.com/valhalla/valhalla/pull/3204) + * FIXED: when binning horizontal edge shapes using single precision floats (converted from not double precision floats) allowed for the possibility of marking many many tiles no where near the shape [#3204](https://github.com/valhalla/valhalla/pull/3204) * FIXED: Fix improper iterator usage in ManeuversBuilder [#3205](https://github.com/valhalla/valhalla/pull/3205) * FIXED: Modified approach for retrieving signs from a directed edge #3166 [#3208](https://github.com/valhalla/valhalla/pull/3208) * FIXED: Improve turn channel classification: detect slip lanes [#3196](https://github.com/valhalla/valhalla/pull/3196) @@ -343,7 +489,7 @@ * FIXED: Isochrone (::Generalize()) fix to avoid generating self-intersecting polygons [#3026](https://github.com/valhalla/valhalla/pull/3026) * FIXED: Handle day_on/day_off/hour_on/hour_off restrictions [#3029](https://github.com/valhalla/valhalla/pull/3029) * FIXED: Apply conditional restrictions with dow only to the edges when routing [#3039](https://github.com/valhalla/valhalla/pull/3039) - * FIXED: Missing locking in incident handler needed to hang out to scop lock rather than let the temporary disolve [#3046](https://github.com/valhalla/valhalla/pull/3046) + * FIXED: Missing locking in incident handler needed to hang out to scop lock rather than let the temporary dissolve [#3046](https://github.com/valhalla/valhalla/pull/3046) * FIXED: Continuous lane guidance fix [#3054](https://github.com/valhalla/valhalla/pull/3054) * FIXED: Fix reclassification for "shorter" ferries and rail ferries (for Chunnel routing issues) [#3038](https://github.com/valhalla/valhalla/pull/3038) * FIXED: Incorrect routing through motor_vehicle:conditional=destination. [#3041](https://github.com/valhalla/valhalla/pull/3041) @@ -411,7 +557,7 @@ * FIXED: Add missing GEOS:GEOS dep to mjolnir target [#2901](https://github.com/valhalla/valhalla/pull/2901) * FIXED: Allow expansion into a region when not_thru_pruning is false on 2nd pass [#2978](https://github.com/valhalla/valhalla/pull/2978) * FIXED: Fix polygon area calculation: use Shoelace formula [#2927](https://github.com/valhalla/valhalla/pull/2927) - * FIXED: Isochrone: orient segments/rings acoording to the right-hand rule [#2932](https://github.com/valhalla/valhalla/pull/2932) + * FIXED: Isochrone: orient segments/rings according to the right-hand rule [#2932](https://github.com/valhalla/valhalla/pull/2932) * FIXED: Parsenodes fix: check if index is out-of-bound first [#2984](https://github.com/valhalla/valhalla/pull/2984) * FIXED: Fix for unique-summary logic [#2996](https://github.com/valhalla/valhalla/pull/2996) * FIXED: Isochrone: handle origin edges properly [#2990](https://github.com/valhalla/valhalla/pull/2990) @@ -433,7 +579,7 @@ * ADDED: avoid_polygons logic [#2750](https://github.com/valhalla/valhalla/pull/2750) * ADDED: Added support for destination for conditional access restrictions [#2857](https://github.com/valhalla/valhalla/pull/2857) * CHANGED: Large sequences are now merge sorted which can be dramatically faster with certain hardware configurations. This is especially useful in speeding up the earlier stages (parsing, graph construction) of tile building [#2850](https://github.com/valhalla/valhalla/pull/2850) - * CHANGED: When creating the intial graph edges by setting at which nodes they start and end, first mark the indices of those nodes in another sequence and then sort them by edgeid so that we can do the setting of start and end node sequentially in the edges file. This is much more efficient on certain hardware configurations [#2851](https://github.com/valhalla/valhalla/pull/2851) + * CHANGED: When creating the initial graph edges by setting at which nodes they start and end, first mark the indices of those nodes in another sequence and then sort them by edgeid so that we can do the setting of start and end node sequentially in the edges file. This is much more efficient on certain hardware configurations [#2851](https://github.com/valhalla/valhalla/pull/2851) * CHANGED: Use relative cost threshold to extend search in bidirectional astar in order to find more alternates [#2868](https://github.com/valhalla/valhalla/pull/2868) * CHANGED: Throw an exception if directory does not exist when building traffic extract [#2871](https://github.com/valhalla/valhalla/pull/2871) * CHANGED: Support for ignoring multiple consecutive closures at start/end locations [#2846](https://github.com/valhalla/valhalla/pull/2846) @@ -476,7 +622,7 @@ * **Bug Fix** * FIXED: Crazy ETAs. If a way has forward speed with no backward speed and it is not oneway, then we must set the default speed. The reverse logic applies as well. If a way has no backward speed but has a forward speed and it is not a oneway, then set the default speed. [#2102](https://github.com/valhalla/valhalla/pull/2102) * FIXED: Map matching elapsed times spliced amongst different legs and discontinuities are now correct [#2104](https://github.com/valhalla/valhalla/pull/2104) - * FIXED: Date time information is now propogated amongst different legs and discontinuities [#2107](https://github.com/valhalla/valhalla/pull/2107) + * FIXED: Date time information is now propagated amongst different legs and discontinuities [#2107](https://github.com/valhalla/valhalla/pull/2107) * FIXED: Adds support for geos-3.8 c++ api [#2021](https://github.com/valhalla/valhalla/issues/2021) * FIXED: Updated the osrm serializer to not set junction name for osrm origin/start maneuver - this is not helpful since we are not transitioning through the intersection. [#2121](https://github.com/valhalla/valhalla/pull/2121) * FIXED: Removes precomputing of edge-costs which lead to wrong results [#2120](https://github.com/valhalla/valhalla/pull/2120) @@ -538,7 +684,7 @@ * FIXED: Make tile building reproducible: fix UB-s [#2480](https://github.com/valhalla/valhalla/pull/2480) * FIXED: Zero initialize EdgeInfoInner.spare0_. Uninitialized spare0_ field produced UB which causes gurka_reproduce_tile_build to fail intermittently. [2499](https://github.com/valhalla/valhalla/pull/2499) * FIXED: Drop unused CHANGELOG validation script, straggling NodeJS references [#2506](https://github.com/valhalla/valhalla/pull/2506) - * FIXED: Fix missing nullptr checks in graphreader and loki::Reach (causing segfault during routing with not all levels of tiles availble) [#2504](https://github.com/valhalla/valhalla/pull/2504) + * FIXED: Fix missing nullptr checks in graphreader and loki::Reach (causing segfault during routing with not all levels of tiles available) [#2504](https://github.com/valhalla/valhalla/pull/2504) * FIXED: Fix mismatch of triplegedge roadclass and directededge roadclass [#2507](https://github.com/valhalla/valhalla/pull/2507) * FIXED: Improve german destination_verbal_alert phrases [#2509](https://github.com/valhalla/valhalla/pull/2509) * FIXED: Undefined behavior cases discovered with undefined behavior sanitizer tool. [2498](https://github.com/valhalla/valhalla/pull/2498) @@ -578,7 +724,7 @@ * FIXED: Fix bidirectional route failures at deadends [#2705](https://github.com/valhalla/valhalla/pull/2705) * FIXED: Updated logic to call out a non-obvious turn [#2708](https://github.com/valhalla/valhalla/pull/2708) * FIXED: valhalla_build_statistics multithreaded mode fixed [#2707](https://github.com/valhalla/valhalla/pull/2707) - * FIXED: If infer_internal_intersections is true then allow internals that are also ramps or TCs. Without this we produce an extra continue manuever. [#2710](https://github.com/valhalla/valhalla/pull/2710) + * FIXED: If infer_internal_intersections is true then allow internals that are also ramps or TCs. Without this we produce an extra continue maneuver. [#2710](https://github.com/valhalla/valhalla/pull/2710) * FIXED: We were routing down roads that should be destination only. Now we mark roads with motor_vehicle=destination and motor_vehicle=customers or access=destination and access=customers as destination only. [#2722](https://github.com/valhalla/valhalla/pull/2722) * FIXED: Replace all Python2 print statements with Python3 syntax [#2716](https://github.com/valhalla/valhalla/issues/2716) * FIXED: Some HGT files not found [#2723](https://github.com/valhalla/valhalla/issues/2723) @@ -600,10 +746,10 @@ * ADDED: Allows more complicated routes in timedependent a-star before timing out [#2068](https://github.com/valhalla/valhalla/pull/2068) * ADDED: Guide signs and junction names [#2096](https://github.com/valhalla/valhalla/pull/2096) * ADDED: Added a bool to the config indicating whether to use commercially set attributes. Added logic to not call IsIntersectionInternal if this is a commercial data set. [#2132](https://github.com/valhalla/valhalla/pull/2132) - * ADDED: Removed commerical data set bool to the config and added more knobs for data. Added infer_internal_intersections, infer_turn_channels, apply_country_overrides, and use_admin_db. [#2173](https://github.com/valhalla/valhalla/pull/2173) + * ADDED: Removed commercial data set bool to the config and added more knobs for data. Added infer_internal_intersections, infer_turn_channels, apply_country_overrides, and use_admin_db. [#2173](https://github.com/valhalla/valhalla/pull/2173) * ADDED: Allow using googletest in unit tests and convert all tests to it (old test.cc is completely removed). [#2128](https://github.com/valhalla/valhalla/pull/2128) * ADDED: Add guidance view capability. [#2209](https://github.com/valhalla/valhalla/pull/2209) - * ADDED: Collect turn cost information as path is formed so that it can be seralized out for trace attributes or osrm flavored intersections. Also add shape_index to osrm intersections. [#2207](https://github.com/valhalla/valhalla/pull/2207) + * ADDED: Collect turn cost information as path is formed so that it can be serialized out for trace attributes or osrm flavored intersections. Also add shape_index to osrm intersections. [#2207](https://github.com/valhalla/valhalla/pull/2207) * ADDED: Added alley factor to autocost. Factor is defaulted at 1.0f or do not avoid alleys. [#2246](https://github.com/valhalla/valhalla/pull/2246) * ADDED: Support unlimited speed limits where maxspeed=none. [#2251](https://github.com/valhalla/valhalla/pull/2251) * ADDED: Implement improved Reachability check using base class Dijkstra. [#2243](https://github.com/valhalla/valhalla/pull/2243) @@ -635,7 +781,7 @@ * ADDED: Unified time tracking for all algorithms that support time-based graph expansion. [#2278](https://github.com/valhalla/valhalla/pull/2278) * ADDED: Add rail_ferry use and costing. [#2408](https://github.com/valhalla/valhalla/pull/2408) * ADDED: `street_side_max_distance`, `display_lat` and `display_lon` to `locations` in input for better control of routing side of street [#1769](https://github.com/valhalla/valhalla/pull/1769) - * ADDED: Add addtional exit phrases. [#2421](https://github.com/valhalla/valhalla/pull/2421) + * ADDED: Add additional exit phrases. [#2421](https://github.com/valhalla/valhalla/pull/2421) * ADDED: Add Japanese locale, update German. [#2432](https://github.com/valhalla/valhalla/pull/2432) * ADDED: Gurka expect_route refactor [#2435](https://github.com/valhalla/valhalla/pull/2435) * ADDED: Add option to suppress roundabout exits [#2437](https://github.com/valhalla/valhalla/pull/2437) @@ -690,7 +836,7 @@ * ADDED: Added functions for predicted speeds encoding-decoding [#2674](https://github.com/valhalla/valhalla/pull/2674) * ADDED: Time invariant routing via the bidirectional algorithm. This has the effect that when time dependent routes (arrive_by and depart_at) fall back to bidirectional due to length restrictions they will actually use the correct time of day for one of the search directions [#2660](https://github.com/valhalla/valhalla/pull/2660) * ADDED: If the length of the edge is greater than kMaxEdgeLength, then consider this a catastrophic error if the should_error bool is true in the set_length function. [2678](https://github.com/valhalla/valhalla/pull/2678) - * ADDED: Moved lat,lon coordinates structures from single to double precision. Improves geometry accuracy noticibly at zooms above 17 as well as coordinate snapping and any other geometric operations. Addes about a 2% performance pentalty for standard routes. Graph nodes now have 7 digits of precision. [#2693](https://github.com/valhalla/valhalla/pull/2693) + * ADDED: Moved lat,lon coordinates structures from single to double precision. Improves geometry accuracy noticibly at zooms above 17 as well as coordinate snapping and any other geometric operations. Adds about a 2% performance penalty for standard routes. Graph nodes now have 7 digits of precision. [#2693](https://github.com/valhalla/valhalla/pull/2693) * ADDED: Added signboards to guidance views. [#2687](https://github.com/valhalla/valhalla/pull/2687) * ADDED: Regular speed on shortcut edges is calculated with turn durations taken into account. Truck, motorcycle and motorscooter profiles use OSRM-like turn duration. [#2662](https://github.com/valhalla/valhalla/pull/2662) * CHANGED: Remove astar algorithm and replace its use with timedep_forward as its redundant [#2706](https://github.com/valhalla/valhalla/pull/2706) @@ -801,7 +947,7 @@ * FIXED: Improved logic to decide between bear vs. continue [#1798](https://github.com/valhalla/valhalla/pull/1798) * FIXED: Bicycle costing allows use of roads with all surface values, but with a penalty based on bicycle type. However, the edge filter totally disallows bad surfaces for some bicycle types, creating situations where reroutes fail if a rider uses a road with a poor surface. [#1800](https://github.com/valhalla/valhalla/pull/1800) * FIXED: Moved complex restrictions building to before validate. [#1805](https://github.com/valhalla/valhalla/pull/1805) - * FIXED: Fix bicycle edge filter whan avoid_bad_surfaces = 1.0 [#1806](https://github.com/valhalla/valhalla/pull/1806) + * FIXED: Fix bicycle edge filter when avoid_bad_surfaces = 1.0 [#1806](https://github.com/valhalla/valhalla/pull/1806) * FIXED: Replace the EnhancedTripPath class inheritance with aggregation [#1807](https://github.com/valhalla/valhalla/pull/1807) * FIXED: Replace the old timezone shape zip file every time valhalla_build_timezones is ran [#1817](https://github.com/valhalla/valhalla/pull/1817) * FIXED: Don't use island snapped edge candidates (from disconnected components or low reach edges) when we rejected other high reachability edges that were closer [#1835](https://github.com/valhalla/valhalla/pull/1835) @@ -857,7 +1003,7 @@ ## Release Date: 2018-11-21 Valhalla 3.0.1 * **Bug Fix** - * FIXED: Fixed a rare, but serious bug with bicycle costing. ferry_factor_ in bicycle costing shadowed the data member in the base dynamic cost class, leading to an unitialized variable. Occasionally, this would lead to negative costs which caused failures. [#1663](https://github.com/valhalla/valhalla/pull/1663) + * FIXED: Fixed a rare, but serious bug with bicycle costing. ferry_factor_ in bicycle costing shadowed the data member in the base dynamic cost class, leading to an uninitialized variable. Occasionally, this would lead to negative costs which caused failures. [#1663](https://github.com/valhalla/valhalla/pull/1663) * FIXED: Fixed use of units in OSRM compatibility mode. [#1662](https://github.com/valhalla/valhalla/pull/1662) ## Release Date: 2018-11-21 Valhalla 3.0.0 @@ -899,7 +1045,7 @@ * **Bug Fix** * FIXED: Fixed a bug with shortcuts that leads to inconsistent routes depending on whether shortcuts are taken, different origins can lead to different paths near the destination. This fix also improves performance on long routes and matrices. * FIXED: We were getting inconsistent results between departing at current date/time vs entering the current date/time. This issue is due to the fact that the iso_date_time function returns the full iso date_time with the timezone offset (e.g., 2018-09-27T10:23-07:00 vs 2018-09-27T10:23). When we refactored the date_time code to use the new Howard Hinnant date library, we introduced this bug. - * FIXED: Increased the threshold in CostMatrix to address null time and distance values occuring for truck costing with locations near the max distance. + * FIXED: Increased the threshold in CostMatrix to address null time and distance values occurring for truck costing with locations near the max distance. ## Release Date: 2018-09-13 Valhalla 2.7.0 * **Enhancement** @@ -1034,7 +1180,7 @@ polyline point. ## Release Date: 2018-01-23 Valhalla 2.4.4 * **Enhancement** - * Elevation service speed improvments and the ability to serve lz4hc compressed data + * Elevation service speed improvements and the ability to serve lz4hc compressed data * Basic support for downloading routing tiles on demand * Deprecated `valhalla_route_service`, now all services (including elevation) are found under `valhalla_service` @@ -1114,7 +1260,7 @@ polyline point. ## Release Date: 2017-08-14 Valhalla 2.3.3 * **Bug Fix** - * Maximum osm node reached now causes bitset to resize to accomodate when building tiles + * Maximum osm node reached now causes bitset to resize to accommodate when building tiles * Fix wrong side of street information and remove redundant node snapping * Fix path differences between services and `valhalla_run_route` * Fix map matching crash when interpolating duplicate input points @@ -1275,7 +1421,7 @@ polyline point. * Fixed through location handling, now includes cost at throughs and properly uses heading * Added ability to adjust location heading tolerance * **Traffic Updates** - * Fixed segment matching json to properly return non-string values where apropriate + * Fixed segment matching json to properly return non-string values where appropriate * **Data Producer Updates** * Process node:ref and way:junction_ref as a semicolon separated list for exit numbers * Removed duplicated interchange sign information when ways are split into edges @@ -1325,7 +1471,7 @@ polyline point. * **Routing Improvement** * Preliminary support for multi-way restrictions * **Issues Fixed** - * Fixed tile incompatiblity between 64 and 32bit architectures + * Fixed tile incompatibility between 64 and 32bit architectures * Fixed missing edges within tile edge search indexes * Fixed an issue where transit isochrone was cut off if we took transit that was greater than the max_seconds and other transit lines or buses were then not considered. @@ -1333,7 +1479,7 @@ polyline point. * **Tile Redesign** * Updated the graph tiles to store edges only on the hierarchy level they belong to. Prior to this, the highways were stored on all levels, they now exist only on the highway hierarchy. Similar changes were made for arterial level roads. This leads to about a 20% reduction in tile size. - * The tile redesign required changes to the path generation algorithms. They must now transition freely beteeen levels, even for pedestrian and bicycle routes. To offset the extra transitions, the main algorithms were changed to expand nodes at each level that has directed edges, rather than adding the transition edges to the priority queue/adjacency list. This change helps performance. The hierarchy limits that are used to speed the computation of driving routes by utilizing the highway hierarchy were adjusted to work with the new path algorithms. + * The tile redesign required changes to the path generation algorithms. They must now transition freely between levels, even for pedestrian and bicycle routes. To offset the extra transitions, the main algorithms were changed to expand nodes at each level that has directed edges, rather than adding the transition edges to the priority queue/adjacency list. This change helps performance. The hierarchy limits that are used to speed the computation of driving routes by utilizing the highway hierarchy were adjusted to work with the new path algorithms. * Some changes to costing were also required, for example pedestrian and bicycle routes skip shortcut edges. * Many tile data structures were altered to explicitly size different fields and make room for "spare" fields that will allow future growth. In addition, the tile itself has extra "spare" records that can be appended to the end of the tile and referenced from the tile header. This also will allow future growth without breaking backward compatibility. * **Guidance Improvement** @@ -1466,7 +1612,7 @@ of non-separated bicycle lanes on high speed roads. ## Release Date: 2016-03-28 - * **Improved Graph Correlation** - Correlating input to the routing graph is carried out via closest first traversal of the graph's, now indexed, geometry. This results in faster correlation and gaurantees the absolute closest edge is found. + * **Improved Graph Correlation** - Correlating input to the routing graph is carried out via closest first traversal of the graph's, now indexed, geometry. This results in faster correlation and guarantees the absolute closest edge is found. ## Release Date: 2016-03-16 diff --git a/CMakeLists.txt b/CMakeLists.txt index fa47a39dc4..3af4452a73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ # This is secondary build configuration provided for convenient development # on Windows and using CMake-enabled IDEs. # -cmake_minimum_required(VERSION 3.12 FATAL_ERROR) +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) project(valhalla LANGUAGES CXX C) include(FindPkgConfig) @@ -31,18 +31,17 @@ option(ENABLE_ADDRESS_SANITIZER "Use memory sanitizer for Debug build" OFF) option(ENABLE_UNDEFINED_SANITIZER "Use UB sanitizer for Debug build" OFF) option(ENABLE_TESTS "Enable Valhalla tests" ON) option(ENABLE_WERROR "Convert compiler warnings to errors. Requires ENABLE_COMPILER_WARNINGS=ON to take effect" OFF) -option(ENABLE_BENCHMARKS "Enable microbenchmarking" ON) option(ENABLE_THREAD_SAFE_TILE_REF_COUNT "If ON uses shared_ptr as tile reference(i.e. it is thread safe)" OFF) option(ENABLE_SINGLE_FILES_WERROR "Convert compiler warnings to errors for single files" ON) +option(PREFER_EXTERNAL_DEPS "Whether to use internally vendored headers or find the equivalent external package" OFF) # useful to workaround issues likes this https://stackoverflow.com/questions/24078873/cmake-generated-xcode-project-wont-compile option(ENABLE_STATIC_LIBRARY_MODULES "If ON builds Valhalla modules as STATIC library targets" OFF) +option(ENABLE_GDAL "Whether to include GDAL; currently only used for raster serialization of isotile grid" ON) set(LOGGING_LEVEL "" CACHE STRING "Logging level, default is INFO") set_property(CACHE LOGGING_LEVEL PROPERTY STRINGS "NONE;ALL;ERROR;WARN;INFO;DEBUG;TRACE") set_property(GLOBAL PROPERTY USE_FOLDERS ON) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(THREADS_PREFER_PTHREAD_FLAG ON) @@ -52,13 +51,10 @@ include(CheckCXXCompilerFlag) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics") elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") - set(COLOR_FLAG "-fdiagnostics-color=auto") check_cxx_compiler_flag("-fdiagnostics-color=auto" HAS_COLOR_FLAG) - if(NOT HAS_COLOR_FLAG) - set(COLOR_FLAG "") + if(HAS_COLOR_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COLOR_FLAG}") endif() - # using GCC - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COLOR_FLAG}") endif() # Explicitly set the build type to Release if no other type is specified @@ -85,18 +81,6 @@ else() message(FATAL_ERROR "Unrecognized build type. Use one of Debug, Release, RelWithDebInfo, MinRelSize, None") endif() -function(create_source_groups prefix) - foreach(file ${ARGN}) - get_filename_component(file "${file}" ABSOLUTE) - string(FIND "${file}" "${PROJECT_BINARY_DIR}/" pos) - if(pos EQUAL 0) - source_group(TREE "${PROJECT_BINARY_DIR}/" PREFIX "Generated Files" FILES "${file}") - else() - source_group(TREE "${PROJECT_SOURCE_DIR}/" PREFIX "${prefix}" FILES "${file}") - endif() - endforeach() -endfunction() - if(ENABLE_CCACHE AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")) find_program(CCACHE_FOUND ccache) if(CCACHE_FOUND) @@ -107,94 +91,98 @@ if(ENABLE_CCACHE AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILE endif() endif() -include(cmake/SanitizerOptions.cmake) - -## Coverage report targets -if(ENABLE_COVERAGE) - find_program(GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat) - if(NOT GENHTML_PATH) - message(FATAL_ERROR "no genhtml installed") +include(ValhallaSanitizerOptions) +include(ValhallaSourceGroups) + +# We use pkg-config for (almost) all dependencies: +# - CMake Find* modules are versioned with CMake, not the packages, see protobuf > 21.12 +# - it's more trivial to use custom installations via PKG_CONFIG_PATH env var +pkg_check_modules(ZLIB REQUIRED IMPORTED_TARGET zlib) +pkg_check_modules(LZ4 REQUIRED IMPORTED_TARGET liblz4) + +# cURL +set(curl_targets "") +if (ENABLE_HTTP OR ENABLE_DATA_TOOLS) + pkg_check_modules(CURL REQUIRED IMPORTED_TARGET libcurl) + if (CURL_FOUND) + set(curl_targets PkgConfig::CURL) + target_compile_definitions(PkgConfig::CURL INTERFACE ENABLE_HTTP) + else() + message(FATAL_ERROR "ENABLE_HTTP=ON, but cURL not found...") endif() - - set(FASTCOV_PATH ${VALHALLA_SOURCE_DIR}/third_party/fastcov/fastcov.py) - add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/coverage.info - COMMAND ${FASTCOV_PATH} -d . --exclude /usr/ third_party/ ${CMAKE_CURRENT_BINARY_DIR}/ --lcov -o coverage.info - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS check) - - add_custom_target(coverage - COMMAND ${GENHTML_PATH} --prefix ${CMAKE_CURRENT_BINARY_DIR} --output-directory coverage --title "Test Coverage" --legend --show-details coverage.info - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/coverage.info) - set_target_properties(coverage PROPERTIES FOLDER "Tests") endif() -## Dependencies -find_package(Threads REQUIRED) -find_package(ZLIB REQUIRED) - -# try to find an installed boost or install locally with conan -set(boost_VERSION "1.71") -find_package(Boost ${boost_VERSION} QUIET) -if (NOT Boost_FOUND) - # bail if there's no conan installed - message(STATUS "No compatible boost version detected, using conan...") - find_program(conan_FOUND conan) - if (NOT conan_FOUND) - message(FATAL_ERROR "conan needs to be installed for boost, see https://docs.conan.io/en/latest/installation.html.") - endif() - - # build dir needs to be added, that's where conan writes some info - list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_BINARY_DIR}) - list(APPEND CMAKE_PREFIX_PATH ${CMAKE_CURRENT_BINARY_DIR}) - include(${VALHALLA_SOURCE_DIR}/cmake/conan.cmake) +# prefer CONFIG mode over MODULE mode, which versions configuration on the package, not CMake +# NOTE: this is only supported for cmake >= 3.15, but shouldn't be a problem in real life +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - conan_cmake_autodetect(settings) - conan_cmake_install(PATH_OR_REFERENCE ${VALHALLA_SOURCE_DIR}/conanfile.txt - REMOTE conancenter - SETTINGS ${settings} - OUTPUT_QUIET) -endif() -find_package(Boost ${boost_VERSION} REQUIRED) +# Boost has no .pc file.. +find_package(Boost 1.71 REQUIRED) add_definitions(-DBOOST_NO_CXX11_SCOPED_ENUMS) add_definitions(-DBOOST_ALLOW_DEPRECATED_HEADERS) add_definitions(-DBOOST_BIND_GLOBAL_PLACEHOLDERS) -if(NOT TARGET CURL::CURL) - add_library(CURL::CURL INTERFACE IMPORTED) - if(ENABLE_HTTP OR ENABLE_DATA_TOOLS) - if(NOT CURL_FOUND) - find_package(CURL REQUIRED) - endif() - set_target_properties(CURL::CURL PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${CURL_INCLUDE_DIR}" - INTERFACE_COMPILE_DEFINITIONS CURL_STATICLIB) - if(NOT MSVC) - set_property(TARGET CURL::CURL APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${CURL_LIBRARIES}") +find_package(PkgConfig REQUIRED) +find_package(Threads REQUIRED) + +# resolve vendored libraries +set(date_include_dir ${VALHALLA_SOURCE_DIR}/third_party/date/include) +set(rapidjson_include_dir ${CMAKE_SOURCE_DIR}/third_party/rapidjson/include) +set(robinhoodhashing_include_dir ${CMAKE_SOURCE_DIR}/third_party/robin-hood-hashing/src/include) +set(cxxopts_include_dir ${VALHALLA_SOURCE_DIR}/third_party/cxxopts/include) +set(dirent_include_dir ${CMAKE_SOURCE_DIR}/third_party/dirent/include) +if (PREFER_EXTERNAL_DEPS) + # date + find_package(date QUIET) + if (date_FOUND) + get_target_property(date_include_dir date::date INTERFACE_INCLUDE_DIRECTORIES) + else() + message(WARNING "No date found in system libraries, using vendored date...") + endif() + # rapidjson + find_package(RapidJSON QUIET) + if (RapidJSON_FOUND) + get_target_property(rapidjson_include_dir rapidjson INTERFACE_INCLUDE_DIRECTORIES) + else() + message(WARNING "No RapidJSON found in system libraries, using vendored RapidJSON...") + endif() + # robin-hood-hashing + find_package(robin_hood QUIET) + if (robin_hood_FOUND) + get_target_property(robinhoodhashing_include_dir robin_hood::robin_hood INTERFACE_INCLUDE_DIRECTORIES) + else() + message(WARNING "No robin_hood found in system libraries, using vendored robin_hood...") + endif() + # cxxopts + if (ENABLE_DATA_TOOLS OR ENABLE_TOOLS) + find_package(cxxopts QUIET) + if (cxxopts_FOUND) + get_target_property(cxxopts_include_dir cxxopts::cxxopts INTERFACE_INCLUDE_DIRECTORIES) else() - link_libraries(${CURL_LIBRARIES}) + message(WARNING "No cxxopts found in system libraries, using vendored cxxopts...") + endif() + endif() + # dirent + if (WIN32) + find_path(dirent_include_dir dirent.h REQUIRED) + if (dirent_include_dir-NOTFOUND) + message(WARNING "No dirent.h found in system headers, using vendored dirent.h...") endif() endif() -else() - message(STATUS "Using curl from the outside") -endif() - -if(NOT Protobuf_FOUND) - find_package(Protobuf REQUIRED) endif() -message(STATUS "Using pbf headers from ${PROTOBUF_INCLUDE_DIR}") -message(STATUS "Using pbf libs from ${PROTOBUF_LIBRARY}") -message(STATUS "Using pbf release libs from ${PROTOBUF_LIBRARY_RELEASE}") -message(STATUS "Using pbf debug libs from ${PROTOBUF_LIBRARY_DEBUG}") +# Protobuf is non-trivial to include via pkg-config, pkg_check_modules has no way to check +# for protoc location in a platform agnostic manner +# newer protobuf versions require a compat bool +set(protobuf_MODULE_COMPATIBLE ON CACHE BOOL "") +find_package(Protobuf REQUIRED) +# and turn it off again +message(STATUS "Using protoc from ${Protobuf_PROTOC_EXECUTABLE}") +message(STATUS "Using pbf headers from ${Protobuf_INCLUDE_DIRS}") +message(STATUS "Using pbf libs from ${PROTOBUF_LIBRARIES}") if(TARGET protobuf::libprotobuf-lite) message(STATUS "Using pbf-lite") endif() - -# Allow linking against full or lite version of Protobuf library -# TODO: After switching to CMake 3.12+, replace with -# $=0.6.3) - # workaround for https://gitlab.kitware.com/cmake/cmake/issues/15804 - find_library(libprime_server_LIBRARY - NAME ${libprime_server_LIBRARIES} - HINTS ${libprime_server_LIBRARY_DIRS}) - set_target_properties(libprime_server PROPERTIES - INTERFACE_LINK_LIBRARIES "${libprime_server_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${libprime_server_INCLUDE_DIRS}" - INTERFACE_COMPILE_DEFINITIONS HAVE_HTTP) + pkg_check_modules(libprime_server IMPORTED_TARGET libprime_server>=0.6.3) + if (libprime_server_FOUND) + target_compile_definitions(PkgConfig::libprime_server INTERFACE ENABLE_SERVICES) + set(libprime_server_targets PkgConfig::libprime_server) + else() + set(ENABLE_SERVICES OFF) + message(FATAL_ERROR "ENABLE_SERVICES=ON but prime_server not found...") + endif() endif() + ## Mjolnir and associated executables if(ENABLE_DATA_TOOLS) add_compile_definitions(DATA_TOOLS) + # keep sqlite3 with cmake find_package(), as OSX needs our own FindSqlite3.cmake + # otherwise the system sqlite3 is found which doesn't allow for spatialite find_package(SQLite3 REQUIRED) - find_package(SpatiaLite REQUIRED) - find_package(LuaJIT) - add_library(Lua::Lua INTERFACE IMPORTED) - set_target_properties(Lua::Lua PROPERTIES - INTERFACE_LINK_LIBRARIES "${LUA_LIBRARIES}" - INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}") + pkg_check_modules(SpatiaLite REQUIRED IMPORTED_TARGET spatialite) + pkg_check_modules(LuaJIT REQUIRED IMPORTED_TARGET luajit) endif() if (ENABLE_THREAD_SAFE_TILE_REF_COUNT) @@ -237,7 +241,7 @@ add_subdirectory(src) ## Python bindings if(ENABLE_PYTHON_BINDINGS) - # favors Py3, falls back to Py2 + # python is fine for cmake as well find_package(Python COMPONENTS Development Interpreter) if (Python_FOUND) add_subdirectory(src/bindings/python) @@ -246,22 +250,11 @@ if(ENABLE_PYTHON_BINDINGS) COMPONENT python) else() set(ENABLE_PYTHON_BINDINGS OFF) - message(WARNING "Skipping Python bindings... Install a development Python version including headers to compile the bindings.") + message(WARNING "Python development version not found, skipping Python bindings...") endif() endif() ## Executable targets -function(get_source_path PATH NAME) - if(EXISTS ${VALHALLA_SOURCE_DIR}/src/${NAME}.cc) - set(${PATH} ${VALHALLA_SOURCE_DIR}/src/${NAME}.cc PARENT_SCOPE) - elseif(EXISTS ${VALHALLA_SOURCE_DIR}/src/meili/${NAME}.cc) - set(${PATH} ${VALHALLA_SOURCE_DIR}/src/meili/${NAME}.cc PARENT_SCOPE) - elseif(EXISTS ${VALHALLA_SOURCE_DIR}/src/mjolnir/${NAME}.cc) - set(${PATH} ${VALHALLA_SOURCE_DIR}/src/mjolnir/${NAME}.cc PARENT_SCOPE) - else() - message(FATAL_ERROR "no source path for ${NAME}") - endif() -endfunction() ## Valhalla programs set(valhalla_programs valhalla_run_map_match valhalla_benchmark_loki valhalla_benchmark_skadi @@ -272,7 +265,7 @@ set(valhalla_programs valhalla_run_map_match valhalla_benchmark_loki valhalla_be set(valhalla_data_tools valhalla_build_statistics valhalla_ways_to_edges valhalla_validate_transit valhalla_benchmark_admins valhalla_build_connectivity valhalla_build_tiles valhalla_build_admins valhalla_convert_transit valhalla_ingest_transit valhalla_query_transit valhalla_add_predicted_traffic - valhalla_assign_speeds valhalla_add_elevation) + valhalla_assign_speeds valhalla_add_elevation valhalla_build_landmarks valhalla_add_landmarks) ## Valhalla services set(valhalla_services valhalla_loki_worker valhalla_odin_worker valhalla_thor_worker) @@ -284,10 +277,7 @@ if(ENABLE_TOOLS) set_target_properties(${program} PROPERTIES FOLDER "Tools") create_source_groups("Source Files" ${path}) target_link_libraries(${program} valhalla) - target_include_directories(${program} PUBLIC ${VALHALLA_SOURCE_DIR}/third_party/cxxopts/include) - if(MSVC) - target_link_libraries(${program} ${CURL_LIBRARIES}) - endif() + target_include_directories(${program} PRIVATE ${cxxopts_include_dir}) install(TARGETS ${program} DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT runtime) endforeach() endif() @@ -298,21 +288,18 @@ if(ENABLE_DATA_TOOLS) add_executable(${program} ${path}) create_source_groups("Source Files" ${path}) set_target_properties(${program} PROPERTIES FOLDER "Data Tools") - target_include_directories(${program} PUBLIC ${VALHALLA_SOURCE_DIR}/third_party/cxxopts/include) + target_include_directories(${program} PRIVATE ${cxxopts_include_dir}) target_link_libraries(${program} valhalla) - if (LUAJIT_FOUND AND APPLE AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") + if (LuaJIT_FOUND AND APPLE AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") # Using LuaJIT on macOS on Intel processors requires a couple of extra linker flags target_link_options(${program} PUBLIC -pagezero_size 10000 -image_base 100000000) endif() - if(MSVC) - target_link_libraries(${program} ${CURL_LIBRARIES}) - endif() install(TARGETS ${program} DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT runtime) endforeach() # Target-specific dependencies - find_package(GEOS) - target_link_libraries(valhalla_build_admins GEOS::GEOS) + pkg_check_modules(GEOS REQUIRED IMPORTED_TARGET geos) + target_link_libraries(valhalla_build_admins PkgConfig::GEOS) target_sources(valhalla_build_statistics PUBLIC ${VALHALLA_SOURCE_DIR}/src/mjolnir/statistics.cc @@ -325,7 +312,6 @@ if(ENABLE_SERVICES) create_source_groups("Source Files" src/${program}.cc) set_target_properties(${program} PROPERTIES FOLDER "Services") target_link_libraries(${program} valhalla) - target_include_directories(${program} PUBLIC ${VALHALLA_SOURCE_DIR}/third_party/cxxopts/include) install(TARGETS ${program} DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT runtime) endforeach() endif() @@ -351,13 +337,38 @@ install(FILES COPYING CHANGELOG.md DESTINATION "${CMAKE_INSTALL_DOCDIR}" COMPONENT runtime) +# install third_party +install(DIRECTORY ${rapidjson_include_dir}/ + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/valhalla/third_party") +install(DIRECTORY ${date_include_dir}/ + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/valhalla/third_party") +if (WIN32) + install(DIRECTORY ${dirent_include_dir}/ + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/valhalla/third_party") +endif() + if(ENABLE_TESTS) add_subdirectory(test) endif() -# NOTE(mookerji): Windows CI seems to break on the gbench build, so shelve Win32 support for now. -if(ENABLE_BENCHMARKS AND ENABLE_DATA_TOOLS AND NOT WIN32) - add_subdirectory(bench) +## Coverage report targets +if(ENABLE_COVERAGE) + find_program(GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat) + if(NOT GENHTML_PATH) + message(FATAL_ERROR "no genhtml installed") + endif() + + set(FASTCOV_PATH ${VALHALLA_SOURCE_DIR}/third_party/fastcov/fastcov.py) + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/coverage.info + COMMAND ${FASTCOV_PATH} -d . --exclude /usr/ third_party/ ${CMAKE_CURRENT_BINARY_DIR}/ --lcov -o coverage.info + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS check) + + add_custom_target(coverage + COMMAND ${GENHTML_PATH} --prefix ${CMAKE_CURRENT_BINARY_DIR} --output-directory coverage --title "Test Coverage" --legend --show-details coverage.info + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/coverage.info) + set_target_properties(coverage PROPERTIES FOLDER "Tests") endif() ## Packaging via CPack @@ -373,7 +384,7 @@ set(CPACK_RESOURCE_FILE_LICENSE "${VALHALLA_SOURCE_DIR}/LICENSE.md") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "OpenStreetMap Routing API A set of routing APIs designed around OSM map data using dynamic costing and a tiled data structure") - set(CPACK_COMPONENT_PYHON_DESCRIPTION "OpenStreetMap Routing Python Bindings + set(CPACK_COMPONENT_PYTHON_DESCRIPTION "OpenStreetMap Routing Python Bindings A set routing APIs designed around OSM map data using dynamic costing and a tiled data structure and accompanying tools and services used to analyse and @@ -416,3 +427,6 @@ endif() set(CPACK_PROJECT_CONFIG_FILE ${VALHALLA_SOURCE_DIR}/cmake/CPackConfig.cmake) set(CPACK_DEBIAN_PACKAGE_DEBUG OFF) include(CPack) + +configure_file(cmake/uninstall.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/valhalla_uninstall.cmake @ONLY) +add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/valhalla_uninstall.cmake) diff --git a/Dockerfile b/Dockerfile index 4fa549fa4f..eb5fb6c510 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,23 @@ +# TODO: we should make use of BUILDPLATFORM and TARGETPLATFORM to figure out cross compiling +# as mentioned here: docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide +# then we could use the host architecture to simply compile to the target architecture without +# emulating the target architecture (thereby making the build hyper slow). the general gist is +# we add arm (or whatever architecture) repositories to apt and then install our dependencies +# with the architecture suffix, eg. :arm64. then we just need to set a bunch of cmake variables +# probably with the use of a cmake toolchain file so that cmake can make sure to use the +# binaries that can target the target architecture. from there bob is your uncle maybe.. + #################################################################### -FROM ubuntu:22.04 as builder +FROM ubuntu:24.04 as builder MAINTAINER Kevin Kreiser ARG CONCURRENCY +ARG ADDITIONAL_TARGETS # set paths -ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH -ENV LD_LIBRARY_PATH /usr/local/lib:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/lib32:/usr/lib32 +ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH +ENV LD_LIBRARY_PATH=/usr/local/lib:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/lib32:/usr/lib32 +RUN export DEBIAN_FRONTEND=noninteractive && apt update && apt install -y sudo # install deps WORKDIR /usr/local/src/valhalla @@ -20,14 +31,11 @@ RUN ls -la RUN git submodule sync && git submodule update --init --recursive RUN rm -rf build && mkdir build -# upgrade Conan again, to avoid using an outdated version: -# https://github.com/valhalla/valhalla/issues/3685#issuecomment-1198604174 -RUN pip install --upgrade "conan<2.0.0" - # configure the build with symbols turned on so that crashes can be triaged WORKDIR /usr/local/src/valhalla/build -RUN cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=gcc -RUN make all -j${CONCURRENCY:-$(nproc)} +# switch back to -DCMAKE_BUILD_TYPE=RelWithDebInfo and uncomment the block below if you want debug symbols +RUN cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=gcc -DENABLE_SINGLE_FILES_WERROR=Off +RUN make all ${ADDITIONAL_TARGETS} -j${CONCURRENCY:-$(nproc)} RUN make install # we wont leave the source around but we'll drop the commit hash we'll also keep the locales @@ -37,36 +45,40 @@ RUN for f in valhalla/locales/*.json; do cat ${f} | python3 -c 'import sys; impo RUN rm -rf valhalla # the binaries are huge with all the symbols so we strip them but keep the debug there if we need it -WORKDIR /usr/local/bin -RUN for f in valhalla_*; do objcopy --only-keep-debug $f $f.debug; done -RUN tar -cvf valhalla.debug.tar valhalla_*.debug && gzip -9 valhalla.debug.tar -RUN rm -f valhalla_*.debug -RUN strip --strip-debug --strip-unneeded valhalla_* || true -RUN strip /usr/local/lib/libvalhalla.a -RUN strip /usr/lib/python3/dist-packages/valhalla/python_valhalla*.so +#WORKDIR /usr/local/bin +#RUN for f in valhalla_*; do objcopy --only-keep-debug $f $f.debug; done +#RUN tar -cvf valhalla.debug.tar valhalla_*.debug && gzip -9 valhalla.debug.tar +#RUN rm -f valhalla_*.debug +#RUN strip --strip-debug --strip-unneeded valhalla_* || true +#RUN strip /usr/local/lib/libvalhalla.a +#RUN strip /usr/lib/python3/dist-packages/valhalla/python_valhalla*.so #################################################################### # copy the important stuff from the build stage to the runner image -FROM ubuntu:22.04 as runner +FROM ubuntu:24.04 as runner MAINTAINER Kevin Kreiser +# basic paths +ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH +ENV LD_LIBRARY_PATH=/usr/local/lib:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/lib32:/usr/lib32 + # github packaging niceties LABEL org.opencontainers.image.description = "Open Source Routing Engine for OpenStreetMap and Other Datasources" LABEL org.opencontainers.image.source = "https://github.com/valhalla/valhalla" +# grab the builder stages artifacts COPY --from=builder /usr/local /usr/local -COPY --from=builder /usr/lib/python3/dist-packages/valhalla/* /usr/lib/python3/dist-packages/valhalla/ +COPY --from=builder /usr/local/lib/python3.12/dist-packages/valhalla/* /usr/local/lib/python3.12/dist-packages/valhalla/ # we need to add back some runtime dependencies for binaries and scripts # install all the posix locales that we support RUN export DEBIAN_FRONTEND=noninteractive && apt update && \ apt install -y \ - libcurl4 libczmq4 libluajit-5.1-2 \ - libprotobuf-lite23 libsqlite3-0 libsqlite3-mod-spatialite libzmq5 zlib1g \ - curl gdb locales parallel python3.10-minimal python3-distutils python-is-python3 \ - spatialite-bin unzip wget && \ - cat /usr/local/src/valhalla_locales | xargs -d '\n' -n1 locale-gen && \ - rm -rf /var/lib/apt/lists/* && \ - \ - # python smoke test - python3 -c "import valhalla,sys; print(sys.version, valhalla)" + libcurl4 libczmq4 libluajit-5.1-2 libgdal34 \ + libprotobuf-lite32 libsqlite3-0 libsqlite3-mod-spatialite libzmq5 zlib1g \ + curl gdb locales parallel python3-minimal python-is-python3 python3-shapely python3-requests \ + spatialite-bin unzip wget && rm -rf /var/lib/apt/lists/* +RUN cat /usr/local/src/valhalla_locales | xargs -d '\n' -n1 locale-gen + +# python smoke test +RUN python3 -c "import valhalla,sys; print(sys.version, valhalla)" diff --git a/README.md b/README.md index ea71077b59..940970b05b 100644 --- a/README.md +++ b/README.md @@ -18,14 +18,14 @@ Valhalla is an open source routing engine and accompanying libraries for use wit ## Build Status -| Linux/MacOs | Windows | MinGW64 | Code Coverage | -| ----------- | ------- | ------------- | ------------- | -| [![Circle CI](https://circleci.com/gh/valhalla/valhalla/tree/master.svg?style=svg)](https://circleci.com/gh/valhalla/valhalla/tree/master) | [![Build Status](https://dev.azure.com/valhalla1/valhalla/_apis/build/status/valhalla.valhalla?branchName=master)](https://dev.azure.com/valhalla1/valhalla/_build/latest?definitionId=1&branchName=master) | ![Valhalla MinGW Build](https://github.com/valhalla/valhalla/workflows/Valhalla%20MinGW%20Build/badge.svg) | [![codecov](https://codecov.io/gh/valhalla/valhalla/branch/master/graph/badge.svg)](https://codecov.io/gh/valhalla/valhalla) | +| Linux/MacOs | Windows | Code Coverage | +| ----------- | ------- | ------------- | +| [![Circle CI](https://circleci.com/gh/valhalla/valhalla/tree/master.svg?style=svg)](https://circleci.com/gh/valhalla/valhalla/tree/master) | [![Windows CI](https://github.com/valhalla/valhalla/actions/workflows/win.yml/badge.svg)](https://github.com/valhalla/valhalla/actions/workflows/win.yml) | [![codecov](https://codecov.io/gh/valhalla/valhalla/branch/master/graph/badge.svg)](https://codecov.io/gh/valhalla/valhalla) | ## License -Valhalla, and all of the projects under the Valhalla organization, use the [MIT License](COPYING). Avatar/logo by [Jordan](https://www.instagram.com/jaykaydraws/). +Valhalla, and all of the projects under the Valhalla organization, use the [MIT License](COPYING). Avatar/logo by [Jordan](https://www.jaykaydraws.com/portfolio). OpenStreetMap data in the `./test/data` is licensed under [ODbL](https://opendatacommons.org/licenses/odbl/) and [copyrighted](https://www.openstreetmap.org/copyright) by OSM contributors. Additional information on licenses and other requirements concerning the data sources most frequently used by Valhalla can be found in [the docs](https://valhalla.github.io/valhalla/mjolnir/data_sources/). @@ -131,3 +131,4 @@ The following projects are open-source and built with the intention to make it e - [**routingjs**](https://github.com/gis-ops/routingjs): A TypeScript client for most open-source routing engines, including Valhalla, with a common interface for all engines. Available as engine-specific packages on [npm](https://www.npmjs.com/package/@routingjs/valhalla). - [**pyvalhalla**](https://github.com/gis-ops/pyvalhalla): Python bindings for Valhalla, so its APIs can be used from within Python without a HTTP service. Available on [PyPI](https://pypi.org/project/pyvalhalla/). - [**Valhalla_jll.jl**](https://github.com/JuliaBinaryWrappers/Valhalla_jll.jl): Valhalla binaries shipped for Julia. +- [**valhalla-go**](https://github.com/pufferffish/valhalla-go): Valhalla Golang bindings via cgo diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt deleted file mode 100644 index ceab3775a5..0000000000 --- a/bench/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -set(BENCHMARK_ENABLE_TESTING OFF) -add_subdirectory(${VALHALLA_SOURCE_DIR}/third_party/benchmark - ${CMAKE_BINARY_DIR}/benchmark) -set_target_properties(benchmark PROPERTIES FOLDER "Dependencies") -set_target_properties(benchmark_main PROPERTIES FOLDER "Dependencies") - -# Custom targets building and running all microbenchmarks in the project -add_custom_target(benchmarks) -set_target_properties(benchmarks PROPERTIES FOLDER "Benchmarks") -add_custom_target(run-benchmarks) -set_target_properties(run-benchmarks PROPERTIES FOLDER "Benchmarks") - -# Benchmarks generally require utrecht test tiles to be present, so add this dependency by default. -add_dependencies(benchmarks utrecht_tiles) - -# Macro for defining Google Benchmark microbenchmark targets -macro (add_valhalla_benchmark target_file) - set(target_name benchmark-${target_file}) - add_executable(${target_name} - ${target_file}.cc) - set_target_properties(${target_name} PROPERTIES FOLDER "Benchmarks") - set_target_properties(${target_name} - PROPERTIES - COMPILE_DEFINITIONS - VALHALLA_SOURCE_DIR="${VALHALLA_SOURCE_DIR}/") - target_link_libraries(${target_name} valhalla_test benchmark::benchmark) - add_dependencies(benchmarks ${target_name}) - add_dependencies(${target_name} utrecht_tiles) - # Add a custom target running the benchmark - add_custom_target(run-${target_name} - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${target_name} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMENT "Running ${target_name} in ${CMAKE_CURRENT_BINARY_DIR}" - DEPENDS ${target_name}) - set_target_properties(run-${target_name} PROPERTIES FOLDER "Benchmarks") - add_dependencies(run-${target_name} utrecht_tiles) - add_dependencies(run-benchmarks run-${target_name}) -endmacro() - -add_subdirectory(meili) -add_subdirectory(thor) diff --git a/bench/meili/CMakeLists.txt b/bench/meili/CMakeLists.txt deleted file mode 100644 index 6d817a025e..0000000000 --- a/bench/meili/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_valhalla_benchmark(mapmatch) diff --git a/bench/meili/config.json b/bench/meili/config.json deleted file mode 100644 index 44a02b8b26..0000000000 --- a/bench/meili/config.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "mjolnir": { - "tile_dir": "test/data/utrecht_tiles", - "concurrency": 1 - }, - "loki": { - "actions": [ - "route", - "sources_to_targets", - "trace_route", - "trace_attributes" - ], - "logging": { - "long_request": 100 - }, - "service_defaults": { - "minimum_reachability": 50, - "radius": 0, - "search_cutoff": 35000, - "node_snap_tolerance": 5, - "street_side_tolerance": 5, - "street_side_max_distance": 1000, - "heading_tolerance": 60 - } - }, - "thor": { - "logging": { - "long_request": 110 - } - }, - "meili": { - "customizable": [ - "turn_penalty_factor", - "max_route_distance_factor", - "max_route_time_factor", - "search_radius" - ], - "mode": "auto", - "grid": { - "cache_size": 100240, - "size": 500 - }, - "default": { - "beta": 3, - "breakage_distance": 2000, - "geometry": false, - "gps_accuracy": 5, - "interpolation_distance": 10, - "max_route_distance_factor": 5, - "max_route_time_factor": 5, - "max_search_radius": 200, - "route": true, - "search_radius": 15, - "sigma_z": 4.07, - "turn_penalty_factor": 200 - } - }, - "service_limits": { - "auto": { - "max_distance": 5000000, - "max_locations": 20, - "max_matrix_distance": 400000, - "max_matrix_location_pairs": 2500 - }, - "auto_shorter": { - "max_distance": 5000000, - "max_locations": 20, - "max_matrix_distance": 400000, - "max_matrix_location_pairs": 2500 - }, - "isochrone": { - "max_contours": 4, - "max_distance": 25000, - "max_locations": 1, - "max_time_contour": 120, - "max_distance_contour": 200 - }, - "max_exclude_locations": 50, - "max_radius": 200, - "max_reachability": 100, - "max_alternates": 2, - "max_exclude_polygons_length": 10000, - "max_distance_disable_hierarchy_culling": 0, - "multimodal": { - "max_distance": 500000, - "max_locations": 50, - "max_matrix_distance": 0, - "max_matrix_location_pairs": 0 - }, - "pedestrian": { - "max_distance": 250000, - "max_locations": 50, - "max_matrix_distance": 200000, - "max_matrix_location_pairs": 2500, - "max_transit_walking_distance": 10000, - "min_transit_walking_distance": 1 - }, - "skadi": { - "max_shape": 750000, - "min_resample": 10 - }, - "trace": { - "max_distance": 200000, - "max_gps_accuracy": 100, - "max_search_radius": 100, - "max_shape": 16000, - "max_alternates": 3, - "max_alternates_shape": 100 - } - } -} diff --git a/bench/meili/fixtures/3km_loop_utrecht.json b/bench/meili/fixtures/3km_loop_utrecht.json deleted file mode 100644 index e3858b1999..0000000000 --- a/bench/meili/fixtures/3km_loop_utrecht.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "costing": "auto", - "format": "osrm", - "shape_match": "map_snap", - "shape": [ - { - "lon": 5.08531221, - "lat": 52.0938563, - "type": "break" - }, - { - "lon": 5.0865867, - "lat": 52.0930211, - "type": "break" - }, - { - "lon": 5.08769141, - "lat": 52.0923946, - "type": "break" - }, - { - "lon": 5.0896245, - "lat": 52.0912591, - "type": "break" - }, - { - "lon": 5.0909416, - "lat": 52.090737, - "type": "break" - }, - { - "lon": 5.0926623, - "lat": 52.0905021, - "type": "break" - }, - { - "lon": 5.0946379, - "lat": 52.090737, - "type": "break" - }, - { - "lon": 5.0961035, - "lat": 52.0907892, - "type": "break" - }, - { - "lon": 5.097442, - "lat": 52.0909328, - "type": "break" - }, - { - "lon": 5.09884401, - "lat": 52.09115474, - "type": "break" - }, - { - "lon": 5.100416, - "lat": 52.0913244, - "type": "break" - }, - { - "lon": 5.101733, - "lat": 52.09137664, - "type": "break" - }, - { - "lon": 5.1034112, - "lat": 52.0915854, - "type": "break" - }, - { - "lon": 5.10351751, - "lat": 52.09202915, - "type": "break" - }, - { - "lon": 5.102345, - "lat": 52.0929627, - "type": "break" - }, - { - "lon": 5.0959337, - "lat": 52.093477899999996, - "type": "break" - }, - { - "lon": 5.0932129, - "lat": 52.0939153, - "type": "break" - }, - { - "lon": 5.08858141, - "lat": 52.094623799999994, - "type": "break" - }, - { - "lon": 5.0858904, - "lat": 52.0958159, - "type": "break" - } - ] -} diff --git a/bench/meili/fixtures/intersection_matching1.json b/bench/meili/fixtures/intersection_matching1.json deleted file mode 100644 index 88b84c375e..0000000000 --- a/bench/meili/fixtures/intersection_matching1.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "costing": "auto", - "format": "osrm", - "shape_match": "map_snap", - "shape": [ - { - "lat": 52.0981267, - "lon": 5.129618, - "type": "break" - }, - { - "lat": 52.098128, - "lon": 5.129725, - "type": "break" - } - ] -} diff --git a/bench/meili/fixtures/intersection_matching2.json b/bench/meili/fixtures/intersection_matching2.json deleted file mode 100644 index 39cb22ed5f..0000000000 --- a/bench/meili/fixtures/intersection_matching2.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "costing": "auto", - "format": "osrm", - "shape_match": "map_snap", - "shape": [ - { - "lat": 52.0981346, - "lon": 5.1300437, - "type": "break" - }, - { - "lat": 52.0981145, - "lon": 5.1309431, - "type": "break" - }, - { - "lat": 52.0980642, - "lon": 5.1314993, - "type": "break" - }, - { - "lat": 52.0971149, - "lon": 5.1311002, - "type": "break" - } - ] -} diff --git a/bench/meili/fixtures/intersection_matching3.json b/bench/meili/fixtures/intersection_matching3.json deleted file mode 100644 index 3b365ea8fe..0000000000 --- a/bench/meili/fixtures/intersection_matching3.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "costing": "auto", - "format": "osrm", - "shape_match": "map_snap", - "shape": [ - { - "lat": 52.0951641, - "lon": 5.1285609, - "type": "break" - }, - { - "lat": 52.0952055, - "lon": 5.1292756, - "type": "break" - }, - { - "lat": 52.095258, - "lon": 5.1301359, - "type": "break" - }, - { - "lat": 52.0952939, - "lon": 5.130902, - "type": "break" - }, - { - "lat": 52.0944788, - "lon": 5.1304066, - "type": "break" - } - ] -} diff --git a/bench/meili/mapmatch.cc b/bench/meili/mapmatch.cc deleted file mode 100644 index 005cfc995c..0000000000 --- a/bench/meili/mapmatch.cc +++ /dev/null @@ -1,122 +0,0 @@ -#include -#include - -#include -#include - -#include "baldr/rapidjson_utils.h" -#include "meili/map_matcher_factory.h" -#include "meili/measurement.h" -#include "sif/costconstants.h" -#include "sif/costfactory.h" -#include "tyr/actor.h" - -using namespace valhalla::midgard; -using namespace valhalla::meili; -using namespace valhalla::sif; - -namespace { - -#if !defined(VALHALLA_SOURCE_DIR) -#define VALHALLA_SOURCE_DIR -#endif - -constexpr float kGpsAccuracyMeters = 4.07; -constexpr float kSearchRadiusMeters = 50; - -// Inline benchmarks for OfflineMatch - -class OfflineMapmatchFixture : public benchmark::Fixture { -public: - void SetUp(const ::benchmark::State& state) { - (void)state; - InitEngineConfig(); - InitMapMatcher(); - } - - void TearDown(const ::benchmark::State& state) { - (void)state; - matcher_factory_->ClearCache(); - mapmatcher_->Clear(); - matcher_factory_.reset(); - mapmatcher_.reset(); - } - -private: - void InitEngineConfig() { - rapidjson::read_json(VALHALLA_SOURCE_DIR "bench/meili/config.json", config_); - const rapidjson::Document doc; - valhalla::sif::ParseCosting(doc, "/costing_options", options_); - options_.set_costing_type(valhalla::Costing::auto_); - } - - void InitMapMatcher() { - matcher_factory_ = std::make_shared(config_); - mapmatcher_.reset(matcher_factory_->Create(options_)); - } - -protected: - // Mapmatcher implementation - std::shared_ptr mapmatcher_; - std::shared_ptr matcher_factory_; - // Mapmatching configuration - boost::property_tree::ptree config_; - valhalla::Options options_; -}; - -std::vector BuildMeasurements(float gps_accuracy, float search_radius) { - std::vector meas; - meas.emplace_back(PointLL(5.09806, 52.09110), gps_accuracy, search_radius); - meas.emplace_back(PointLL(5.09769, 52.09050), gps_accuracy, search_radius); - meas.emplace_back(PointLL(5.09679, 52.09098), gps_accuracy, search_radius); - return meas; -} - -BENCHMARK_DEFINE_F(OfflineMapmatchFixture, BasicOfflineMatch)(benchmark::State& state) { - logging::Configure({{"type", ""}}); - const auto& meas = BuildMeasurements(kGpsAccuracyMeters, kSearchRadiusMeters); - for (auto _ : state) { - benchmark::DoNotOptimize(mapmatcher_->OfflineMatch(meas)); - } -} - -BENCHMARK_REGISTER_F(OfflineMapmatchFixture, BasicOfflineMatch); - -// Load fixture files, intended to mirror test cases defined in test/mapmatch.cc. - -std::string LoadFile(const std::string& filename) { - std::stringstream ss; - std::string line; - std::ifstream input_file; - input_file.open(filename.c_str()); - while (std::getline(input_file, line)) { - ss << line; - } - return ss.str(); -} - -const std::vector kBenchmarkCases = { - // Intersection matching test cases - VALHALLA_SOURCE_DIR "bench/meili/fixtures/intersection_matching1.json", - VALHALLA_SOURCE_DIR "bench/meili/fixtures/intersection_matching2.json", - VALHALLA_SOURCE_DIR "bench/meili/fixtures/intersection_matching3.json", - // 2.8km loop in Utrecht - VALHALLA_SOURCE_DIR "bench/meili/fixtures/3km_loop_utrecht.json", -}; - -static void BM_ManyCases(benchmark::State& state) { - logging::Configure({{"type", ""}}); - boost::property_tree::ptree config; - rapidjson::read_json(VALHALLA_SOURCE_DIR "bench/meili/config.json", config); - valhalla::tyr::actor_t actor(config, true); - const std::string test_case(LoadFile(kBenchmarkCases[state.range(0)])); - for (auto _ : state) { - benchmark::DoNotOptimize(actor.trace_route(test_case)); - } -} - -BENCHMARK(BM_ManyCases)->DenseRange(0, kBenchmarkCases.size() - 1); - -} // namespace - -BENCHMARK_MAIN(); diff --git a/bench/thor/CMakeLists.txt b/bench/thor/CMakeLists.txt deleted file mode 100644 index de77da6b45..0000000000 --- a/bench/thor/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_valhalla_benchmark(costmatrix) -add_valhalla_benchmark(routes) -add_valhalla_benchmark(isochrone) -add_valhalla_benchmark(reach) diff --git a/bench/thor/costmatrix.cc b/bench/thor/costmatrix.cc deleted file mode 100644 index 1dbc415bdc..0000000000 --- a/bench/thor/costmatrix.cc +++ /dev/null @@ -1,121 +0,0 @@ -#include -#include -#include -#include -#include - -#include "baldr/graphreader.h" -#include "loki/search.h" -#include "midgard/pointll.h" -#include "sif/autocost.h" -#include "sif/costfactory.h" -#include "thor/costmatrix.h" -#include - -using namespace valhalla; - -namespace { - -boost::property_tree::ptree json_to_pt(const std::string& json) { - std::stringstream ss; - ss << json; - boost::property_tree::ptree pt; - rapidjson::read_json(ss, pt); - return pt; -} - -const auto config = json_to_pt(R"({ - "mjolnir":{"tile_dir":"test/data/utrecht_tiles", "concurrency": 1}, - "loki":{ - "actions":["sources_to_targets"], - "logging":{"long_request": 100}, - "service_defaults":{"minimum_reachability": 50,"radius": 0,"search_cutoff": 35000, "node_snap_tolerance": 5, "street_side_tolerance": 5, "street_side_max_distance": 1000, "heading_tolerance": 60} - }, - "thor":{ - "logging":{"long_request": 100} - }, - "meili":{ - "grid": { - "cache_size": 100240, - "size": 500 - } - }, - "service_limits": { - "auto": {"max_distance": 5000000.0, "max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "auto_shorter": {"max_distance": 5000000.0,"max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "bicycle": {"max_distance": 500000.0,"max_locations": 50,"max_matrix_distance": 200000.0,"max_matrix_location_pairs": 2500}, - "bus": {"max_distance": 5000000.0,"max_locations": 50,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "hov": {"max_distance": 5000000.0,"max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "taxi": {"max_distance": 5000000.0,"max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "isochrone": {"max_contours": 4,"max_distance": 25000.0,"max_locations": 1,"max_time_contour": 120,"max_distance_contour":200}, - "max_exclude_locations": 50,"max_radius": 200,"max_reachability": 100,"max_alternates":2,"max_exclude_polygons_length":10000, - "multimodal": {"max_distance": 500000.0,"max_locations": 50,"max_matrix_distance": 0.0,"max_matrix_location_pairs": 0}, - "pedestrian": {"max_distance": 250000.0,"max_locations": 50,"max_matrix_distance": 200000.0,"max_matrix_location_pairs": 2500,"max_transit_walking_distance": 10000,"min_transit_walking_distance": 1}, - "skadi": {"max_shape": 750000,"min_resample": 10.0}, - "trace": {"max_distance": 200000.0,"max_gps_accuracy": 100.0,"max_search_radius": 100,"max_shape": 16000,"max_best_paths":4,"max_best_paths_shape":100}, - "transit": {"max_distance": 500000.0,"max_locations": 50,"max_matrix_distance": 200000.0,"max_matrix_location_pairs": 2500}, - "truck": {"max_distance": 5000000.0,"max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500} - } - })"); - -constexpr float kMaxRange = 256; - -static void BM_UtrechtCostMatrix(benchmark::State& state) { - const int size = state.range(0); - baldr::GraphReader reader(config.get_child("mjolnir")); - - // Generate N random locations within the Utrect bounding box; - std::vector locations; - const double min_lon = 5.0163; - const double max_lon = 5.1622; - const double min_lat = 52.0469999; - const double max_lat = 52.1411; - - std::mt19937 gen(0); // Seed with the same value for consistent benchmarking - std::uniform_real_distribution<> lng_distribution(min_lon, max_lon); - std::uniform_real_distribution<> lat_distribution(min_lat, max_lat); - - locations.reserve(size); - for (int i = 0; i < size; i++) { - locations.emplace_back(midgard::PointLL{lng_distribution(gen), lat_distribution(gen)}); - } - - Options options; - options.set_costing_type(Costing::auto_); - rapidjson::Document doc; - sif::ParseCosting(doc, "/costing_options", options); - sif::TravelMode mode; - auto costs = sif::CostFactory().CreateModeCosting(options, mode); - auto cost = costs[static_cast(mode)]; - - const auto projections = loki::Search(locations, reader, cost); - if (projections.size() == 0) { - throw std::runtime_error("Found no matching locations"); - } - - google::protobuf::RepeatedPtrField sources; - - for (const auto& projection : projections) { - auto* p = sources.Add(); - baldr::PathLocation::toPBF(projection.second, p, reader); - } - - std::size_t result_size = 0; - - thor::CostMatrix matrix; - for (auto _ : state) { - auto result = matrix.SourceToTarget(sources, sources, reader, costs, mode, 100000.); - matrix.clear(); - result_size += result.size(); - } - state.counters["Routes"] = benchmark::Counter(size, benchmark::Counter::kIsIterationInvariantRate); -} - -BENCHMARK(BM_UtrechtCostMatrix) - ->Unit(benchmark::kMillisecond) - ->RangeMultiplier(2) - ->Range(1, kMaxRange); - -} // namespace - -BENCHMARK_MAIN(); diff --git a/bench/thor/isochrone.cc b/bench/thor/isochrone.cc deleted file mode 100644 index f67867ce24..0000000000 --- a/bench/thor/isochrone.cc +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include -#include -#include - -#include "baldr/graphreader.h" -//#include - -#include "loki/worker.h" -#include "thor/worker.h" - -#include "test.h" - -using namespace valhalla; - -namespace { - -// Maximum isochrone range to test during the benchmark -// This benchmark is on the Utrecht tiles we test against, so -// 30 minutes pretty much gets you to the edge from the middle -// in any direction -constexpr float kMaxDurationMinutes = 120; - -// Test the core isochrone calculation algorithm -void BM_IsochroneUtrecht(benchmark::State& state) { - const int size = state.range(0); - - const auto config = - test::make_config("test/data/utrecht_tiles", {}, - {{"additional_data", "mjolnir.traffic_extract", "mjolnir.tile_extract"}}); - valhalla::loki::loki_worker_t loki_worker(config); - valhalla::thor::thor_worker_t thor_worker(config); - - const auto request_json = - R"({"locations":[{"lat":52.078937,"lon":5.115321}],"costing":"auto","contours":[{"time":)" + - std::to_string(size) + R"(}],"polygons":false,"denoise":1,"generalize":20})"; - - // compute the isochrone - valhalla::Api request; - valhalla::ParseApi(request_json, Options::isochrone, request); - loki_worker.isochrones(request); - - for (auto _ : state) { - auto response_json = thor_worker.isochrones(request); - // std::cout << response_json << std::endl; - } -} - -BENCHMARK(BM_IsochroneUtrecht) - ->Unit(benchmark::kMillisecond) - ->RangeMultiplier(2) - ->Range(1, kMaxDurationMinutes) - ->Repetitions(10); - -} // namespace - -BENCHMARK_MAIN(); diff --git a/bench/thor/reach.cc b/bench/thor/reach.cc deleted file mode 100644 index de5801a966..0000000000 --- a/bench/thor/reach.cc +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include -#include -#include - -#include "baldr/graphreader.h" -#include "loki/reach.h" -#include "sif/costfactory.h" -#include "test.h" - -using namespace valhalla; - -namespace { - -// Test the core isochrone calculation algorithm -void BM_ReachUtrecht(benchmark::State& state) { - - const auto config = - test::make_config("test/data/utrecht_tiles", {}, - {{"additional_data", "mjolnir.traffic_extract", "mjolnir.tile_extract"}}); - - // get tile access - GraphReader reader(config.get_child("mjolnir")); - - auto costing = sif::CostFactory{}.Create(Costing::auto_); - loki::Reach reach_finder; - - using Edge = std::pair; - std::vector edges; - - for (auto tile_id : reader.GetTileSet()) { - auto tile = reader.GetGraphTile(tile_id); - for (GraphId edge_id = tile->header()->graphid(); - edge_id.id() < tile->header()->directededgecount(); ++edge_id) { - const auto* edge = tile->directededge(edge_id); - edges.emplace_back(edge_id, edge); - } - } - - for (auto _ : state) { - for (const auto& edge : edges) { - auto reach = reach_finder(edge.second, edge.first, 50, reader, costing, kInbound | kOutbound); - } - } -} - -BENCHMARK(BM_ReachUtrecht)->Unit(benchmark::kMillisecond)->Repetitions(10); - -} // namespace - -BENCHMARK_MAIN(); diff --git a/bench/thor/routes.cc b/bench/thor/routes.cc deleted file mode 100644 index f588966b0c..0000000000 --- a/bench/thor/routes.cc +++ /dev/null @@ -1,426 +0,0 @@ -#include -#include -#include -#include -#include - -#include "baldr/graphreader.h" -#include "loki/search.h" -#include "midgard/logging.h" -#include "midgard/pointll.h" -#include "sif/autocost.h" -#include "sif/costfactory.h" -#include "test.h" -#include "thor/bidirectional_astar.h" -#include "thor/unidirectional_astar.h" -#include - -using namespace valhalla; - -namespace { - -void create_costing_options(Options& options) { - options.set_costing_type(Costing::auto_); - rapidjson::Document doc; - sif::ParseCosting(doc, "/costing_options", options); -} - -boost::property_tree::ptree json_to_pt(const std::string& json) { - std::stringstream ss; - ss << json; - boost::property_tree::ptree pt; - rapidjson::read_json(ss, pt); - return pt; -} - -boost::property_tree::ptree build_config(const char* live_traffic_tar) { - return json_to_pt(R"({ - "mjolnir":{ - "traffic_extract": "test/data/utrecht_tiles/)" + - std::string(live_traffic_tar) + R"(", - "tile_dir": "test/data/utrecht_tiles", - "concurrency": 1 - }, - "loki":{ - "actions":["route"], - "logging":{"long_request": 100}, - "service_defaults":{ - "minimum_reachability": 10, - "radius": 50, - "search_cutoff": 35000, - "node_snap_tolerance": 5, - "street_side_tolerance": 5, - "heading_tolerance": 360 - } - }, - "thor":{ - "logging":{"long_request": 100} - }, - "meili":{ - "grid": { - "cache_size": 100240, - "size": 500 - } - }, - "service_limits": { - "auto": {"max_distance": 5000000.0, "max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "auto_shorter": {"max_distance": 5000000.0,"max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "bicycle": {"max_distance": 500000.0,"max_locations": 50,"max_matrix_distance": 200000.0,"max_matrix_location_pairs": 2500}, - "bus": {"max_distance": 5000000.0,"max_locations": 50,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "hov": {"max_distance": 5000000.0,"max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "taxi": {"max_distance": 5000000.0,"max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "isochrone": {"max_contours": 4,"max_distance": 25000.0,"max_locations": 1,"max_time_contour": 120,"max_distance_contour":200}, - "max_exclude_locations": 50,"max_radius": 200,"max_reachability": 100,"max_alternates":2,"max_exclude_polygons_length":10000, - "multimodal": {"max_distance": 500000.0,"max_locations": 50,"max_matrix_distance": 0.0,"max_matrix_location_pairs": 0}, - "pedestrian": {"max_distance": 250000.0,"max_locations": 50,"max_matrix_distance": 200000.0,"max_matrix_location_pairs": 2500,"max_transit_walking_distance": 10000,"min_transit_walking_distance": 1}, - "skadi": {"max_shape": 750000,"min_resample": 10.0}, - "trace": {"max_distance": 200000.0,"max_gps_accuracy": 100.0,"max_search_radius": 100,"max_shape": 16000,"max_best_paths":4,"max_best_paths_shape":100}, - "transit": {"max_distance": 500000.0,"max_locations": 50,"max_matrix_distance": 200000.0,"max_matrix_location_pairs": 2500}, - "truck": {"max_distance": 5000000.0,"max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500} - } - })"); -} - -constexpr float kMaxRange = 256; - -static void BM_UtrechtBidirectionalAstar(benchmark::State& state) { - const auto config = build_config("generated-live-data.tar"); - test::build_live_traffic_data(config); - - std::mt19937 gen(0); // Seed with the same value for consistent benchmarking - { - // Something to generate traffic with - std::uniform_real_distribution<> traffic_dist(0., 1.); - // This fraction of edges have live traffic - float has_live_traffic = 0.2; - - // Make some updates to the traffic .tar file. - // Generate traffic data - std::function - generate_traffic = [&gen, &traffic_dist, - &has_live_traffic](baldr::GraphReader& reader, baldr::TrafficTile& tile, - int index, baldr::TrafficSpeed* current) -> void { - baldr::GraphId tile_id(tile.header->tile_id); - if (traffic_dist(gen) < has_live_traffic) { - current->breakpoint1 = 255; - current->overall_encoded_speed = traffic_dist(gen) * 100; - } else { - } - }; - test::customize_live_traffic_data(config, generate_traffic); - } - - auto clean_reader = test::make_clean_graphreader(config.get_child("mjolnir")); - - std::vector locations; - - Options options; - create_costing_options(options); - sif::TravelMode mode; - auto costs = sif::CostFactory().CreateModeCosting(options, mode); - auto cost = costs[static_cast(mode)]; - - // A few locations around Utrecht. Origins and destinations are constructed - // from these for the queries - locations.emplace_back(midgard::PointLL{5.115873, 52.099247}); - locations.emplace_back(midgard::PointLL{5.117328, 52.099464}); - locations.emplace_back(midgard::PointLL{5.114576, 52.101841}); - locations.emplace_back(midgard::PointLL{5.114598, 52.103607}); - locations.emplace_back(midgard::PointLL{5.112481, 52.074073}); - locations.emplace_back(midgard::PointLL{5.135983, 52.110116}); - locations.emplace_back(midgard::PointLL{5.095273, 52.108956}); - locations.emplace_back(midgard::PointLL{5.110077, 52.062043}); - locations.emplace_back(midgard::PointLL{5.025595, 52.067372}); - - const auto projections = loki::Search(locations, *clean_reader, cost); - if (projections.size() == 0) { - throw std::runtime_error("Found no matching locations"); - } - - std::vector origins; - std::vector destinations; - - { - auto it = projections.cbegin(); - if (it == projections.cend()) { - throw std::runtime_error("Found no matching locations"); - } - while (true) { - auto origin = valhalla::Location{}; - baldr::PathLocation::toPBF(it->second, &origin, *clean_reader); - ++it; - if (it == projections.cend()) { - break; - } - origins.push_back(origin); - destinations.push_back(valhalla::Location{}); - baldr::PathLocation::toPBF(it->second, &destinations.back(), *clean_reader); - } - } - - if (origins.size() == 0) { - throw std::runtime_error("No origins available for test"); - } - - std::size_t route_size = 0; - - thor::BidirectionalAStar astar; - for (auto _ : state) { - for (int i = 0; i < origins.size(); ++i) { - // LOG_WARN("Running index "+std::to_string(i)); - auto result = astar.GetBestPath(origins[i], destinations[i], *clean_reader, costs, - sif::TravelMode::kDrive); - astar.Clear(); - route_size += 1; - } - } - if (route_size == 0) { - throw std::runtime_error("Failed all routes"); - } - state.counters["Routes"] = route_size; -} - -void customize_traffic(const boost::property_tree::ptree& config, - baldr::GraphId& target_edge_id, - const int target_speed) { - test::build_live_traffic_data(config); - // Make some updates to the traffic .tar file. - // Generate traffic data - std::function - generate_traffic = [&target_edge_id, &target_speed](baldr::GraphReader& reader, - baldr::TrafficTile& tile, int index, - baldr::TrafficSpeed* current) -> void { - baldr::GraphId tile_id(tile.header->tile_id); - auto edge_id = baldr::GraphId(tile_id.tileid(), tile_id.level(), index); - if (edge_id == target_edge_id) { - current->breakpoint1 = 255; - current->overall_encoded_speed = target_speed >> 1; - current->encoded_speed1 = target_speed >> 1; - } - }; - test::customize_live_traffic_data(config, generate_traffic); -} - -BENCHMARK(BM_UtrechtBidirectionalAstar)->Unit(benchmark::kMillisecond); - -/* - * A set of fixed random routes across the globe. Taken from test_requests/random.txt - */ -std::vector global_locations = - {midgard::PointLL{-8.336801, 33.286377}, midgard::PointLL{5.872467, 50.575802}, - midgard::PointLL{11.524066, 3.862927}, midgard::PointLL{30.490564, -22.948921}, - midgard::PointLL{21.407406, 12.212897}, midgard::PointLL{21.408346, 12.209968}, - midgard::PointLL{17.784868, 44.147346}, midgard::PointLL{15.582961, 45.906693}, - midgard::PointLL{8.685453, 39.226093}, midgard::PointLL{2.142843, 52.584072}, - midgard::PointLL{16.960527, 52.423416}, midgard::PointLL{18.670666, 54.35183}, - midgard::PointLL{85.840378, 12.75919}, midgard::PointLL{84.008522, 9.926284}, - midgard::PointLL{20.597891, 41.602592}, midgard::PointLL{20.880438, 41.886894}, - midgard::PointLL{8.057651, 52.261757}, midgard::PointLL{6.309168, 49.66972}, - midgard::PointLL{37.617016, 55.746685}, midgard::PointLL{37.623234, 55.746956}, - midgard::PointLL{66.781364, 10.485166}, midgard::PointLL{68.890945, 10.163307}, - midgard::PointLL{13.961927, 15.293241}, midgard::PointLL{13.967668, 15.293}, - midgard::PointLL{4.126567, 51.035511}, midgard::PointLL{5.887219, 49.531387}, - midgard::PointLL{9.22713, 49.130718}, midgard::PointLL{11.066673, 49.452415}, - midgard::PointLL{95.504768, 18.474234}, midgard::PointLL{95.494049, 18.483744}, - midgard::PointLL{135.590393, 34.623756}, midgard::PointLL{135.521576, 34.759117}, - midgard::PointLL{3.953196, 36.537201}, midgard::PointLL{3.178043, 36.726479}, - midgard::PointLL{35.160679, 32.520855}, midgard::PointLL{35.766632, 32.706535}, - midgard::PointLL{1.74563, 53.791374}, midgard::PointLL{2.110271, 53.535301}, - midgard::PointLL{21.016792, 41.08852}, midgard::PointLL{21.020342, 41.080669}}; - -template -void BM_GlobalFixedRandom(benchmark::State& state, const std::string& planet_path) { - - if (planet_path.empty()) { - state.SkipWithError( - "No planet file specified, please supply --planet-path=X on the command line"); - return; - } - - auto config = - test::make_config("test/data/utrecht_tiles", {}, - {{"additional_data", "mjolnir.traffic_extract", "mjolnir.tile_dir"}}); - config.put("mjolnir.tile_extract", planet_path); - - auto clean_reader = test::make_clean_graphreader(config.get_child("mjolnir")); - - Options options; - create_costing_options(options); - sif::TravelMode mode; - auto costs = sif::CostFactory().CreateModeCosting(options, mode); - auto cost = costs[static_cast(mode)]; - - std::vector locations(global_locations.begin() + state.range(0), - global_locations.begin() + state.range(0) + 2); - - const auto projections = loki::Search(locations, *clean_reader, cost); - if (projections.size() == 0) { - throw std::runtime_error("Found no matching locations"); - } - - std::vector origins; - std::vector destinations; - - { - auto it = projections.cbegin(); - if (it == projections.cend()) { - throw std::runtime_error("Found no matching locations"); - } - while (true) { - auto origin = valhalla::Location{}; - origin.set_date_time("2021-04-01T00:00:00"); - baldr::PathLocation::toPBF(it->second, &origin, *clean_reader); - ++it; - if (it == projections.cend()) { - break; - } - origins.push_back(origin); - destinations.push_back(valhalla::Location{}); - baldr::PathLocation::toPBF(it->second, &destinations.back(), *clean_reader); - destinations.back().set_date_time("2021-04-01T00:00:00"); - } - } - - if (origins.size() == 0) { - throw std::runtime_error("No origins available for test"); - } - - Algorithm algorithm; - { - // Do it once to warmup - auto result = algorithm.GetBestPath(origins.front(), destinations.front(), *clean_reader, costs, - sif::TravelMode::kDrive); - std::string coords = std::to_string(locations[0].latlng_.lng()) + "," + - std::to_string(locations[0].latlng_.lat()) + " -> " + - std::to_string(locations[1].latlng_.lng()) + "," + - std::to_string(locations[1].latlng_.lat()); - if (result.empty() || result.front().empty()) { - state.SkipWithError(std::string("Route " + coords + " returned no result").c_str()); - return; - } - algorithm.Clear(); - // std::cout << coords << " = route eta of " - // << std::to_string(result.back().back().elapsed_cost.secs) << " seconds" << std::endl; - } - for (auto _ : state) { - auto result = algorithm.GetBestPath(origins.front(), destinations.front(), *clean_reader, costs, - sif::TravelMode::kDrive); - algorithm.Clear(); - } -} - -/** Benchmarks the GetSpeed function */ -static void BM_GetSpeed(benchmark::State& state) { - - const auto config = build_config("get-speed.tar"); - auto tgt_edge_id = baldr::GraphId(3196, 0, 3221); - const auto tgt_speed = 50; - customize_traffic(config, tgt_edge_id, tgt_speed); - - auto clean_reader = test::make_clean_graphreader(config.get_child("mjolnir")); - - auto tile = clean_reader->GetGraphTile(baldr::GraphId(tgt_edge_id)); - if (tile == nullptr) { - throw std::runtime_error("Target tile not found"); - } - auto edge = tile->directededge(tgt_edge_id); - if (edge == nullptr) { - throw std::runtime_error("Target edge not found"); - } - - if (tile->GetSpeed(edge, 255, 1) != tgt_speed) { - fprintf(stderr, "ERROR: tgt_speed: %i, GetSpeed(...): %i\n", tgt_speed, - tile->GetSpeed(edge, 255, 1)); - throw std::runtime_error("Target edge was not at target speed"); - } - - for (auto _ : state) { - tile->GetSpeed(edge, 255, 1); - } -} - -BENCHMARK(BM_GetSpeed)->Unit(benchmark::kNanosecond); - -/** Benchmarks the Allowed function */ -static void BM_Sif_Allowed(benchmark::State& state) { - - const auto config = build_config("sif-allowed.tar"); - auto tgt_edge_id = baldr::GraphId(3196, 0, 3221); - auto tgt_speed = 100; - customize_traffic(config, tgt_edge_id, tgt_speed); - - auto clean_reader = test::make_clean_graphreader(config.get_child("mjolnir")); - - Options options; - create_costing_options(options); - sif::TravelMode mode; - auto costs = sif::CostFactory().CreateModeCosting(options, mode); - auto cost = costs[static_cast(mode)]; - - auto tile = clean_reader->GetGraphTile(baldr::GraphId(tgt_edge_id)); - if (tile == nullptr) { - throw std::runtime_error("Target tile not found"); - } - auto edge = tile->directededge(tgt_edge_id); - if (edge == nullptr) { - throw std::runtime_error("Target edge not found"); - } - - if (tile->GetSpeed(edge, 255, 1) != tgt_speed) { - throw std::runtime_error("Target edge was not at target speed"); - } - - // Mock a phony predecessor - // auto pred = sif::EdgeLabel(0, tgt_edge_id, edge, costs, 1.0, 1.0, - // sif::TravelMode::kDrive,10,sif::Cost()); - auto pred = sif::EdgeLabel(); - uint8_t restriction_idx; - - for (auto _ : state) { - cost->Allowed(edge, false, pred, tile, tgt_edge_id, 0, 0, restriction_idx); - } -} - -BENCHMARK(BM_Sif_Allowed)->Unit(benchmark::kNanosecond); - -} // namespace - -int main(int argc, char** argv) { - - logging::Configure({{"type", ""}}); - - std::string planet_path = ""; - int num_routes = 0; - - for (int i = 0; i < argc; i++) { - if (std::string(argv[i]).find("--planet-path=") != std::string::npos) { - planet_path = std::string(argv[i]).substr(strlen("--planet-path=")); - std::cerr << "Registered planet_path = " << planet_path << std::endl; - } else if (std::string(argv[i]).find("--num-routes=") != std::string::npos) { - num_routes = std::atoi(argv[i] + strlen("--num-routes=")); - if (num_routes == 0) { - std::cerr << "num-routes must be > 0" << std::endl; - return 1; - } else { - std::cerr << "Registered num_routes = " << num_routes << std::endl; - } - } - } - - if (!planet_path.empty() && num_routes > 0) { - ::benchmark::RegisterBenchmark("BM_GlobalFixedRandom", BM_GlobalFixedRandom, - planet_path) - ->Unit(benchmark::kMillisecond) - ->DenseRange(0, num_routes); - ::benchmark::RegisterBenchmark("BM_GlobalFixedRandom", BM_GlobalFixedRandom, - planet_path) - ->Unit(benchmark::kMillisecond) - ->DenseRange(0, num_routes); - ::benchmark::RegisterBenchmark("BM_GlobalFixedRandom", - BM_GlobalFixedRandom, planet_path) - ->Unit(benchmark::kMillisecond) - ->DenseRange(0, num_routes); - } - ::benchmark::Initialize(&argc, argv); - ::benchmark::RunSpecifiedBenchmarks(); -} diff --git a/cmake/FindGEOS.cmake b/cmake/FindGEOS.cmake deleted file mode 100644 index 90ced818c2..0000000000 --- a/cmake/FindGEOS.cmake +++ /dev/null @@ -1,89 +0,0 @@ -#--- -# File: FindGEOS.cmake -# -# Find the native GEOS(Geometry Engine - Open Source) includes and libraries. -# -# This module defines: -# -# GEOS_INCLUDE_DIR, where to find geos.h, etc. -# GEOS_LIBRARY, libraries to link against to use GEOS. Currently there are -# two looked for, geos and geos_c libraries. -# GEOS_FOUND, True if found, false if one of the above are not found. -# -# For ossim, typically geos will be system installed which should be found; -# or found in the ossim 3rd party dependencies directory from a geos build -# and install. If the latter it will rely on CMAKE_INCLUDE_PATH and -# CMAKE_LIBRARY_PATH having the path to the party dependencies directory. -# -# NOTE: -# This script is specialized for ossim, e.g. looking in /usr/local/ossim. -# -# $Id$ -#--- - -#--- -# Find include path: -# Note: Ubuntu 14.04+ did not have geos.h (not included in any ossim src). -# Instead looking for Geometry.h -#--- - -find_path( GEOS_INCLUDE_DIR geos/geom/Geometry.h - PATHS - ${CMAKE_INSTALL_PREFIX}/include - ${GEOS_DIR}/include - /usr/include - /usr/local/include - /usr/local/ossim/include ) - -# Find GEOS library: -find_library( GEOS_LIB NAMES geos - PATHS - ${CMAKE_INSTALL_PREFIX}/lib - ${GEOS_DIR}/lib - /usr/lib64 - /usr/lib - /usr/local/lib - /usr/local/ossim/lib ) - -# Find GEOS C library: -find_library( GEOS_C_LIB NAMES geos_c - PATHS - ${CMAKE_INSTALL_PREFIX}/lib - ${GEOS_DIR}/lib - /usr/lib64 - /usr/lib - /usr/local/lib - /usr/local/ossim/lib ) - -# Set the GEOS_LIBRARY: -if( GEOS_LIB AND GEOS_C_LIB ) - set( GEOS_LIBRARY ${GEOS_LIB} ${GEOS_C_LIB} CACHE STRING INTERNAL ) -endif(GEOS_LIB AND GEOS_C_LIB ) - -#--- -# This function sets GEOS_FOUND if variables are valid. -#--- -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( GEOS DEFAULT_MSG - GEOS_LIBRARY - GEOS_INCLUDE_DIR ) - -add_library(GEOS::GEOS INTERFACE IMPORTED) -if( GEOS_FOUND ) - if( NOT GEOS_FIND_QUIETLY ) - message( STATUS "Found GEOS..." ) - endif( NOT GEOS_FIND_QUIETLY ) - - set_target_properties(GEOS::GEOS PROPERTIES - INTERFACE_LINK_LIBRARIES "${GEOS_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${GEOS_INCLUDE_DIR}") -else( GEOS_FOUND ) - if( NOT GEOS_FIND_QUIETLY ) - message( WARNING "Could not find GEOS" ) - endif( NOT GEOS_FIND_QUIETLY ) -endif( GEOS_FOUND ) - -if( NOT GEOS_FIND_QUIETLY ) - message( STATUS "GEOS_INCLUDE_DIR=${GEOS_INCLUDE_DIR}" ) - message( STATUS "GEOS_LIBRARY=${GEOS_LIBRARY}" ) -endif( NOT GEOS_FIND_QUIETLY ) diff --git a/cmake/FindLuaJIT.cmake b/cmake/FindLuaJIT.cmake deleted file mode 100644 index 2a37871cd1..0000000000 --- a/cmake/FindLuaJIT.cmake +++ /dev/null @@ -1,62 +0,0 @@ -# Locate LuaJIT library -# This module defines -# LUAJIT_FOUND, if false, do not try to link to Lua -# LUA_LIBRARIES -# LUA_INCLUDE_DIR, where to find lua.h -# LUAJIT_VERSION_STRING, the version of Lua found (since CMake 2.8.8) - -## Copied from default CMake FindLua51.cmake - -find_path(LUA_INCLUDE_DIR luajit.h - HINTS - ENV LUA_DIR - PATH_SUFFIXES include/luajit-2.0 include/luajit-2.1 include/luajit include - PATHS - ~/Library/Frameworks - /Library/Frameworks - /sw # Fink - /opt/local # DarwinPorts - /opt/csw # Blastwave - /opt -) - -find_library(LUA_LIBRARY - NAMES luajit-5.1 lua51 - HINTS - ENV LUA_DIR - PATH_SUFFIXES lib - PATHS - ~/Library/Frameworks - /Library/Frameworks - /sw - /opt/local - /opt/csw - /opt -) - -if(LUA_LIBRARY) - # include the math library for Unix - if(UNIX AND NOT APPLE) - find_library(LUA_MATH_LIBRARY m) - set( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") - # For Windows and Mac, don't need to explicitly include the math library - else() - set( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") - endif() -endif() - -if(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/luajit.h") - file(STRINGS "${LUA_INCLUDE_DIR}/luajit.h" luajit_version_str REGEX "^#define[ \t]+LUAJIT_VERSION[ \t]+\"LuaJIT .+\"") - - string(REGEX REPLACE "^#define[ \t]+LUAJIT_VERSION[ \t]+\"LuaJIT ([^\"]+)\".*" "\\1" LUAJIT_VERSION_STRING "${luajit_version_str}") - unset(luajit_version_str) -endif() - -include(FindPackageHandleStandardArgs) -# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if -# all listed variables are TRUE -FIND_PACKAGE_HANDLE_STANDARD_ARGS(LuaJIT - REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR - VERSION_VAR LUAJIT_VERSION_STRING) - -mark_as_advanced(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY) diff --git a/cmake/FindSpatiaLite.cmake b/cmake/FindSpatiaLite.cmake deleted file mode 100644 index 7ad4ed3399..0000000000 --- a/cmake/FindSpatiaLite.cmake +++ /dev/null @@ -1,88 +0,0 @@ -# Find SpatiaLite -# ~~~~~~~~~~~~~~~ -# Copyright (c) 2009, Sandro Furieri -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# -# CMake module to search for SpatiaLite library -# -# If it's found it sets SPATIALITE_FOUND to TRUE -# and following variables are set: -# SPATIALITE_INCLUDE_DIR -# SPATIALITE_LIBRARY - -# This macro checks if the symbol exists -include(CheckLibraryExists) - - -# FIND_PATH and FIND_LIBRARY normally search standard locations -# before the specified paths. To search non-standard paths first, -# FIND_* is invoked first with specified paths and NO_DEFAULT_PATH -# and then again with no specified paths to search the default -# locations. When an earlier FIND_* succeeds, subsequent FIND_*s -# searching for the same item do nothing. - -# try to use sqlite framework on mac -# want clean framework path, not unix compatibility path -IF (APPLE) - IF (CMAKE_FIND_FRAMEWORK MATCHES "FIRST" - OR CMAKE_FRAMEWORK_PATH MATCHES "ONLY" - OR NOT CMAKE_FIND_FRAMEWORK) - SET (CMAKE_FIND_FRAMEWORK_save ${CMAKE_FIND_FRAMEWORK} CACHE STRING "" FORCE) - SET (CMAKE_FIND_FRAMEWORK "ONLY" CACHE STRING "" FORCE) - FIND_PATH(SPATIALITE_INCLUDE_DIR SQLite3/spatialite.h) - # if no SpatiaLite header, we don't want SQLite find below to succeed - IF (SPATIALITE_INCLUDE_DIR) - FIND_LIBRARY(SPATIALITE_LIBRARY SQLite3) - # FIND_PATH doesn't add "Headers" for a framework - SET (SPATIALITE_INCLUDE_DIR ${SPATIALITE_LIBRARY}/Headers CACHE PATH "Path to a file." FORCE) - ENDIF (SPATIALITE_INCLUDE_DIR) - SET (CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK_save} CACHE STRING "" FORCE) - ENDIF () -ENDIF (APPLE) - -FIND_PATH(SPATIALITE_INCLUDE_DIR spatialite.h - /usr/include - "$ENV{INCLUDE}" - "$ENV{LIB_DIR}/include" - "$ENV{LIB_DIR}/include/spatialite" - ) - -FIND_LIBRARY(SPATIALITE_LIBRARY NAMES spatialite_i spatialite PATHS - /usr/lib - $ENV{LIB} - $ENV{LIB_DIR}/lib - ) - -# Handle the QUIETLY and REQUIRED arguments and set SQLITE3_FOUND to TRUE -# if all listed variables are TRUE -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(SpatiaLite - DEFAULT_MSG - SPATIALITE_LIBRARY - SPATIALITE_INCLUDE_DIR) - -mark_as_advanced(SPATIALITE_LIBRARY SPATIALITE_INCLUDE_DIR SPATIALITE_LIBRARY) - -IF (SPATIALITE_FOUND) - set(SPATIALITE_LIBRARIES ${SPATIALITE_LIBRARY}) - - IF(APPLE) - # no extra LDFLAGS used in link test, may fail in OS X SDK - SET(CMAKE_REQUIRED_LIBRARIES "-F/Library/Frameworks" ${CMAKE_REQUIRED_LIBRARIES}) - ENDIF(APPLE) - - check_library_exists("${SPATIALITE_LIBRARY}" gaiaStatisticsInvalidate "" SPATIALITE_VERSION_GE_4_2_0) - IF (NOT SPATIALITE_VERSION_GE_4_2_0) - MESSAGE(FATAL_ERROR "Found SpatiaLite, but version is too old. Requires at least version 4.2.0") - ENDIF (NOT SPATIALITE_VERSION_GE_4_2_0) - - if(NOT TARGET SpatiaLite::SpatiaLite) - add_library(SpatiaLite::SpatiaLite INTERFACE IMPORTED) - - set_target_properties(SpatiaLite::SpatiaLite - PROPERTIES - INTERFACE_LINK_LIBRARIES "${SPATIALITE_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${SPATIALITE_INCLUDE_DIR}") - endif() -ENDIF (SPATIALITE_FOUND) diff --git a/cmake/PkgConfig.cmake b/cmake/PkgConfig.cmake deleted file mode 100644 index 0bf007b5af..0000000000 --- a/cmake/PkgConfig.cmake +++ /dev/null @@ -1,3 +0,0 @@ -string(REGEX REPLACE " [^ ]*valhalla[^ ]+" "" deplibs "${deplibs}") -string(REGEX REPLACE " [^ ]*lib([^/ ]+).so" " -l\\1" deplibs "${deplibs}") -configure_file(${INPUT} ${OUTPUT} @ONLY) diff --git a/cmake/Binary2Header.cmake b/cmake/ValhallaBin2Header.cmake similarity index 98% rename from cmake/Binary2Header.cmake rename to cmake/ValhallaBin2Header.cmake index 1e59e96568..c380208b6e 100644 --- a/cmake/Binary2Header.cmake +++ b/cmake/ValhallaBin2Header.cmake @@ -127,7 +127,7 @@ function(BIN2H) endif() endfunction() -if(CMAKE_SCRIPT_MODE_FILE AND "${CMAKE_SCRIPT_MODE_FILE}" MATCHES "Binary2Header.cmake$") +if(CMAKE_SCRIPT_MODE_FILE AND "${CMAKE_SCRIPT_MODE_FILE}" MATCHES "ValhallaBin2Header.cmake$") # Parse command line argmuents set(ARG_NUM 1) set(conversion_type "HEADER") @@ -136,7 +136,7 @@ if(CMAKE_SCRIPT_MODE_FILE AND "${CMAKE_SCRIPT_MODE_FILE}" MATCHES "Binary2Header set(CURRENT_ARG ${CMAKE_ARGV${ARG_NUM}}) if(${CURRENT_ARG} MATCHES "^--usage$") message("Usage: - cmake -P cmake/Binary2Header.cmake [options] infile outfile + cmake -P cmake/ValhallaBin2Header.cmake [options] infile outfile Options: --header convert to a header file --locales convert locales json files to a header file diff --git a/cmake/ValhallaPkgConfig.cmake b/cmake/ValhallaPkgConfig.cmake new file mode 100644 index 0000000000..a0483c801a --- /dev/null +++ b/cmake/ValhallaPkgConfig.cmake @@ -0,0 +1,54 @@ +function(set_variable_from_rel_or_absolute_path var root rel_or_abs_path) + if(IS_ABSOLUTE "${rel_or_abs_path}") + set(${var} "${rel_or_abs_path}" PARENT_SCOPE) + else() + set(${var} "${root}/${rel_or_abs_path}" PARENT_SCOPE) + endif() +endfunction() + +# configure a pkg-config file libvalhalla.pc +function(configure_valhalla_pc) + set(prefix ${CMAKE_INSTALL_PREFIX}) + set(exec_prefix ${prefix}) + set_variable_from_rel_or_absolute_path("libdir" "$\{prefix\}" "${CMAKE_INSTALL_LIBDIR}") + set_variable_from_rel_or_absolute_path("includedir" "$\{prefix\}" "${CMAKE_INSTALL_INCLUDEDIR}") + # Build strings of dependencies + set(LIBS "") + set(REQUIRES "zlib") + set(LIBS_PRIVATE "${CMAKE_THREAD_LIBS_INIT}") + set(CFLAGS "-I$\{includedir\}/valhalla/third_party") + + if(TARGET protobuf::libprotobuf-lite) + list(APPEND REQUIRES protobuf-lite) + else() + list(APPEND REQUIRES protobuf) + endif() + + if(ENABLE_DATA_TOOLS) + list(APPEND REQUIRES spatialite sqlite3 luajit geos) + endif() + if(ENABLE_HTTP OR ENABLE_PYTHON_BINDINGS) + list(APPEND REQUIRES libcurl) + endif() + if(ENABLE_SERVICES) + list(APPEND REQUIRES libprime_server) + endif() + if(ENABLE_GDAL) + list(APPEND REQUIRES gdal) + endif() + if(WIN32 AND NOT MINGW) + list(APPEND LIBS_PRIVATE -lole32 -lshell32) + else() + if(NOT "-lm" IN_LIST LIBS_PRIVATE) + list(APPEND LIBS_PRIVATE -lm) + endif() + endif() + list(JOIN LIBS " " LIBS) + list(JOIN REQUIRES " " REQUIRES) + list(JOIN LIBS_PRIVATE " " LIBS_PRIVATE) + + configure_file( + ${CMAKE_SOURCE_DIR}/libvalhalla.pc.in + ${CMAKE_BINARY_DIR}/libvalhalla.pc + @ONLY) +endfunction() diff --git a/cmake/SanitizerOptions.cmake b/cmake/ValhallaSanitizerOptions.cmake similarity index 75% rename from cmake/SanitizerOptions.cmake rename to cmake/ValhallaSanitizerOptions.cmake index 794ead973a..a0d9052762 100644 --- a/cmake/SanitizerOptions.cmake +++ b/cmake/ValhallaSanitizerOptions.cmake @@ -3,7 +3,7 @@ if (ENABLE_SANITIZERS) set(ENABLE_UNDEFINED_SANITIZER ON) endif() -# Inlclude build macros for updating configuration variables +# Include build macros for updating configuration variables include(HandleLibcxxFlags) set (SANITIZER_FLAGS_LIST "") @@ -27,10 +27,10 @@ if(ENABLE_UNDEFINED_SANITIZER) append_flags_if_supported(SANITIZER_SHARED_LINKER_FLAGS_LIST -fsanitize=undefined -fno-sanitize=vptr) endif() -string(REPLACE ";" " " SANITIZER_FLAGS_STR "${SANITIZER_FLAGS_LIST}") -string(REPLACE ";" " " SANITIZER_EXE_LINKER_FLAGS_STR "${SANITIZER_EXE_LINKER_FLAGS_LIST}") -string(REPLACE ";" " " SANITIZER_SHARED_LINKER_FLAGS_STR "${SANITIZER_SHARED_LINKER_FLAGS_LIST}") +list(JOIN SANITIZER_FLAGS_LIST " " SANITIZER_FLAGS_LIST) +list(JOIN SANITIZER_EXE_LINKER_FLAGS_LIST " " SANITIZER_EXE_LINKER_FLAGS_LIST) +list(JOIN SANITIZER_SHARED_LINKER_FLAGS_LIST " " SANITIZER_SHARED_LINKER_FLAGS_LIST) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS_STR}") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_EXE_LINKER_FLAGS_STR}") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${SANITIZER_SHARED_LINKER_FLAGS_STR}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS_LIST}") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_EXE_LINKER_FLAGS_LIST}") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${SANITIZER_SHARED_LINKER_FLAGS_LIST}") diff --git a/cmake/ValhallaSourceGroups.cmake b/cmake/ValhallaSourceGroups.cmake new file mode 100644 index 0000000000..c688b15406 --- /dev/null +++ b/cmake/ValhallaSourceGroups.cmake @@ -0,0 +1,25 @@ +# create source groups for IDEs such as Visual Studio & XCode + +function(create_source_groups prefix) + foreach(file ${ARGN}) + get_filename_component(file "${file}" ABSOLUTE) + string(FIND "${file}" "${PROJECT_BINARY_DIR}/" pos) + if(pos EQUAL 0) + source_group(TREE "${PROJECT_BINARY_DIR}/" PREFIX "Generated Files" FILES "${file}") + else() + source_group(TREE "${PROJECT_SOURCE_DIR}/" PREFIX "${prefix}" FILES "${file}") + endif() + endforeach() +endfunction() + +function(get_source_path PATH NAME) + if(EXISTS ${VALHALLA_SOURCE_DIR}/src/${NAME}.cc) + set(${PATH} ${VALHALLA_SOURCE_DIR}/src/${NAME}.cc PARENT_SCOPE) + elseif(EXISTS ${VALHALLA_SOURCE_DIR}/src/meili/${NAME}.cc) + set(${PATH} ${VALHALLA_SOURCE_DIR}/src/meili/${NAME}.cc PARENT_SCOPE) + elseif(EXISTS ${VALHALLA_SOURCE_DIR}/src/mjolnir/${NAME}.cc) + set(${PATH} ${VALHALLA_SOURCE_DIR}/src/mjolnir/${NAME}.cc PARENT_SCOPE) + else() + message(FATAL_ERROR "no source path for ${NAME}") + endif() +endfunction() diff --git a/cmake/ValhallaVersion.cmake b/cmake/ValhallaVersion.cmake new file mode 100644 index 0000000000..b1ddb56087 --- /dev/null +++ b/cmake/ValhallaVersion.cmake @@ -0,0 +1,19 @@ +## Get Valhalla version +file(STRINGS "${VALHALLA_SOURCE_DIR}/valhalla/valhalla.h" version_lines REGEX "VALHALLA_VERSION_(MAJOR|MINOR|PATCH)") +foreach(line ${version_lines}) + if("${line}" MATCHES "(VALHALLA_VERSION_(MAJOR|MINOR|PATCH))[\t ]+([0-9]+)") + set(${CMAKE_MATCH_1} ${CMAKE_MATCH_3}) + set(${CMAKE_MATCH_1} ${CMAKE_MATCH_3} PARENT_SCOPE) + endif() +endforeach() +if(DEFINED VALHALLA_VERSION_MAJOR) + set(VERSION "${VALHALLA_VERSION_MAJOR}") + if(DEFINED VALHALLA_VERSION_MINOR) + set(VERSION "${VERSION}.${VALHALLA_VERSION_MINOR}") + if(DEFINED VALHALLA_VERSION_PATCH) + set(VERSION "${VERSION}.${VALHALLA_VERSION_PATCH}") + endif() + endif() +else() + message(FATAL_ERROR "No Valhalla major version") +endif() diff --git a/cmake/ValhallaWarnings.cmake b/cmake/ValhallaWarnings.cmake new file mode 100644 index 0000000000..30c2577e81 --- /dev/null +++ b/cmake/ValhallaWarnings.cmake @@ -0,0 +1,18 @@ +## Declare C++ build configuration variables as part of HandleLibcxxFlags. +# +# - LIBCXX_COMPILE_FLAGS: flags used to compile libc++ +# - LIBCXX_LINK_FLAGS: flags used to link libc++ +# - LIBCXX_LIBRARIES: libraries to link libc++ to +set(LIBCXX_COMPILE_FLAGS "") +set(LIBCXX_LINK_FLAGS "") +set(LIBCXX_LIBRARIES "") + +# Include build macros for updating configuration variables +include(HandleLibcxxFlags) + +function (cxx_add_warning_flags target) + target_add_compile_flags_if_supported(${target} PRIVATE -Wall -Wextra) + if (ENABLE_WERROR) + target_add_compile_flags_if_supported(${target} PRIVATE -Werror) + endif() +endfunction() diff --git a/cmake/conan.cmake b/cmake/conan.cmake deleted file mode 100644 index 7254847363..0000000000 --- a/cmake/conan.cmake +++ /dev/null @@ -1,909 +0,0 @@ -# The MIT License (MIT) - -# Copyright (c) 2018 JFrog - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - - - -# This file comes from: https://github.com/conan-io/cmake-conan. Please refer -# to this repository for issues and documentation. - -# Its purpose is to wrap and launch Conan C/C++ Package Manager when cmake is called. -# It will take CMake current settings (os, compiler, compiler version, architecture) -# and translate them to conan settings for installing and retrieving dependencies. - -# It is intended to facilitate developers building projects that have conan dependencies, -# but it is only necessary on the end-user side. It is not necessary to create conan -# packages, in fact it shouldn't be use for that. Check the project documentation. - -# version: 0.17.0 - -include(CMakeParseArguments) - -function(_get_msvc_ide_version result) - set(${result} "" PARENT_SCOPE) - if(NOT MSVC_VERSION VERSION_LESS 1400 AND MSVC_VERSION VERSION_LESS 1500) - set(${result} 8 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1500 AND MSVC_VERSION VERSION_LESS 1600) - set(${result} 9 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1600 AND MSVC_VERSION VERSION_LESS 1700) - set(${result} 10 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1700 AND MSVC_VERSION VERSION_LESS 1800) - set(${result} 11 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1800 AND MSVC_VERSION VERSION_LESS 1900) - set(${result} 12 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1900 AND MSVC_VERSION VERSION_LESS 1910) - set(${result} 14 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1910 AND MSVC_VERSION VERSION_LESS 1920) - set(${result} 15 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1920 AND MSVC_VERSION VERSION_LESS 1930) - set(${result} 16 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1930 AND MSVC_VERSION VERSION_LESS 1940) - set(${result} 17 PARENT_SCOPE) - else() - message(FATAL_ERROR "Conan: Unknown MSVC compiler version [${MSVC_VERSION}]") - endif() -endfunction() - -macro(_conan_detect_build_type) - conan_parse_arguments(${ARGV}) - - if(ARGUMENTS_BUILD_TYPE) - set(_CONAN_SETTING_BUILD_TYPE ${ARGUMENTS_BUILD_TYPE}) - elseif(CMAKE_BUILD_TYPE) - set(_CONAN_SETTING_BUILD_TYPE ${CMAKE_BUILD_TYPE}) - else() - message(FATAL_ERROR "Please specify in command line CMAKE_BUILD_TYPE (-DCMAKE_BUILD_TYPE=Release)") - endif() - - string(TOUPPER ${_CONAN_SETTING_BUILD_TYPE} _CONAN_SETTING_BUILD_TYPE_UPPER) - if (_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "DEBUG") - set(_CONAN_SETTING_BUILD_TYPE "Debug") - elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "RELEASE") - set(_CONAN_SETTING_BUILD_TYPE "Release") - elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "RELWITHDEBINFO") - set(_CONAN_SETTING_BUILD_TYPE "RelWithDebInfo") - elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "MINSIZEREL") - set(_CONAN_SETTING_BUILD_TYPE "MinSizeRel") - endif() -endmacro() - -macro(_conan_check_system_name) - #handle -s os setting - if(CMAKE_SYSTEM_NAME AND NOT CMAKE_SYSTEM_NAME STREQUAL "Generic") - #use default conan os setting if CMAKE_SYSTEM_NAME is not defined - set(CONAN_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}) - if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") - set(CONAN_SYSTEM_NAME Macos) - endif() - if(${CMAKE_SYSTEM_NAME} STREQUAL "QNX") - set(CONAN_SYSTEM_NAME Neutrino) - endif() - set(CONAN_SUPPORTED_PLATFORMS Windows Linux Macos Android iOS FreeBSD WindowsStore WindowsCE watchOS tvOS FreeBSD SunOS AIX Arduino Emscripten Neutrino) - list (FIND CONAN_SUPPORTED_PLATFORMS "${CONAN_SYSTEM_NAME}" _index) - if (${_index} GREATER -1) - #check if the cmake system is a conan supported one - set(_CONAN_SETTING_OS ${CONAN_SYSTEM_NAME}) - else() - message(FATAL_ERROR "cmake system ${CONAN_SYSTEM_NAME} is not supported by conan. Use one of ${CONAN_SUPPORTED_PLATFORMS}") - endif() - endif() -endmacro() - -macro(_conan_check_language) - get_property(_languages GLOBAL PROPERTY ENABLED_LANGUAGES) - if (";${_languages};" MATCHES ";CXX;") - set(LANGUAGE CXX) - set(USING_CXX 1) - elseif (";${_languages};" MATCHES ";C;") - set(LANGUAGE C) - set(USING_CXX 0) - else () - message(FATAL_ERROR "Conan: Neither C or C++ was detected as a language for the project. Unabled to detect compiler version.") - endif() -endmacro() - -macro(_conan_detect_compiler) - - conan_parse_arguments(${ARGV}) - - if(ARGUMENTS_ARCH) - set(_CONAN_SETTING_ARCH ${ARGUMENTS_ARCH}) - endif() - - if(USING_CXX) - set(_CONAN_SETTING_COMPILER_CPPSTD ${CMAKE_CXX_STANDARD}) - endif() - - if (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL GNU) - # using GCC - # TODO: Handle other params - string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) - list(GET VERSION_LIST 0 MAJOR) - list(GET VERSION_LIST 1 MINOR) - set(COMPILER_VERSION ${MAJOR}.${MINOR}) - if(${MAJOR} GREATER 4) - set(COMPILER_VERSION ${MAJOR}) - endif() - set(_CONAN_SETTING_COMPILER gcc) - set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION}) - if (USING_CXX) - conan_cmake_detect_unix_libcxx(_LIBCXX) - set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) - endif () - elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL Intel) - string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) - list(GET VERSION_LIST 0 MAJOR) - list(GET VERSION_LIST 1 MINOR) - set(COMPILER_VERSION ${MAJOR}.${MINOR}) - set(_CONAN_SETTING_COMPILER intel) - set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION}) - if (USING_CXX) - conan_cmake_detect_unix_libcxx(_LIBCXX) - set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) - endif () - elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL AppleClang) - # using AppleClang - string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) - list(GET VERSION_LIST 0 MAJOR) - list(GET VERSION_LIST 1 MINOR) - set(_CONAN_SETTING_COMPILER apple-clang) - set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}.${MINOR}) - if (USING_CXX) - conan_cmake_detect_unix_libcxx(_LIBCXX) - set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) - endif () - elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL Clang) - string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) - list(GET VERSION_LIST 0 MAJOR) - list(GET VERSION_LIST 1 MINOR) - set(_CONAN_SETTING_COMPILER clang) - set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}.${MINOR}) - if(APPLE) - cmake_policy(GET CMP0025 APPLE_CLANG_POLICY) - if(NOT APPLE_CLANG_POLICY STREQUAL NEW) - message(STATUS "Conan: APPLE and Clang detected. Assuming apple-clang compiler. Set CMP0025 to avoid it") - set(_CONAN_SETTING_COMPILER apple-clang) - endif() - endif() - if(${_CONAN_SETTING_COMPILER} STREQUAL clang AND ${MAJOR} GREATER 7) - set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}) - endif() - if (USING_CXX) - conan_cmake_detect_unix_libcxx(_LIBCXX) - set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) - endif () - elseif(${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL MSVC) - set(_VISUAL "Visual Studio") - _get_msvc_ide_version(_VISUAL_VERSION) - if("${_VISUAL_VERSION}" STREQUAL "") - message(FATAL_ERROR "Conan: Visual Studio not recognized") - else() - set(_CONAN_SETTING_COMPILER ${_VISUAL}) - set(_CONAN_SETTING_COMPILER_VERSION ${_VISUAL_VERSION}) - endif() - - if(NOT _CONAN_SETTING_ARCH) - if (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "64") - set(_CONAN_SETTING_ARCH x86_64) - elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "^ARM") - message(STATUS "Conan: Using default ARM architecture from MSVC") - set(_CONAN_SETTING_ARCH armv6) - elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "86") - set(_CONAN_SETTING_ARCH x86) - else () - message(FATAL_ERROR "Conan: Unknown MSVC architecture [${MSVC_${LANGUAGE}_ARCHITECTURE_ID}]") - endif() - endif() - - conan_cmake_detect_vs_runtime(_vs_runtime ${ARGV}) - message(STATUS "Conan: Detected VS runtime: ${_vs_runtime}") - set(_CONAN_SETTING_COMPILER_RUNTIME ${_vs_runtime}) - - if (CMAKE_GENERATOR_TOOLSET) - set(_CONAN_SETTING_COMPILER_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET}) - elseif(CMAKE_VS_PLATFORM_TOOLSET AND (CMAKE_GENERATOR STREQUAL "Ninja")) - set(_CONAN_SETTING_COMPILER_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET}) - endif() - else() - message(FATAL_ERROR "Conan: compiler setup not recognized") - endif() - -endmacro() - -function(conan_cmake_settings result) - #message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER}) - #message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER_ID}) - #message(STATUS "VERSION " ${CMAKE_CXX_COMPILER_VERSION}) - #message(STATUS "FLAGS " ${CMAKE_LANG_FLAGS}) - #message(STATUS "LIB ARCH " ${CMAKE_CXX_LIBRARY_ARCHITECTURE}) - #message(STATUS "BUILD TYPE " ${CMAKE_BUILD_TYPE}) - #message(STATUS "GENERATOR " ${CMAKE_GENERATOR}) - #message(STATUS "GENERATOR WIN64 " ${CMAKE_CL_64}) - - message(STATUS "Conan: Automatic detection of conan settings from cmake") - - conan_parse_arguments(${ARGV}) - - _conan_detect_build_type(${ARGV}) - - _conan_check_system_name() - - _conan_check_language() - - _conan_detect_compiler(${ARGV}) - - # If profile is defined it is used - if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND ARGUMENTS_DEBUG_PROFILE) - set(_APPLIED_PROFILES ${ARGUMENTS_DEBUG_PROFILE}) - elseif(CMAKE_BUILD_TYPE STREQUAL "Release" AND ARGUMENTS_RELEASE_PROFILE) - set(_APPLIED_PROFILES ${ARGUMENTS_RELEASE_PROFILE}) - elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" AND ARGUMENTS_RELWITHDEBINFO_PROFILE) - set(_APPLIED_PROFILES ${ARGUMENTS_RELWITHDEBINFO_PROFILE}) - elseif(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" AND ARGUMENTS_MINSIZEREL_PROFILE) - set(_APPLIED_PROFILES ${ARGUMENTS_MINSIZEREL_PROFILE}) - elseif(ARGUMENTS_PROFILE) - set(_APPLIED_PROFILES ${ARGUMENTS_PROFILE}) - endif() - - foreach(ARG ${_APPLIED_PROFILES}) - set(_SETTINGS ${_SETTINGS} -pr=${ARG}) - endforeach() - foreach(ARG ${ARGUMENTS_PROFILE_BUILD}) - conan_check(VERSION 1.24.0 REQUIRED DETECT_QUIET) - set(_SETTINGS ${_SETTINGS} -pr:b=${ARG}) - endforeach() - - if(NOT _SETTINGS OR ARGUMENTS_PROFILE_AUTO STREQUAL "ALL") - set(ARGUMENTS_PROFILE_AUTO arch build_type compiler compiler.version - compiler.runtime compiler.libcxx compiler.toolset) - endif() - - # remove any manually specified settings from the autodetected settings - foreach(ARG ${ARGUMENTS_SETTINGS}) - string(REGEX MATCH "[^=]*" MANUAL_SETTING "${ARG}") - message(STATUS "Conan: ${MANUAL_SETTING} was added as an argument. Not using the autodetected one.") - list(REMOVE_ITEM ARGUMENTS_PROFILE_AUTO "${MANUAL_SETTING}") - endforeach() - - # Automatic from CMake - foreach(ARG ${ARGUMENTS_PROFILE_AUTO}) - string(TOUPPER ${ARG} _arg_name) - string(REPLACE "." "_" _arg_name ${_arg_name}) - if(_CONAN_SETTING_${_arg_name}) - set(_SETTINGS ${_SETTINGS} -s ${ARG}=${_CONAN_SETTING_${_arg_name}}) - endif() - endforeach() - - foreach(ARG ${ARGUMENTS_SETTINGS}) - set(_SETTINGS ${_SETTINGS} -s ${ARG}) - endforeach() - - message(STATUS "Conan: Settings= ${_SETTINGS}") - - set(${result} ${_SETTINGS} PARENT_SCOPE) -endfunction() - - -function(conan_cmake_detect_unix_libcxx result) - # Take into account any -stdlib in compile options - get_directory_property(compile_options DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_OPTIONS) - string(GENEX_STRIP "${compile_options}" compile_options) - - # Take into account any _GLIBCXX_USE_CXX11_ABI in compile definitions - get_directory_property(defines DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_DEFINITIONS) - string(GENEX_STRIP "${defines}" defines) - - foreach(define ${defines}) - if(define MATCHES "_GLIBCXX_USE_CXX11_ABI") - if(define MATCHES "^-D") - set(compile_options ${compile_options} "${define}") - else() - set(compile_options ${compile_options} "-D${define}") - endif() - endif() - endforeach() - - # add additional compiler options ala cmRulePlaceholderExpander::ExpandRuleVariable - set(EXPAND_CXX_COMPILER ${CMAKE_CXX_COMPILER}) - if(CMAKE_CXX_COMPILER_ARG1) - # CMake splits CXX="foo bar baz" into CMAKE_CXX_COMPILER="foo", CMAKE_CXX_COMPILER_ARG1="bar baz" - # without this, ccache, winegcc, or other wrappers might lose all their arguments - separate_arguments(SPLIT_CXX_COMPILER_ARG1 NATIVE_COMMAND ${CMAKE_CXX_COMPILER_ARG1}) - list(APPEND EXPAND_CXX_COMPILER ${SPLIT_CXX_COMPILER_ARG1}) - endif() - - if(CMAKE_CXX_COMPILE_OPTIONS_TARGET AND CMAKE_CXX_COMPILER_TARGET) - # without --target= we may be calling the wrong underlying GCC - list(APPEND EXPAND_CXX_COMPILER "${CMAKE_CXX_COMPILE_OPTIONS_TARGET}${CMAKE_CXX_COMPILER_TARGET}") - endif() - - if(CMAKE_CXX_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN AND CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN) - list(APPEND EXPAND_CXX_COMPILER "${CMAKE_CXX_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN}${CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN}") - endif() - - if(CMAKE_CXX_COMPILE_OPTIONS_SYSROOT) - # without --sysroot= we may find the wrong #include - if(CMAKE_SYSROOT_COMPILE) - list(APPEND EXPAND_CXX_COMPILER "${CMAKE_CXX_COMPILE_OPTIONS_SYSROOT}${CMAKE_SYSROOT_COMPILE}") - elseif(CMAKE_SYSROOT) - list(APPEND EXPAND_CXX_COMPILER "${CMAKE_CXX_COMPILE_OPTIONS_SYSROOT}${CMAKE_SYSROOT}") - endif() - endif() - - separate_arguments(SPLIT_CXX_FLAGS NATIVE_COMMAND ${CMAKE_CXX_FLAGS}) - - if(CMAKE_OSX_SYSROOT) - set(xcode_sysroot_option "--sysroot=${CMAKE_OSX_SYSROOT}") - endif() - - execute_process( - COMMAND ${CMAKE_COMMAND} -E echo "#include " - COMMAND ${EXPAND_CXX_COMPILER} ${SPLIT_CXX_FLAGS} -x c++ ${xcode_sysroot_option} ${compile_options} -E -dM - - OUTPUT_VARIABLE string_defines - ) - - if(string_defines MATCHES "#define __GLIBCXX__") - # Allow -D_GLIBCXX_USE_CXX11_ABI=ON/OFF as argument to cmake - if(DEFINED _GLIBCXX_USE_CXX11_ABI) - if(_GLIBCXX_USE_CXX11_ABI) - set(${result} libstdc++11 PARENT_SCOPE) - return() - else() - set(${result} libstdc++ PARENT_SCOPE) - return() - endif() - endif() - - if(string_defines MATCHES "#define _GLIBCXX_USE_CXX11_ABI 1\n") - set(${result} libstdc++11 PARENT_SCOPE) - else() - # Either the compiler is missing the define because it is old, and so - # it can't use the new abi, or the compiler was configured to use the - # old abi by the user or distro (e.g. devtoolset on RHEL/CentOS) - set(${result} libstdc++ PARENT_SCOPE) - endif() - else() - set(${result} libc++ PARENT_SCOPE) - endif() -endfunction() - -function(conan_cmake_detect_vs_runtime result) - - conan_parse_arguments(${ARGV}) - if(ARGUMENTS_BUILD_TYPE) - set(build_type "${ARGUMENTS_BUILD_TYPE}") - elseif(CMAKE_BUILD_TYPE) - set(build_type "${CMAKE_BUILD_TYPE}") - else() - message(FATAL_ERROR "Please specify in command line CMAKE_BUILD_TYPE (-DCMAKE_BUILD_TYPE=Release)") - endif() - - if(build_type) - string(TOUPPER "${build_type}" build_type) - endif() - set(variables CMAKE_CXX_FLAGS_${build_type} CMAKE_C_FLAGS_${build_type} CMAKE_CXX_FLAGS CMAKE_C_FLAGS) - foreach(variable ${variables}) - if(NOT "${${variable}}" STREQUAL "") - string(REPLACE " " ";" flags "${${variable}}") - foreach (flag ${flags}) - if("${flag}" STREQUAL "/MD" OR "${flag}" STREQUAL "/MDd" OR "${flag}" STREQUAL "/MT" OR "${flag}" STREQUAL "/MTd") - string(SUBSTRING "${flag}" 1 -1 runtime) - set(${result} "${runtime}" PARENT_SCOPE) - return() - endif() - endforeach() - endif() - endforeach() - if("${build_type}" STREQUAL "DEBUG") - set(${result} "MDd" PARENT_SCOPE) - else() - set(${result} "MD" PARENT_SCOPE) - endif() -endfunction() - -function(_collect_settings result) - set(ARGUMENTS_PROFILE_AUTO arch build_type compiler compiler.version - compiler.runtime compiler.libcxx compiler.toolset - compiler.cppstd) - foreach(ARG ${ARGUMENTS_PROFILE_AUTO}) - string(TOUPPER ${ARG} _arg_name) - string(REPLACE "." "_" _arg_name ${_arg_name}) - if(_CONAN_SETTING_${_arg_name}) - set(detected_setings ${detected_setings} ${ARG}=${_CONAN_SETTING_${_arg_name}}) - endif() - endforeach() - set(${result} ${detected_setings} PARENT_SCOPE) -endfunction() - -function(conan_cmake_autodetect detected_settings) - _conan_detect_build_type(${ARGV}) - _conan_check_system_name() - _conan_check_language() - _conan_detect_compiler(${ARGV}) - _collect_settings(collected_settings) - set(${detected_settings} ${collected_settings} PARENT_SCOPE) -endfunction() - -macro(conan_parse_arguments) - set(options BASIC_SETUP CMAKE_TARGETS UPDATE KEEP_RPATHS NO_LOAD NO_OUTPUT_DIRS OUTPUT_QUIET NO_IMPORTS SKIP_STD) - set(oneValueArgs CONANFILE ARCH BUILD_TYPE INSTALL_FOLDER CONAN_COMMAND) - set(multiValueArgs DEBUG_PROFILE RELEASE_PROFILE RELWITHDEBINFO_PROFILE MINSIZEREL_PROFILE - PROFILE REQUIRES OPTIONS IMPORTS SETTINGS BUILD ENV GENERATORS PROFILE_AUTO - INSTALL_ARGS CONFIGURATION_TYPES PROFILE_BUILD BUILD_REQUIRES) - cmake_parse_arguments(ARGUMENTS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) -endmacro() - -function(old_conan_cmake_install) - # Calls "conan install" - # Argument BUILD is equivalant to --build={missing, PkgName,...} or - # --build when argument is 'BUILD all' (which builds all packages from source) - # Argument CONAN_COMMAND, to specify the conan path, e.g. in case of running from source - # cmake does not identify conan as command, even if it is +x and it is in the path - conan_parse_arguments(${ARGV}) - - if(CONAN_CMAKE_MULTI) - set(ARGUMENTS_GENERATORS ${ARGUMENTS_GENERATORS} cmake_multi) - else() - set(ARGUMENTS_GENERATORS ${ARGUMENTS_GENERATORS} cmake) - endif() - - set(CONAN_BUILD_POLICY "") - foreach(ARG ${ARGUMENTS_BUILD}) - if(${ARG} STREQUAL "all") - set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build) - break() - else() - set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build=${ARG}) - endif() - endforeach() - if(ARGUMENTS_CONAN_COMMAND) - set(CONAN_CMD ${ARGUMENTS_CONAN_COMMAND}) - else() - conan_check(REQUIRED) - endif() - set(CONAN_OPTIONS "") - if(ARGUMENTS_CONANFILE) - if(IS_ABSOLUTE ${ARGUMENTS_CONANFILE}) - set(CONANFILE ${ARGUMENTS_CONANFILE}) - else() - set(CONANFILE ${CMAKE_CURRENT_SOURCE_DIR}/${ARGUMENTS_CONANFILE}) - endif() - else() - set(CONANFILE ".") - endif() - foreach(ARG ${ARGUMENTS_OPTIONS}) - set(CONAN_OPTIONS ${CONAN_OPTIONS} -o=${ARG}) - endforeach() - if(ARGUMENTS_UPDATE) - set(CONAN_INSTALL_UPDATE --update) - endif() - if(ARGUMENTS_NO_IMPORTS) - set(CONAN_INSTALL_NO_IMPORTS --no-imports) - endif() - set(CONAN_INSTALL_FOLDER "") - if(ARGUMENTS_INSTALL_FOLDER) - set(CONAN_INSTALL_FOLDER -if=${ARGUMENTS_INSTALL_FOLDER}) - endif() - foreach(ARG ${ARGUMENTS_GENERATORS}) - set(CONAN_GENERATORS ${CONAN_GENERATORS} -g=${ARG}) - endforeach() - foreach(ARG ${ARGUMENTS_ENV}) - set(CONAN_ENV_VARS ${CONAN_ENV_VARS} -e=${ARG}) - endforeach() - set(conan_args install ${CONANFILE} ${settings} ${CONAN_ENV_VARS} ${CONAN_GENERATORS} ${CONAN_BUILD_POLICY} ${CONAN_INSTALL_UPDATE} ${CONAN_INSTALL_NO_IMPORTS} ${CONAN_OPTIONS} ${CONAN_INSTALL_FOLDER} ${ARGUMENTS_INSTALL_ARGS}) - - string (REPLACE ";" " " _conan_args "${conan_args}") - message(STATUS "Conan executing: ${CONAN_CMD} ${_conan_args}") - - if(ARGUMENTS_OUTPUT_QUIET) - execute_process(COMMAND ${CONAN_CMD} ${conan_args} - RESULT_VARIABLE return_code - OUTPUT_VARIABLE conan_output - ERROR_VARIABLE conan_output - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - else() - execute_process(COMMAND ${CONAN_CMD} ${conan_args} - RESULT_VARIABLE return_code - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - endif() - - if(NOT "${return_code}" STREQUAL "0") - message(FATAL_ERROR "Conan install failed='${return_code}'") - endif() - -endfunction() - -function(conan_cmake_install) - if(DEFINED CONAN_COMMAND) - set(CONAN_CMD ${CONAN_COMMAND}) - else() - conan_check(REQUIRED) - endif() - - set(installOptions UPDATE NO_IMPORTS OUTPUT_QUIET ERROR_QUIET) - set(installOneValueArgs PATH_OR_REFERENCE REFERENCE REMOTE LOCKFILE LOCKFILE_OUT LOCKFILE_NODE_ID INSTALL_FOLDER) - set(installMultiValueArgs GENERATOR BUILD ENV ENV_HOST ENV_BUILD OPTIONS_HOST OPTIONS OPTIONS_BUILD PROFILE - PROFILE_HOST PROFILE_BUILD SETTINGS SETTINGS_HOST SETTINGS_BUILD) - cmake_parse_arguments(ARGS "${installOptions}" "${installOneValueArgs}" "${installMultiValueArgs}" ${ARGN}) - foreach(arg ${installOptions}) - if(ARGS_${arg}) - set(${arg} ${${arg}} ${ARGS_${arg}}) - endif() - endforeach() - foreach(arg ${installOneValueArgs}) - if(DEFINED ARGS_${arg}) - if("${arg}" STREQUAL "REMOTE") - set(flag "--remote") - elseif("${arg}" STREQUAL "LOCKFILE") - set(flag "--lockfile") - elseif("${arg}" STREQUAL "LOCKFILE_OUT") - set(flag "--lockfile-out") - elseif("${arg}" STREQUAL "LOCKFILE_NODE_ID") - set(flag "--lockfile-node-id") - elseif("${arg}" STREQUAL "INSTALL_FOLDER") - set(flag "--install-folder") - endif() - set(${arg} ${${arg}} ${flag} ${ARGS_${arg}}) - endif() - endforeach() - foreach(arg ${installMultiValueArgs}) - if(DEFINED ARGS_${arg}) - if("${arg}" STREQUAL "GENERATOR") - set(flag "--generator") - elseif("${arg}" STREQUAL "BUILD") - set(flag "--build") - elseif("${arg}" STREQUAL "ENV") - set(flag "--env") - elseif("${arg}" STREQUAL "ENV_HOST") - set(flag "--env:host") - elseif("${arg}" STREQUAL "ENV_BUILD") - set(flag "--env:build") - elseif("${arg}" STREQUAL "OPTIONS") - set(flag "--options") - elseif("${arg}" STREQUAL "OPTIONS_HOST") - set(flag "--options:host") - elseif("${arg}" STREQUAL "OPTIONS_BUILD") - set(flag "--options:build") - elseif("${arg}" STREQUAL "PROFILE") - set(flag "--profile") - elseif("${arg}" STREQUAL "PROFILE_HOST") - set(flag "--profile:host") - elseif("${arg}" STREQUAL "PROFILE_BUILD") - set(flag "--profile:build") - elseif("${arg}" STREQUAL "SETTINGS") - set(flag "--settings") - elseif("${arg}" STREQUAL "SETTINGS_HOST") - set(flag "--settings:host") - elseif("${arg}" STREQUAL "SETTINGS_BUILD") - set(flag "--settings:build") - endif() - list(LENGTH ARGS_${arg} numargs) - foreach(item ${ARGS_${arg}}) - if(${item} STREQUAL "all" AND ${arg} STREQUAL "BUILD") - set(${arg} "--build") - break() - endif() - set(${arg} ${${arg}} ${flag} ${item}) - endforeach() - endif() - endforeach() - if(DEFINED UPDATE) - set(UPDATE --update) - endif() - if(DEFINED NO_IMPORTS) - set(NO_IMPORTS --no-imports) - endif() - set(install_args install ${PATH_OR_REFERENCE} ${REFERENCE} ${UPDATE} ${NO_IMPORTS} ${REMOTE} ${LOCKFILE} ${LOCKFILE_OUT} ${LOCKFILE_NODE_ID} ${INSTALL_FOLDER} - ${GENERATOR} ${BUILD} ${ENV} ${ENV_HOST} ${ENV_BUILD} ${OPTIONS} ${OPTIONS_HOST} ${OPTIONS_BUILD} - ${PROFILE} ${PROFILE_HOST} ${PROFILE_BUILD} ${SETTINGS} ${SETTINGS_HOST} ${SETTINGS_BUILD}) - - string(REPLACE ";" " " _install_args "${install_args}") - message(STATUS "Conan executing: ${CONAN_CMD} ${_install_args}") - - if(ARGS_OUTPUT_QUIET) - set(OUTPUT_OPT OUTPUT_QUIET) - endif() - if(ARGS_ERROR_QUIET) - set(ERROR_OPT ERROR_QUIET) - endif() - - execute_process(COMMAND ${CONAN_CMD} ${install_args} - RESULT_VARIABLE return_code - ${OUTPUT_OPT} - ${ERROR_OPT} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - - if(NOT "${return_code}" STREQUAL "0") - if (ARGS_ERROR_QUIET) - message(WARNING "Conan install failed='${return_code}'") - else() - message(FATAL_ERROR "Conan install failed='${return_code}'") - endif() - endif() - -endfunction() - -function(conan_cmake_setup_conanfile) - conan_parse_arguments(${ARGV}) - if(ARGUMENTS_CONANFILE) - get_filename_component(_CONANFILE_NAME ${ARGUMENTS_CONANFILE} NAME) - # configure_file will make sure cmake re-runs when conanfile is updated - configure_file(${ARGUMENTS_CONANFILE} ${CMAKE_CURRENT_BINARY_DIR}/${_CONANFILE_NAME}.junk COPYONLY) - file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/${_CONANFILE_NAME}.junk) - else() - conan_cmake_generate_conanfile(ON ${ARGV}) - endif() -endfunction() - -function(conan_cmake_configure) - conan_cmake_generate_conanfile(OFF ${ARGV}) -endfunction() - -# Generate, writing in disk a conanfile.txt with the requires, options, and imports -# specified as arguments -# This will be considered as temporary file, generated in CMAKE_CURRENT_BINARY_DIR) -function(conan_cmake_generate_conanfile DEFAULT_GENERATOR) - - conan_parse_arguments(${ARGV}) - - set(_FN "${CMAKE_CURRENT_BINARY_DIR}/conanfile.txt") - file(WRITE ${_FN} "") - - if(DEFINED ARGUMENTS_REQUIRES) - file(APPEND ${_FN} "[requires]\n") - foreach(REQUIRE ${ARGUMENTS_REQUIRES}) - file(APPEND ${_FN} ${REQUIRE} "\n") - endforeach() - endif() - - if (DEFAULT_GENERATOR OR DEFINED ARGUMENTS_GENERATORS) - file(APPEND ${_FN} "[generators]\n") - if (DEFAULT_GENERATOR) - file(APPEND ${_FN} "cmake\n") - endif() - if (DEFINED ARGUMENTS_GENERATORS) - foreach(GENERATOR ${ARGUMENTS_GENERATORS}) - file(APPEND ${_FN} ${GENERATOR} "\n") - endforeach() - endif() - endif() - - if(DEFINED ARGUMENTS_BUILD_REQUIRES) - file(APPEND ${_FN} "[build_requires]\n") - foreach(BUILD_REQUIRE ${ARGUMENTS_BUILD_REQUIRES}) - file(APPEND ${_FN} ${BUILD_REQUIRE} "\n") - endforeach() - endif() - - if(DEFINED ARGUMENTS_IMPORTS) - file(APPEND ${_FN} "[imports]\n") - foreach(IMPORTS ${ARGUMENTS_IMPORTS}) - file(APPEND ${_FN} ${IMPORTS} "\n") - endforeach() - endif() - - if(DEFINED ARGUMENTS_OPTIONS) - file(APPEND ${_FN} "[options]\n") - foreach(OPTION ${ARGUMENTS_OPTIONS}) - file(APPEND ${_FN} ${OPTION} "\n") - endforeach() - endif() - -endfunction() - - -macro(conan_load_buildinfo) - if(CONAN_CMAKE_MULTI) - set(_CONANBUILDINFO conanbuildinfo_multi.cmake) - else() - set(_CONANBUILDINFO conanbuildinfo.cmake) - endif() - if(ARGUMENTS_INSTALL_FOLDER) - set(_CONANBUILDINFOFOLDER ${ARGUMENTS_INSTALL_FOLDER}) - else() - set(_CONANBUILDINFOFOLDER ${CMAKE_CURRENT_BINARY_DIR}) - endif() - # Checks for the existence of conanbuildinfo.cmake, and loads it - # important that it is macro, so variables defined at parent scope - if(EXISTS "${_CONANBUILDINFOFOLDER}/${_CONANBUILDINFO}") - message(STATUS "Conan: Loading ${_CONANBUILDINFO}") - include(${_CONANBUILDINFOFOLDER}/${_CONANBUILDINFO}) - else() - message(FATAL_ERROR "${_CONANBUILDINFO} doesn't exist in ${CMAKE_CURRENT_BINARY_DIR}") - endif() -endmacro() - - -macro(conan_cmake_run) - conan_parse_arguments(${ARGV}) - - if(ARGUMENTS_CONFIGURATION_TYPES AND NOT CMAKE_CONFIGURATION_TYPES) - message(WARNING "CONFIGURATION_TYPES should only be specified for multi-configuration generators") - elseif(ARGUMENTS_CONFIGURATION_TYPES AND ARGUMENTS_BUILD_TYPE) - message(WARNING "CONFIGURATION_TYPES and BUILD_TYPE arguments should not be defined at the same time.") - endif() - - if(CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE AND NOT CONAN_EXPORTED - AND NOT ARGUMENTS_BUILD_TYPE) - set(CONAN_CMAKE_MULTI ON) - if (NOT ARGUMENTS_CONFIGURATION_TYPES) - set(ARGUMENTS_CONFIGURATION_TYPES "Release;Debug") - endif() - message(STATUS "Conan: Using cmake-multi generator") - else() - set(CONAN_CMAKE_MULTI OFF) - endif() - - if(NOT CONAN_EXPORTED) - conan_cmake_setup_conanfile(${ARGV}) - if(CONAN_CMAKE_MULTI) - foreach(CMAKE_BUILD_TYPE ${ARGUMENTS_CONFIGURATION_TYPES}) - set(ENV{CONAN_IMPORT_PATH} ${CMAKE_BUILD_TYPE}) - conan_cmake_settings(settings ${ARGV}) - old_conan_cmake_install(SETTINGS ${settings} ${ARGV}) - endforeach() - set(CMAKE_BUILD_TYPE) - else() - conan_cmake_settings(settings ${ARGV}) - old_conan_cmake_install(SETTINGS ${settings} ${ARGV}) - endif() - endif() - - if (NOT ARGUMENTS_NO_LOAD) - conan_load_buildinfo() - endif() - - if(ARGUMENTS_BASIC_SETUP) - foreach(_option CMAKE_TARGETS KEEP_RPATHS NO_OUTPUT_DIRS SKIP_STD) - if(ARGUMENTS_${_option}) - if(${_option} STREQUAL "CMAKE_TARGETS") - list(APPEND _setup_options "TARGETS") - else() - list(APPEND _setup_options ${_option}) - endif() - endif() - endforeach() - conan_basic_setup(${_setup_options}) - endif() -endmacro() - -macro(conan_check) - # Checks conan availability in PATH - # Arguments REQUIRED, DETECT_QUIET and VERSION are optional - # Example usage: - # conan_check(VERSION 1.0.0 REQUIRED) - set(options REQUIRED DETECT_QUIET) - set(oneValueArgs VERSION) - cmake_parse_arguments(CONAN "${options}" "${oneValueArgs}" "" ${ARGN}) - if(NOT CONAN_DETECT_QUIET) - message(STATUS "Conan: checking conan executable") - endif() - - find_program(CONAN_CMD conan) - if(NOT CONAN_CMD AND CONAN_REQUIRED) - message(FATAL_ERROR "Conan executable not found! Please install conan.") - endif() - if(NOT CONAN_DETECT_QUIET) - message(STATUS "Conan: Found program ${CONAN_CMD}") - endif() - execute_process(COMMAND ${CONAN_CMD} --version - RESULT_VARIABLE return_code - OUTPUT_VARIABLE CONAN_VERSION_OUTPUT - ERROR_VARIABLE CONAN_VERSION_OUTPUT) - - if(NOT "${return_code}" STREQUAL "0") - message(FATAL_ERROR "Conan --version failed='${return_code}'") - endif() - - if(NOT CONAN_DETECT_QUIET) - string(STRIP "${CONAN_VERSION_OUTPUT}" _CONAN_VERSION_OUTPUT) - message(STATUS "Conan: Version found ${_CONAN_VERSION_OUTPUT}") - endif() - - if(DEFINED CONAN_VERSION) - string(REGEX MATCH ".*Conan version ([0-9]+\\.[0-9]+\\.[0-9]+)" FOO - "${CONAN_VERSION_OUTPUT}") - if(${CMAKE_MATCH_1} VERSION_LESS ${CONAN_VERSION}) - message(FATAL_ERROR "Conan outdated. Installed: ${CMAKE_MATCH_1}, \ - required: ${CONAN_VERSION}. Consider updating via 'pip \ - install conan==${CONAN_VERSION}'.") - endif() - endif() -endmacro() - -function(conan_add_remote) - # Adds a remote - # Arguments URL and NAME are required, INDEX, COMMAND and VERIFY_SSL are optional - # Example usage: - # conan_add_remote(NAME bincrafters INDEX 1 - # URL https://api.bintray.com/conan/bincrafters/public-conan - # VERIFY_SSL True) - set(oneValueArgs URL NAME INDEX COMMAND VERIFY_SSL) - cmake_parse_arguments(CONAN "" "${oneValueArgs}" "" ${ARGN}) - - if(DEFINED CONAN_INDEX) - set(CONAN_INDEX_ARG "-i ${CONAN_INDEX}") - endif() - if(DEFINED CONAN_COMMAND) - set(CONAN_CMD ${CONAN_COMMAND}) - else() - conan_check(REQUIRED DETECT_QUIET) - endif() - set(CONAN_VERIFY_SSL_ARG "True") - if(DEFINED CONAN_VERIFY_SSL) - set(CONAN_VERIFY_SSL_ARG ${CONAN_VERIFY_SSL}) - endif() - message(STATUS "Conan: Adding ${CONAN_NAME} remote repository (${CONAN_URL}) verify ssl (${CONAN_VERIFY_SSL_ARG})") - execute_process(COMMAND ${CONAN_CMD} remote add ${CONAN_NAME} ${CONAN_INDEX_ARG} -f ${CONAN_URL} ${CONAN_VERIFY_SSL_ARG} - RESULT_VARIABLE return_code) - if(NOT "${return_code}" STREQUAL "0") - message(FATAL_ERROR "Conan remote failed='${return_code}'") - endif() -endfunction() - -macro(conan_config_install) - # install a full configuration from a local or remote zip file - # Argument ITEM is required, arguments TYPE, SOURCE, TARGET and VERIFY_SSL are optional - # Example usage: - # conan_config_install(ITEM https://github.com/conan-io/cmake-conan.git - # TYPE git SOURCE source-folder TARGET target-folder VERIFY_SSL false) - set(oneValueArgs ITEM TYPE SOURCE TARGET VERIFY_SSL) - set(multiValueArgs ARGS) - cmake_parse_arguments(CONAN "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - find_program(CONAN_CMD conan) - if(NOT CONAN_CMD AND CONAN_REQUIRED) - message(FATAL_ERROR "Conan executable not found!") - endif() - - if(DEFINED CONAN_VERIFY_SSL) - set(CONAN_VERIFY_SSL_ARG "--verify-ssl=${CONAN_VERIFY_SSL}") - endif() - - if(DEFINED CONAN_TYPE) - set(CONAN_TYPE_ARG "--type=${CONAN_TYPE}") - endif() - - if(DEFINED CONAN_ARGS) - set(CONAN_ARGS_ARGS "--args=\"${CONAN_ARGS}\"") - endif() - - if(DEFINED CONAN_SOURCE) - set(CONAN_SOURCE_ARGS "--source-folder=${CONAN_SOURCE}") - endif() - - if(DEFINED CONAN_TARGET) - set(CONAN_TARGET_ARGS "--target-folder=${CONAN_TARGET}") - endif() - - set (CONAN_CONFIG_INSTALL_ARGS ${CONAN_VERIFY_SSL_ARG} - ${CONAN_TYPE_ARG} - ${CONAN_ARGS_ARGS} - ${CONAN_SOURCE_ARGS} - ${CONAN_TARGET_ARGS}) - - message(STATUS "Conan: Installing config from ${CONAN_ITEM}") - execute_process(COMMAND ${CONAN_CMD} config install ${CONAN_ITEM} ${CONAN_CONFIG_INSTALL_ARGS} - RESULT_VARIABLE return_code) - if(NOT "${return_code}" STREQUAL "0") - message(FATAL_ERROR "Conan config failed='${return_code}'") - endif() -endmacro() diff --git a/cmake/uninstall.cmake.in b/cmake/uninstall.cmake.in new file mode 100644 index 0000000000..d7a10953b6 --- /dev/null +++ b/cmake/uninstall.cmake.in @@ -0,0 +1,65 @@ +# from https://github.com/OSGeo/PROJ/blob/5920e2998318189970c0fe1e2db60f643515b9ea/cmake/uninstall.cmake.in + +# install_manifest.txt is created in the top build tree, not the project one +if (NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_BINARY_DIR@/install_manifest.txt\"") +endif() + +set(uninstall_file_list "@CMAKE_BINARY_DIR@/install_manifest.txt") +if(EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest_extra.txt") + list(APPEND uninstall_file_list "@CMAKE_CURRENT_BINARY_DIR@/install_manifest_extra.txt") +endif() + +set(dir_list) +foreach (manifest_file IN ITEMS ${uninstall_file_list}) + file(READ "${manifest_file}" files) + string(REGEX REPLACE "\n$" "" files "${files}") + string(REGEX REPLACE "\n" ";" files "${files}") + list(REVERSE files) + foreach (file ${files}) + message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + if (IS_DIRECTORY "$ENV{DESTDIR}${file}") + list(APPEND dir_list "${file}") + elseif (EXISTS "$ENV{DESTDIR}${file}") + get_filename_component(dir "${file}" DIRECTORY) + list(APPEND dir_list "${dir}") + execute_process( + COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" + OUTPUT_VARIABLE rm_out + RESULT_VARIABLE rm_retval + ) + if(NOT ${rm_retval} EQUAL 0) + message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + endif (NOT ${rm_retval} EQUAL 0) + else () + message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") + endif () + endforeach(file) +endforeach() + +while(NOT "${dir_list}" STREQUAL "") + list(REMOVE_DUPLICATES dir_list) + set(new_dir_list) + foreach (file IN ITEMS ${dir_list}) + if (IS_DIRECTORY "$ENV{DESTDIR}${file}" AND "${file}" MATCHES "@CMAKE_INSTALL_PREFIX@[/\\].+") + file(GLOB file_list "$ENV{DESTDIR}${file}/*") + list(LENGTH file_list file_list_len) + # Only remove empty directories + if(file_list_len EQUAL 0) + message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + execute_process( + COMMAND @CMAKE_COMMAND@ -E remove_directory "$ENV{DESTDIR}${file}" + OUTPUT_VARIABLE rm_out + RESULT_VARIABLE rm_retval + ) + if(${rm_retval} EQUAL 0) + get_filename_component(upper_dir "${file}" DIRECTORY) + list(APPEND new_dir_list "${upper_dir}") + else() + message(STATUS "Problem when removing directory \"$ENV{DESTDIR}${file}\"") + endif() + endif() + endif() + endforeach() + set(dir_list "${new_dir_list}") +endwhile() diff --git a/codecov.yml b/codecov.yml index 629965e1be..7750fa7857 100644 --- a/codecov.yml +++ b/codecov.yml @@ -2,6 +2,5 @@ coverage: ignore: - third_party/.* - - bench/.* comment: off diff --git a/conanfile.txt b/conanfile.txt deleted file mode 100644 index 61aafb8abc..0000000000 --- a/conanfile.txt +++ /dev/null @@ -1,39 +0,0 @@ -[requires] -boost/1.71.0 - -[generators] -cmake_find_package -json - -[options] -boost:header_only=True -boost:without_atomic=True -boost:without_chrono=True -boost:without_container=True -boost:without_context=True -boost:without_contract=True -boost:without_coroutine=True -boost:without_date_time=True -boost:without_exception=True -boost:without_fiber=True -boost:without_filesystem=True -boost:without_graph=True -boost:without_graph_parallel=True -boost:without_iostreams=True -boost:without_locale=True -boost:without_log=True -boost:without_math=True -boost:without_mpi=True -boost:without_program_options=True -boost:without_python=True -boost:without_random=True -boost:without_regex=True -boost:without_serialization=True -boost:without_stacktrace=True -boost:without_system=True -boost:without_locale=True -boost:without_test=True -boost:without_thread=True -boost:without_timer=True -boost:without_type_erasure=True -boost:without_wave=True diff --git a/date_time/africa b/date_time/africa deleted file mode 100644 index 84e1ce2d5d..0000000000 --- a/date_time/africa +++ /dev/null @@ -1,1285 +0,0 @@ -# This file is in the public domain, so clarified as of -# 2009-05-17 by Arthur David Olson. - -# This file is by no means authoritative; if you think you know better, -# go ahead and edit the file (and please send any changes to -# tz@iana.org for general use in the future). For more, please see -# the file CONTRIBUTING in the tz distribution. - -# From Paul Eggert (2017-04-09): -# -# Unless otherwise specified, the source for data through 1990 is: -# Thomas G. Shanks and Rique Pottenger, The International Atlas (6th edition), -# San Diego: ACS Publications, Inc. (2003). -# Unfortunately this book contains many errors and cites no sources. -# -# Many years ago Gwillim Law wrote that a good source -# for time zone data was the International Air Transport -# Association's Standard Schedules Information Manual (IATA SSIM), -# published semiannually. Law sent in several helpful summaries -# of the IATA's data after 1990. Except where otherwise noted, -# IATA SSIM is the source for entries after 1990. -# -# Another source occasionally used is Edward W. Whitman, World Time Differences, -# Whitman Publishing Co, 2 Niagara Av, Ealing, London (undated), which -# I found in the UCLA library. -# -# For data circa 1899, a common source is: -# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94. -# https://www.jstor.org/stable/1774359 -# -# A reliable and entertaining source about time zones is -# Derek Howse, Greenwich time and longitude, Philip Wilson Publishers (1997). -# -# European-style abbreviations are commonly used along the Mediterranean. -# For sub-Saharan Africa abbreviations were less standardized. -# Previous editions of this database used WAT, CAT, SAT, and EAT -# for UT +00 through +03, respectively, -# but in 1997 Mark R V Murray reported that -# 'SAST' is the official abbreviation for +02 in the country of South Africa, -# 'CAT' is commonly used for +02 in countries north of South Africa, and -# 'WAT' is probably the best name for +01, as the common phrase for -# the area that includes Nigeria is "West Africa". -# -# To summarize, the following abbreviations seemed to have some currency: -# +00 GMT Greenwich Mean Time -# +02 CAT Central Africa Time -# +02 SAST South Africa Standard Time -# and Murray suggested the following abbreviation: -# +01 WAT West Africa Time -# Murray's suggestion seems to have caught on in news reports and the like. -# I vaguely recall 'WAT' also being used for -01 in the past but -# cannot now come up with solid citations. -# -# I invented the following abbreviations; corrections are welcome! -# +02 WAST West Africa Summer Time (no longer used) -# +03 CAST Central Africa Summer Time (no longer used) -# +03 SAST South Africa Summer Time (no longer used) -# +03 EAT East Africa Time -# 'EAT' also seems to have caught on; the others are rare but are paired -# with better-attested non-DST abbreviations. - -# Algeria -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Algeria 1916 only - Jun 14 23:00s 1:00 S -Rule Algeria 1916 1919 - Oct Sun>=1 23:00s 0 - -Rule Algeria 1917 only - Mar 24 23:00s 1:00 S -Rule Algeria 1918 only - Mar 9 23:00s 1:00 S -Rule Algeria 1919 only - Mar 1 23:00s 1:00 S -Rule Algeria 1920 only - Feb 14 23:00s 1:00 S -Rule Algeria 1920 only - Oct 23 23:00s 0 - -Rule Algeria 1921 only - Mar 14 23:00s 1:00 S -Rule Algeria 1921 only - Jun 21 23:00s 0 - -Rule Algeria 1939 only - Sep 11 23:00s 1:00 S -Rule Algeria 1939 only - Nov 19 1:00 0 - -Rule Algeria 1944 1945 - Apr Mon>=1 2:00 1:00 S -Rule Algeria 1944 only - Oct 8 2:00 0 - -Rule Algeria 1945 only - Sep 16 1:00 0 - -Rule Algeria 1971 only - Apr 25 23:00s 1:00 S -Rule Algeria 1971 only - Sep 26 23:00s 0 - -Rule Algeria 1977 only - May 6 0:00 1:00 S -Rule Algeria 1977 only - Oct 21 0:00 0 - -Rule Algeria 1978 only - Mar 24 1:00 1:00 S -Rule Algeria 1978 only - Sep 22 3:00 0 - -Rule Algeria 1980 only - Apr 25 0:00 1:00 S -Rule Algeria 1980 only - Oct 31 2:00 0 - -# Shanks & Pottenger give 0:09:20 for Paris Mean Time; go with Howse's -# more precise 0:09:21. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Algiers 0:12:12 - LMT 1891 Mar 15 0:01 - 0:09:21 - PMT 1911 Mar 11 # Paris Mean Time - 0:00 Algeria WE%sT 1940 Feb 25 2:00 - 1:00 Algeria CE%sT 1946 Oct 7 - 0:00 - WET 1956 Jan 29 - 1:00 - CET 1963 Apr 14 - 0:00 Algeria WE%sT 1977 Oct 21 - 1:00 Algeria CE%sT 1979 Oct 26 - 0:00 Algeria WE%sT 1981 May - 1:00 - CET - -# Angola -# Benin -# See Africa/Lagos. - -# Botswana -# See Africa/Maputo. - -# Burkina Faso -# See Africa/Abidjan. - -# Burundi -# See Africa/Maputo. - -# Cameroon -# See Africa/Lagos. - -# Cape Verde / Cabo Verde -# -# From Paul Eggert (2018-02-16): -# Shanks gives 1907 for the transition to +02. -# For now, ignore that and follow the 1911-05-26 Portuguese decree -# (see Europe/Lisbon). -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Atlantic/Cape_Verde -1:34:04 - LMT 1912 Jan 01 2:00u # Praia - -2:00 - -02 1942 Sep - -2:00 1:00 -01 1945 Oct 15 - -2:00 - -02 1975 Nov 25 2:00 - -1:00 - -01 - -# Central African Republic -# See Africa/Lagos. - -# Chad -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Ndjamena 1:00:12 - LMT 1912 # N'Djamena - 1:00 - WAT 1979 Oct 14 - 1:00 1:00 WAST 1980 Mar 8 - 1:00 - WAT - -# Comoros -# See Africa/Nairobi. - -# Democratic Republic of the Congo -# See Africa/Lagos for the western part and Africa/Maputo for the eastern. - -# Republic of the Congo -# See Africa/Lagos. - -# Côte d'Ivoire / Ivory Coast -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Abidjan -0:16:08 - LMT 1912 - 0:00 - GMT -Link Africa/Abidjan Africa/Bamako # Mali -Link Africa/Abidjan Africa/Banjul # Gambia -Link Africa/Abidjan Africa/Conakry # Guinea -Link Africa/Abidjan Africa/Dakar # Senegal -Link Africa/Abidjan Africa/Freetown # Sierra Leone -Link Africa/Abidjan Africa/Lome # Togo -Link Africa/Abidjan Africa/Nouakchott # Mauritania -Link Africa/Abidjan Africa/Ouagadougou # Burkina Faso -Link Africa/Abidjan Atlantic/St_Helena # St Helena - -# Djibouti -# See Africa/Nairobi. - -############################################################################### - -# Egypt - -# Milne says Cairo used 2:05:08.9, the local mean time of the Abbasizeh -# observatory; round to nearest. Milne also says that the official time for -# Egypt was mean noon at the Great Pyramid, 2:04:30.5, but apparently this -# did not apply to Cairo, Alexandria, or Port Said. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Egypt 1940 only - Jul 15 0:00 1:00 S -Rule Egypt 1940 only - Oct 1 0:00 0 - -Rule Egypt 1941 only - Apr 15 0:00 1:00 S -Rule Egypt 1941 only - Sep 16 0:00 0 - -Rule Egypt 1942 1944 - Apr 1 0:00 1:00 S -Rule Egypt 1942 only - Oct 27 0:00 0 - -Rule Egypt 1943 1945 - Nov 1 0:00 0 - -Rule Egypt 1945 only - Apr 16 0:00 1:00 S -Rule Egypt 1957 only - May 10 0:00 1:00 S -Rule Egypt 1957 1958 - Oct 1 0:00 0 - -Rule Egypt 1958 only - May 1 0:00 1:00 S -Rule Egypt 1959 1981 - May 1 1:00 1:00 S -Rule Egypt 1959 1965 - Sep 30 3:00 0 - -Rule Egypt 1966 1994 - Oct 1 3:00 0 - -Rule Egypt 1982 only - Jul 25 1:00 1:00 S -Rule Egypt 1983 only - Jul 12 1:00 1:00 S -Rule Egypt 1984 1988 - May 1 1:00 1:00 S -Rule Egypt 1989 only - May 6 1:00 1:00 S -Rule Egypt 1990 1994 - May 1 1:00 1:00 S -# IATA (after 1990) says transitions are at 0:00. -# Go with IATA starting in 1995, except correct 1995 entry from 09-30 to 09-29. - -# From Alexander Krivenyshev (2011-04-20): -# "...Egypt's interim cabinet decided on Wednesday to cancel daylight -# saving time after a poll posted on its website showed the majority of -# Egyptians would approve the cancellation." -# -# Egypt to cancel daylight saving time -# http://www.almasryalyoum.com/en/node/407168 -# or -# http://www.worldtimezone.com/dst_news/dst_news_egypt04.html -Rule Egypt 1995 2010 - Apr lastFri 0:00s 1:00 S -Rule Egypt 1995 2005 - Sep lastThu 24:00 0 - -# From Steffen Thorsen (2006-09-19): -# The Egyptian Gazette, issue 41,090 (2006-09-18), page 1, reports: -# Egypt will turn back clocks by one hour at the midnight of Thursday -# after observing the daylight saving time since May. -# http://news.gom.com.eg/gazette/pdf/2006/09/18/01.pdf -Rule Egypt 2006 only - Sep 21 24:00 0 - -# From Dirk Losch (2007-08-14): -# I received a mail from an airline which says that the daylight -# saving time in Egypt will end in the night of 2007-09-06 to 2007-09-07. -# From Jesper Nørgaard Welen (2007-08-15): [The following agree:] -# http://www.nentjes.info/Bill/bill5.htm -# https://www.timeanddate.com/worldclock/city.html?n=53 -# From Steffen Thorsen (2007-09-04): The official information...: -# http://www.sis.gov.eg/En/EgyptOnline/Miscellaneous/000002/0207000000000000001580.htm -Rule Egypt 2007 only - Sep Thu>=1 24:00 0 - -# From Abdelrahman Hassan (2007-09-06): -# Due to the Hijri (lunar Islamic calendar) year being 11 days shorter -# than the year of the Gregorian calendar, Ramadan shifts earlier each -# year. This year it will be observed September 13 (September is quite -# hot in Egypt), and the idea is to make fasting easier for workers by -# shifting business hours one hour out of daytime heat. Consequently, -# unless discontinued, next DST may end Thursday 28 August 2008. -# From Paul Eggert (2007-08-17): -# For lack of better info, assume the new rule is last Thursday in August. - -# From Petr Machata (2009-04-06): -# The following appeared in Red Hat bugzilla[1] (edited): -# -# > $ zdump -v /usr/share/zoneinfo/Africa/Cairo | grep 2009 -# > /usr/share/zoneinfo/Africa/Cairo Thu Apr 23 21:59:59 2009 UTC = Thu = -# Apr 23 -# > 23:59:59 2009 EET isdst=0 gmtoff=7200 -# > /usr/share/zoneinfo/Africa/Cairo Thu Apr 23 22:00:00 2009 UTC = Fri = -# Apr 24 -# > 01:00:00 2009 EEST isdst=1 gmtoff=10800 -# > /usr/share/zoneinfo/Africa/Cairo Thu Aug 27 20:59:59 2009 UTC = Thu = -# Aug 27 -# > 23:59:59 2009 EEST isdst=1 gmtoff=10800 -# > /usr/share/zoneinfo/Africa/Cairo Thu Aug 27 21:00:00 2009 UTC = Thu = -# Aug 27 -# > 23:00:00 2009 EET isdst=0 gmtoff=7200 -# -# > end date should be Thu Sep 24 2009 (Last Thursday in September at 23:59= -# :59) -# > http://support.microsoft.com/kb/958729/ -# -# timeanddate[2] and another site I've found[3] also support that. -# -# [1] https://bugzilla.redhat.com/show_bug.cgi?id=492263 -# [2] https://www.timeanddate.com/worldclock/clockchange.html?n=53 -# [3] https://wwp.greenwichmeantime.com/time-zone/africa/egypt/ - -# From Arthur David Olson (2009-04-20): -# In 2009 (and for the next several years), Ramadan ends before the fourth -# Thursday in September; Egypt is expected to revert to the last Thursday -# in September. - -# From Steffen Thorsen (2009-08-11): -# We have been able to confirm the August change with the Egyptian Cabinet -# Information and Decision Support Center: -# https://www.timeanddate.com/news/time/egypt-dst-ends-2009.html -# -# The Middle East News Agency -# https://www.mena.org.eg/index.aspx -# also reports "Egypt starts winter time on August 21" -# today in article numbered "71, 11/08/2009 12:25 GMT." -# Only the title above is available without a subscription to their service, -# and can be found by searching for "winter" in their search engine -# (at least today). - -# From Alexander Krivenyshev (2010-07-20): -# According to News from Egypt - Al-Masry Al-Youm Egypt's cabinet has -# decided that Daylight Saving Time will not be used in Egypt during -# Ramadan. -# -# Arabic translation: -# "Clocks to go back during Ramadan - and then forward again" -# http://www.almasryalyoum.com/en/news/clocks-go-back-during-ramadan-and-then-forward-again -# http://www.worldtimezone.com/dst_news/dst_news_egypt02.html - -# From Ahmad El-Dardiry (2014-05-07): -# Egypt is to change back to Daylight system on May 15 -# http://english.ahram.org.eg/NewsContent/1/64/100735/Egypt/Politics-/Egypts-government-to-reapply-daylight-saving-time-.aspx - -# From Gunther Vermier (2014-05-13): -# our Egypt office confirms that the change will be at 15 May "midnight" (24:00) - -# From Imed Chihi (2014-06-04): -# We have finally "located" a precise official reference about the DST changes -# in Egypt. The Ministers Cabinet decision is explained at -# http://www.cabinet.gov.eg/Media/CabinetMeetingsDetails.aspx?id=347 ... -# [T]his (Arabic) site is not accessible outside Egypt, but the page ... -# translates into: "With regard to daylight saving time, it is scheduled to -# take effect at exactly twelve o'clock this evening, Thursday, 15 MAY 2014, -# to be suspended by twelve o'clock on the evening of Thursday, 26 JUN 2014, -# and re-established again at the end of the month of Ramadan, at twelve -# o'clock on the evening of Thursday, 31 JUL 2014." This statement has been -# reproduced by other (more accessible) sites[, e.g.,]... -# http://elgornal.net/news/news.aspx?id=4699258 - -# From Paul Eggert (2014-06-04): -# Sarah El Deeb and Lee Keath of AP report that the Egyptian government says -# the change is because of blackouts in Cairo, even though Ahram Online (cited -# above) says DST had no affect on electricity consumption. There is -# no information about when DST will end this fall. See: -# http://abcnews.go.com/International/wireStory/el-sissi-pushes-egyptians-line-23614833 - -# From Steffen Thorsen (2015-04-08): -# Egypt will start DST on midnight after Thursday, April 30, 2015. -# This is based on a law (no 35) from May 15, 2014 saying it starts the last -# Thursday of April.... Clocks will still be turned back for Ramadan, but -# dates not yet announced.... -# http://almogaz.com/news/weird-news/2015/04/05/1947105 ... -# https://www.timeanddate.com/news/time/egypt-starts-dst-2015.html - -# From Ahmed Nazmy (2015-04-20): -# Egypt's ministers cabinet just announced ... that it will cancel DST at -# least for 2015. -# -# From Tim Parenti (2015-04-20): -# http://english.ahram.org.eg/WriterArticles/NewsContentP/1/128195/Egypt/No-daylight-saving-this-summer-Egypts-prime-minist.aspx -# "Egypt's cabinet agreed on Monday not to switch clocks for daylight saving -# time this summer, and carry out studies on the possibility of canceling the -# practice altogether in future years." -# -# From Paul Eggert (2015-04-24): -# Yesterday the office of Egyptian President El-Sisi announced his -# decision to abandon DST permanently. See Ahram Online 2015-04-24. -# http://english.ahram.org.eg/NewsContent/1/64/128509/Egypt/Politics-/Sisi-cancels-daylight-saving-time-in-Egypt.aspx - -# From Steffen Thorsen (2016-04-29): -# Egypt will have DST from July 7 until the end of October.... -# http://english.ahram.org.eg/NewsContentP/1/204655/Egypt/Daylight-savings-time-returning-to-Egypt-on--July.aspx -# From Mina Samuel (2016-07-04): -# Egyptian government took the decision to cancel the DST, - -Rule Egypt 2008 only - Aug lastThu 24:00 0 - -Rule Egypt 2009 only - Aug 20 24:00 0 - -Rule Egypt 2010 only - Aug 10 24:00 0 - -Rule Egypt 2010 only - Sep 9 24:00 1:00 S -Rule Egypt 2010 only - Sep lastThu 24:00 0 - -Rule Egypt 2014 only - May 15 24:00 1:00 S -Rule Egypt 2014 only - Jun 26 24:00 0 - -Rule Egypt 2014 only - Jul 31 24:00 1:00 S -Rule Egypt 2014 only - Sep lastThu 24:00 0 - - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Cairo 2:05:09 - LMT 1900 Oct - 2:00 Egypt EE%sT - -# Equatorial Guinea -# See Africa/Lagos. - -# Eritrea -# Ethiopia -# See Africa/Nairobi. - -# Gabon -# See Africa/Lagos. - -# Gambia -# See Africa/Abidjan. - -# Ghana - -# From Paul Eggert (2018-01-30): -# Whitman says DST was observed from 1931 to "the present"; -# Shanks & Pottenger say 1936 to 1942 with 20 minutes of DST, -# with transitions on 09-01 and 12-31 at 00:00. -# Page 33 of Parish GCB, Colonial Reports - Annual. No. 1066. Gold -# Coast. Report for 1919. (March 1921), OCLC 784024077 -# http://libsysdigi.library.illinois.edu/ilharvest/africana/books2011-05/5530214/5530214_1919/5530214_1919_opt.pdf -# lists the Determination of the Time Ordinance, 1919, No. 18, -# "to advance the time observed locally by the space of twenty minutes -# during the last four months of each year; the object in view being -# to extend during those months the period of daylight-time available -# for evening recreation after office hours." -# Vanessa Ogle, The Global Transformation of Time, 1870-1950 (2015), p 33, -# writes "In 1919, the Gold Coast (Ghana as of 1957) made Greenwich -# time its legal time and simultaneously legalized a summer time of -# UTC - 00:20 minutes from March to October."; a footnote lists -# the ordinance as being dated 1919-11-24. -# The Crown Colonist, Volume 12 (1942), p 176, says "the Government -# intend advancing Gold Coast time half an hour ahead of G.M.T. -# The actual date of the alteration has not yet been announced." -# These sources are incomplete and contradictory. Possibly what is -# now Ghana observed different DST regimes in different years. For -# lack of better info, use Shanks except treat the minus sign as a -# typo, and assume DST started in 1920 not 1936. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Ghana 1920 1942 - Sep 1 0:00 0:20 - -Rule Ghana 1920 1942 - Dec 31 0:00 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Accra -0:00:52 - LMT 1918 - 0:00 Ghana GMT/+0020 - -# Guinea -# See Africa/Abidjan. - -# Guinea-Bissau -# -# From Paul Eggert (2018-02-16): -# Shanks gives 1911-05-26 for the transition to WAT, -# evidently confusing the date of the Portuguese decree -# (see Europe/Lisbon) with the date that it took effect. -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Bissau -1:02:20 - LMT 1912 Jan 1 1:00u - -1:00 - -01 1975 - 0:00 - GMT - -# Kenya -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Nairobi 2:27:16 - LMT 1928 Jul - 3:00 - EAT 1930 - 2:30 - +0230 1940 - 2:45 - +0245 1960 - 3:00 - EAT -Link Africa/Nairobi Africa/Addis_Ababa # Ethiopia -Link Africa/Nairobi Africa/Asmara # Eritrea -Link Africa/Nairobi Africa/Dar_es_Salaam # Tanzania -Link Africa/Nairobi Africa/Djibouti -Link Africa/Nairobi Africa/Kampala # Uganda -Link Africa/Nairobi Africa/Mogadishu # Somalia -Link Africa/Nairobi Indian/Antananarivo # Madagascar -Link Africa/Nairobi Indian/Comoro -Link Africa/Nairobi Indian/Mayotte - -# Lesotho -# See Africa/Johannesburg. - -# Liberia -# -# From Paul Eggert (2017-03-02): -# -# The Nautical Almanac for the Year 1970, p 264, is the source for -0:44:30. -# -# In 1972 Liberia was the last country to switch from a UT offset -# that was not a multiple of 15 or 20 minutes. The 1972 change was on -# 1972-01-07, according to an entry dated 1972-01-04 on p 330 of: -# Presidential Papers: First year of the administration of -# President William R. Tolbert, Jr., July 23, 1971-July 31, 1972. -# Monrovia: Executive Mansion. -# -# Use the abbreviation "MMT" before 1972, as the more-accurate numeric -# abbreviation "-004430" would be one byte over the POSIX limit. -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Monrovia -0:43:08 - LMT 1882 - -0:43:08 - MMT 1919 Mar # Monrovia Mean Time - -0:44:30 - MMT 1972 Jan 7 # approximately MMT - 0:00 - GMT - -############################################################################### - -# Libya - -# From Even Scharning (2012-11-10): -# Libya set their time one hour back at 02:00 on Saturday November 10. -# https://www.libyaherald.com/2012/11/04/clocks-to-go-back-an-hour-on-saturday/ -# Here is an official source [in Arabic]: http://ls.ly/fb6Yc -# -# Steffen Thorsen forwarded a translation (2012-11-10) in -# https://mm.icann.org/pipermail/tz/2012-November/018451.html -# -# From Tim Parenti (2012-11-11): -# Treat the 2012-11-10 change as a zone change from UTC+2 to UTC+1. -# The DST rules planned for 2013 and onward roughly mirror those of Europe -# (either two days before them or five days after them, so as to fall on -# lastFri instead of lastSun). - -# From Even Scharning (2013-10-25): -# The scheduled end of DST in Libya on Friday, October 25, 2013 was -# cancelled yesterday.... -# https://www.libyaherald.com/2013/10/24/correction-no-time-change-tomorrow/ -# -# From Paul Eggert (2013-10-25): -# For now, assume they're reverting to the pre-2012 rules of permanent UT +02. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Libya 1951 only - Oct 14 2:00 1:00 S -Rule Libya 1952 only - Jan 1 0:00 0 - -Rule Libya 1953 only - Oct 9 2:00 1:00 S -Rule Libya 1954 only - Jan 1 0:00 0 - -Rule Libya 1955 only - Sep 30 0:00 1:00 S -Rule Libya 1956 only - Jan 1 0:00 0 - -Rule Libya 1982 1984 - Apr 1 0:00 1:00 S -Rule Libya 1982 1985 - Oct 1 0:00 0 - -Rule Libya 1985 only - Apr 6 0:00 1:00 S -Rule Libya 1986 only - Apr 4 0:00 1:00 S -Rule Libya 1986 only - Oct 3 0:00 0 - -Rule Libya 1987 1989 - Apr 1 0:00 1:00 S -Rule Libya 1987 1989 - Oct 1 0:00 0 - -Rule Libya 1997 only - Apr 4 0:00 1:00 S -Rule Libya 1997 only - Oct 4 0:00 0 - -Rule Libya 2013 only - Mar lastFri 1:00 1:00 S -Rule Libya 2013 only - Oct lastFri 2:00 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Tripoli 0:52:44 - LMT 1920 - 1:00 Libya CE%sT 1959 - 2:00 - EET 1982 - 1:00 Libya CE%sT 1990 May 4 -# The 1996 and 1997 entries are from Shanks & Pottenger; -# the IATA SSIM data entries contain some obvious errors. - 2:00 - EET 1996 Sep 30 - 1:00 Libya CE%sT 1997 Oct 4 - 2:00 - EET 2012 Nov 10 2:00 - 1:00 Libya CE%sT 2013 Oct 25 2:00 - 2:00 - EET - -# Madagascar -# See Africa/Nairobi. - -# Malawi -# See Africa/Maputo. - -# Mali -# Mauritania -# See Africa/Abidjan. - -# Mauritius - -# From Steffen Thorsen (2008-06-25): -# Mauritius plans to observe DST from 2008-11-01 to 2009-03-31 on a trial -# basis.... -# It seems that Mauritius observed daylight saving time from 1982-10-10 to -# 1983-03-20 as well, but that was not successful.... -# https://www.timeanddate.com/news/time/mauritius-daylight-saving-time.html - -# From Alex Krivenyshev (2008-06-25): -# http://economicdevelopment.gov.mu/portal/site/Mainhomepage/menuitem.a42b24128104d9845dabddd154508a0c/?content_id=0a7cee8b5d69a110VgnVCM1000000a04a8c0RCRD - -# From Arthur David Olson (2008-06-30): -# The www.timeanddate.com article cited by Steffen Thorsen notes that "A -# final decision has yet to be made on the times that daylight saving -# would begin and end on these dates." As a place holder, use midnight. - -# From Paul Eggert (2008-06-30): -# Follow Thorsen on DST in 1982/1983, instead of Shanks & Pottenger. - -# From Steffen Thorsen (2008-07-10): -# According to -# http://www.lexpress.mu/display_article.php?news_id=111216 -# (in French), Mauritius will start and end their DST a few days earlier -# than previously announced (2008-11-01 to 2009-03-31). The new start -# date is 2008-10-26 at 02:00 and the new end date is 2009-03-27 (no time -# given, but it is probably at either 2 or 3 wall clock time). -# -# A little strange though, since the article says that they moved the date -# to align itself with Europe and USA which also change time on that date, -# but that means they have not paid attention to what happened in -# USA/Canada last year (DST ends first Sunday in November). I also wonder -# why that they end on a Friday, instead of aligning with Europe which -# changes two days later. - -# From Alex Krivenyshev (2008-07-11): -# Seems that English language article "The revival of daylight saving -# time: Energy conservation?"- No. 16578 (07/11/2008) was originally -# published on Monday, June 30, 2008... -# -# I guess that article in French "Le gouvernement avance l'introduction -# de l'heure d'été" stating that DST in Mauritius starting on October 26 -# and ending on March 27, 2009 is the most recent one.... -# http://www.worldtimezone.com/dst_news/dst_news_mauritius02.html - -# From Riad M. Hossen Ally (2008-08-03): -# The Government of Mauritius weblink -# http://www.gov.mu/portal/site/pmosite/menuitem.4ca0efdee47462e7440a600248a521ca/?content_id=4728ca68b2a5b110VgnVCM1000000a04a8c0RCRD -# Cabinet Decision of July 18th, 2008 states as follows: -# -# 4. ...Cabinet has agreed to the introduction into the National Assembly -# of the Time Bill which provides for the introduction of summer time in -# Mauritius. The summer time period which will be of one hour ahead of -# the standard time, will be aligned with that in Europe and the United -# States of America. It will start at two o'clock in the morning on the -# last Sunday of October and will end at two o'clock in the morning on -# the last Sunday of March the following year. The summer time for the -# year 2008-2009 will, therefore, be effective as from 26 October 2008 -# and end on 29 March 2009. - -# From Ed Maste (2008-10-07): -# THE TIME BILL (No. XXVII of 2008) Explanatory Memorandum states the -# beginning / ending of summer time is 2 o'clock standard time in the -# morning of the last Sunday of October / last Sunday of March. -# http://www.gov.mu/portal/goc/assemblysite/file/bill2708.pdf - -# From Steffen Thorsen (2009-06-05): -# According to several sources, Mauritius will not continue to observe -# DST the coming summer... -# -# Some sources, in French: -# http://www.defimedia.info/news/946/Rashid-Beebeejaun-:-%C2%AB-L%E2%80%99heure-d%E2%80%99%C3%A9t%C3%A9-ne-sera-pas-appliqu%C3%A9e-cette-ann%C3%A9e-%C2%BB -# http://lexpress.mu/Story/3398~Beebeejaun---Les-objectifs-d-%C3%A9conomie-d-%C3%A9nergie-de-l-heure-d-%C3%A9t%C3%A9-ont-%C3%A9t%C3%A9-atteints- -# -# Our wrap-up: -# https://www.timeanddate.com/news/time/mauritius-dst-will-not-repeat.html - -# From Arthur David Olson (2009-07-11): -# The "mauritius-dst-will-not-repeat" wrapup includes this: -# "The trial ended on March 29, 2009, when the clocks moved back by one hour -# at 2am (or 02:00) local time..." - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Mauritius 1982 only - Oct 10 0:00 1:00 - -Rule Mauritius 1983 only - Mar 21 0:00 0 - -Rule Mauritius 2008 only - Oct lastSun 2:00 1:00 - -Rule Mauritius 2009 only - Mar lastSun 2:00 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis - 4:00 Mauritius +04/+05 -# Agalega Is, Rodriguez -# no information; probably like Indian/Mauritius - -# Mayotte -# See Africa/Nairobi. - -# Morocco -# See the 'europe' file for Spanish Morocco (Africa/Ceuta). - -# From Alex Krivenyshev (2008-05-09): -# Here is an article that Morocco plan to introduce Daylight Saving Time between -# 1 June, 2008 and 27 September, 2008. -# -# "... Morocco is to save energy by adjusting its clock during summer so it will -# be one hour ahead of GMT between 1 June and 27 September, according to -# Communication Minister and Government Spokesman, Khalid Naciri...." -# -# http://www.worldtimezone.com/dst_news/dst_news_morocco01.html -# http://en.afrik.com/news11892.html - -# From Alex Krivenyshev (2008-05-09): -# The Morocco time change can be confirmed on Morocco web site Maghreb Arabe -# Presse: -# http://www.map.ma/eng/sections/box3/morocco_shifts_to_da/view -# -# Morocco shifts to daylight time on June 1st through September 27, Govt. -# spokesman. - -# From Patrice Scattolin (2008-05-09): -# According to this article: -# https://www.avmaroc.com/actualite/heure-dete-comment-a127896.html -# (and republished here: ) -# the changes occur at midnight: -# -# Saturday night May 31st at midnight (which in French is to be -# interpreted as the night between Saturday and Sunday) -# Sunday night the 28th at midnight -# -# Seeing that the 28th is Monday, I am guessing that she intends to say -# the midnight of the 28th which is the midnight between Sunday and -# Monday, which jives with other sources that say that it's inclusive -# June 1st to Sept 27th. -# -# The decision was taken by decree *2-08-224 *but I can't find the decree -# published on the web. -# -# It's also confirmed here: -# http://www.maroc.ma/NR/exeres/FACF141F-D910-44B0-B7FA-6E03733425D1.htm -# on a government portal as being between June 1st and Sept 27th (not yet -# posted in English). -# -# The following Google query will generate many relevant hits: -# https://www.google.com/search?hl=en&q=Conseil+de+gouvernement+maroc+heure+avance&btnG=Search - -# From Steffen Thorsen (2008-08-27): -# Morocco will change the clocks back on the midnight between August 31 -# and September 1. They originally planned to observe DST to near the end -# of September: -# -# One article about it (in French): -# http://www.menara.ma/fr/Actualites/Maroc/Societe/ci.retour_a_l_heure_gmt_a_partir_du_dimanche_31_aout_a_minuit_officiel_.default -# -# We have some further details posted here: -# https://www.timeanddate.com/news/time/morocco-ends-dst-early-2008.html - -# From Steffen Thorsen (2009-03-17): -# Morocco will observe DST from 2009-06-01 00:00 to 2009-08-21 00:00 according -# to many sources, such as -# http://news.marweb.com/morocco/entertainment/morocco-daylight-saving.html -# http://www.medi1sat.ma/fr/depeche.aspx?idp=2312 -# (French) -# -# Our summary: -# https://www.timeanddate.com/news/time/morocco-starts-dst-2009.html - -# From Alexander Krivenyshev (2009-03-17): -# Here is a link to official document from Royaume du Maroc Premier Ministre, -# Ministère de la Modernisation des Secteurs Publics -# -# Under Article 1 of Royal Decree No. 455-67 of Act 23 safar 1387 (2 June 1967) -# concerning the amendment of the legal time, the Ministry of Modernization of -# Public Sectors announced that the official time in the Kingdom will be -# advanced 60 minutes from Sunday 31 May 2009 at midnight. -# -# http://www.mmsp.gov.ma/francais/Actualites_fr/PDF_Actualites_Fr/HeureEte_FR.pdf -# http://www.worldtimezone.com/dst_news/dst_news_morocco03.html - -# From Steffen Thorsen (2010-04-13): -# Several news media in Morocco report that the Ministry of Modernization -# of Public Sectors has announced that Morocco will have DST from -# 2010-05-02 to 2010-08-08. -# -# Example: -# http://www.lavieeco.com/actualites/4099-le-maroc-passera-a-l-heure-d-ete-gmt1-le-2-mai.html -# (French) -# Our page: -# https://www.timeanddate.com/news/time/morocco-starts-dst-2010.html - -# From Dan Abitol (2011-03-30): -# ...Rules for Africa/Casablanca are the following (24h format) -# The 3rd April 2011 at 00:00:00, [it] will be 3rd April 01:00:00 -# The 31st July 2011 at 00:59:59, [it] will be 31st July 00:00:00 -# ...Official links of change in morocco -# The change was broadcast on the FM Radio -# I ve called ANRT (telecom regulations in Morocco) at -# +212.537.71.84.00 -# http://www.anrt.net.ma/fr/ -# They said that -# http://www.map.ma/fr/sections/accueil/l_heure_legale_au_ma/view -# is the official publication to look at. -# They said that the decision was already taken. -# -# More articles in the press -# https://www.yabiladi.com/articles/details/5058/secret-l-heure-d-ete-maroc-leve.html -# http://www.lematin.ma/Actualite/Express/Article.asp?id=148923 -# http://www.lavieeco.com/actualite/Le-Maroc-passe-sur-GMT%2B1-a-partir-de-dim - -# From Petr Machata (2011-03-30): -# They have it written in English here: -# http://www.map.ma/eng/sections/home/morocco_to_spring_fo/view -# -# It says there that "Morocco will resume its standard time on July 31, -# 2011 at midnight." Now they don't say whether they mean midnight of -# wall clock time (i.e. 11pm UTC), but that's what I would assume. It has -# also been like that in the past. - -# From Alexander Krivenyshev (2012-03-09): -# According to Infomédiaire web site from Morocco (infomediaire.ma), -# on March 9, 2012, (in French) Heure légale: -# Le Maroc adopte officiellement l'heure d'été -# http://www.infomediaire.ma/news/maroc/heure-l%C3%A9gale-le-maroc-adopte-officiellement-lheure-d%C3%A9t%C3%A9 -# Governing Council adopted draft decree, that Morocco DST starts on -# the last Sunday of March (March 25, 2012) and ends on -# last Sunday of September (September 30, 2012) -# except the month of Ramadan. -# or (brief) -# http://www.worldtimezone.com/dst_news/dst_news_morocco06.html - -# From Arthur David Olson (2012-03-10): -# The infomediaire.ma source indicates that the system is to be in -# effect every year. It gives 03H00 as the "fall back" time of day; -# it lacks a "spring forward" time of day; assume 2:00 XXX. -# Wait on specifying the Ramadan exception for details about -# start date, start time of day, end date, and end time of day XXX. - -# From Christophe Tropamer (2012-03-16): -# Seen Morocco change again: -# http://www.le2uminutes.com/actualite.php -# "...à partir du dernier dimanche d'avril et non fins mars, -# comme annoncé précédemment." - -# From Milamber Space Network (2012-07-17): -# The official return to GMT is announced by the Moroccan government: -# http://www.mmsp.gov.ma/fr/actualites.aspx?id=288 [in French] -# -# Google translation, lightly edited: -# Back to the standard time of the Kingdom (GMT) -# Pursuant to Decree No. 2-12-126 issued on 26 Jumada (I) 1433 (April 18, -# 2012) and in accordance with the order of Mr. President of the -# Government No. 3-47-12 issued on 24 Sha'ban (11 July 2012), the Ministry -# of Public Service and Administration Modernization announces the return -# of the legal time of the Kingdom (GMT) from Friday, July 20, 2012 until -# Monday, August 20, 2012. So the time will be delayed by 60 minutes from -# 3:00 am Friday, July 20, 2012 and will again be advanced by 60 minutes -# August 20, 2012 from 2:00 am. - -# From Paul Eggert (2013-03-06): -# Morocco's daylight-saving transitions due to Ramadan seem to be -# announced a bit in advance. On 2012-07-11 the Moroccan government -# announced that year's Ramadan daylight-saving transitions would be -# 2012-07-20 and 2012-08-20; see -# http://www.mmsp.gov.ma/fr/actualites.aspx?id=288 - -# From Andrew Paprocki (2013-07-02): -# Morocco announced that the year's Ramadan daylight-savings -# transitions would be 2013-07-07 and 2013-08-10; see: -# http://www.maroc.ma/en/news/morocco-suspends-daylight-saving-time-july-7-aug10 - -# From Steffen Thorsen (2013-09-28): -# Morocco extends DST by one month, on very short notice, just 1 day -# before it was going to end. There is a new decree (2.13.781) for -# this, where DST from now on goes from last Sunday of March at 02:00 -# to last Sunday of October at 03:00, similar to EU rules. Official -# source (French): -# http://www.maroc.gov.ma/fr/actualites/lhoraire-dete-gmt1-maintenu-jusquau-27-octobre-2013 -# Another source (specifying the time for start and end in the decree): -# http://www.lemag.ma/Heure-d-ete-au-Maroc-jusqu-au-27-octobre_a75620.html - -# From Sebastien Willemijns (2014-03-18): -# http://www.afriquinfos.com/articles/2014/3/18/maroc-heure-dete-avancez-tous-horloges-247891.asp - -# From Milamber Space Network (2014-06-05): -# The Moroccan government has recently announced that the country will return -# to standard time at 03:00 on Saturday, June 28, 2014 local time.... DST -# will resume again at 02:00 on Saturday, August 2, 2014.... -# http://www.mmsp.gov.ma/fr/actualites.aspx?id=586 - -# From Milamber (2015-06-08): -# (Google Translation) The hour will thus be delayed 60 minutes -# Sunday, June 14 at 3:00, the ministry said in a statement, adding -# that the time will be advanced again 60 minutes Sunday, July 19, -# 2015 at 2:00. The move comes under 2.12.126 Decree of 26 Jumada I -# 1433 (18 April 2012) and the decision of the Head of Government of -# 16 N. 3-29-15 Chaaban 1435 (4 June 2015). -# Source (french): -# https://lnt.ma/le-maroc-reculera-dune-heure-le-dimanche-14-juin/ -# -# From Milamber (2015-06-09): -# http://www.mmsp.gov.ma/fr/actualites.aspx?id=863 -# -# From Michael Deckers (2015-06-09): -# [The gov.ma announcement] would (probably) make the switch on 2015-07-19 go -# from 03:00 to 04:00 rather than from 02:00 to 03:00, as in the patch.... -# I think the patch is correct and the quoted text is wrong; the text in -# agrees -# with the patch. - -# From Paul Eggert (2015-06-08): -# For now, guess that later spring and fall transitions will use 2015's rules, -# and guess that Morocco will switch to standard time at 03:00 the last -# Sunday before Ramadan, and back to DST at 02:00 the first Sunday after -# Ramadan. To implement this, transition dates for 2016 through 2037 were -# determined by running the following program under GNU Emacs 24.3, with the -# results integrated by hand into the table below. -# (let ((islamic-year 1437)) -# (require 'cal-islam) -# (while (< islamic-year 1460) -# (let ((a (calendar-islamic-to-absolute (list 9 1 islamic-year))) -# (b (calendar-islamic-to-absolute (list 10 1 islamic-year))) -# (sunday 0)) -# (while (/= sunday (mod (setq a (1- a)) 7))) -# (while (/= sunday (mod b 7)) -# (setq b (1+ b))) -# (setq a (calendar-gregorian-from-absolute a)) -# (setq b (calendar-gregorian-from-absolute b)) -# (insert -# (format -# (concat "Rule\tMorocco\t%d\tonly\t-\t%s\t%2d\t 3:00\t0\t-\n" -# "Rule\tMorocco\t%d\tonly\t-\t%s\t%2d\t 2:00\t1:00\tS\n") -# (car (cdr (cdr a))) (calendar-month-name (car a) t) (car (cdr a)) -# (car (cdr (cdr b))) (calendar-month-name (car b) t) (car (cdr b))))) -# (setq islamic-year (+ 1 islamic-year)))) - -# RULE NAME FROM TO TYPE IN ON AT SAVE LETTER/S - -Rule Morocco 1939 only - Sep 12 0:00 1:00 S -Rule Morocco 1939 only - Nov 19 0:00 0 - -Rule Morocco 1940 only - Feb 25 0:00 1:00 S -Rule Morocco 1945 only - Nov 18 0:00 0 - -Rule Morocco 1950 only - Jun 11 0:00 1:00 S -Rule Morocco 1950 only - Oct 29 0:00 0 - -Rule Morocco 1967 only - Jun 3 12:00 1:00 S -Rule Morocco 1967 only - Oct 1 0:00 0 - -Rule Morocco 1974 only - Jun 24 0:00 1:00 S -Rule Morocco 1974 only - Sep 1 0:00 0 - -Rule Morocco 1976 1977 - May 1 0:00 1:00 S -Rule Morocco 1976 only - Aug 1 0:00 0 - -Rule Morocco 1977 only - Sep 28 0:00 0 - -Rule Morocco 1978 only - Jun 1 0:00 1:00 S -Rule Morocco 1978 only - Aug 4 0:00 0 - -Rule Morocco 2008 only - Jun 1 0:00 1:00 S -Rule Morocco 2008 only - Sep 1 0:00 0 - -Rule Morocco 2009 only - Jun 1 0:00 1:00 S -Rule Morocco 2009 only - Aug 21 0:00 0 - -Rule Morocco 2010 only - May 2 0:00 1:00 S -Rule Morocco 2010 only - Aug 8 0:00 0 - -Rule Morocco 2011 only - Apr 3 0:00 1:00 S -Rule Morocco 2011 only - Jul 31 0:00 0 - -Rule Morocco 2012 2013 - Apr lastSun 2:00 1:00 S -Rule Morocco 2012 only - Jul 20 3:00 0 - -Rule Morocco 2012 only - Aug 20 2:00 1:00 S -Rule Morocco 2012 only - Sep 30 3:00 0 - -Rule Morocco 2013 only - Jul 7 3:00 0 - -Rule Morocco 2013 only - Aug 10 2:00 1:00 S -Rule Morocco 2013 max - Oct lastSun 3:00 0 - -Rule Morocco 2014 2021 - Mar lastSun 2:00 1:00 S -Rule Morocco 2014 only - Jun 28 3:00 0 - -Rule Morocco 2014 only - Aug 2 2:00 1:00 S -Rule Morocco 2015 only - Jun 14 3:00 0 - -Rule Morocco 2015 only - Jul 19 2:00 1:00 S -Rule Morocco 2016 only - Jun 5 3:00 0 - -Rule Morocco 2016 only - Jul 10 2:00 1:00 S -Rule Morocco 2017 only - May 21 3:00 0 - -Rule Morocco 2017 only - Jul 2 2:00 1:00 S -Rule Morocco 2018 only - May 13 3:00 0 - -Rule Morocco 2018 only - Jun 17 2:00 1:00 S -Rule Morocco 2019 only - May 5 3:00 0 - -Rule Morocco 2019 only - Jun 9 2:00 1:00 S -Rule Morocco 2020 only - Apr 19 3:00 0 - -Rule Morocco 2020 only - May 24 2:00 1:00 S -Rule Morocco 2021 only - Apr 11 3:00 0 - -Rule Morocco 2021 only - May 16 2:00 1:00 S -Rule Morocco 2022 only - May 8 2:00 1:00 S -Rule Morocco 2023 only - Apr 23 2:00 1:00 S -Rule Morocco 2024 only - Apr 14 2:00 1:00 S -Rule Morocco 2025 only - Apr 6 2:00 1:00 S -Rule Morocco 2026 max - Mar lastSun 2:00 1:00 S -Rule Morocco 2036 only - Oct 19 3:00 0 - -Rule Morocco 2037 only - Oct 4 3:00 0 - - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Casablanca -0:30:20 - LMT 1913 Oct 26 - 0:00 Morocco WE%sT 1984 Mar 16 - 1:00 - CET 1986 - 0:00 Morocco WE%sT - -# Western Sahara -# -# From Gwillim Law (2013-10-22): -# A correspondent who is usually well informed about time zone matters -# ... says that Western Sahara observes daylight saving time, just as -# Morocco does. -# -# From Paul Eggert (2013-10-23): -# Assume that this has been true since Western Sahara switched to GMT, -# since most of it was then controlled by Morocco. - -Zone Africa/El_Aaiun -0:52:48 - LMT 1934 Jan # El Aaiún - -1:00 - -01 1976 Apr 14 - 0:00 Morocco WE%sT - -# Mozambique -# -# Shanks gives 1903-03-01 for the transition to CAT. -# Perhaps the 1911-05-26 Portuguese decree -# https://dre.pt/pdf1sdip/1911/05/12500/23132313.pdf -# merely made it official? -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Maputo 2:10:20 - LMT 1903 Mar - 2:00 - CAT -Link Africa/Maputo Africa/Blantyre # Malawi -Link Africa/Maputo Africa/Bujumbura # Burundi -Link Africa/Maputo Africa/Gaborone # Botswana -Link Africa/Maputo Africa/Harare # Zimbabwe -Link Africa/Maputo Africa/Kigali # Rwanda -Link Africa/Maputo Africa/Lubumbashi # E Dem. Rep. of Congo -Link Africa/Maputo Africa/Lusaka # Zambia - - -# Namibia - -# From Arthur David Olson (2017-08-09): -# The text of the "Namibia Time Act, 1994" is available online at -# www.lac.org.na/laws/1994/811.pdf -# and includes this nugget: -# Notwithstanding the provisions of subsection (2) of section 1, the -# first winter period after the commencement of this Act shall -# commence at OOhOO on Monday 21 March 1994 and shall end at 02h00 on -# Sunday 4 September 1994. - -# From Michael Deckers (2017-04-06): -# ... both summer and winter time are called "standard" -# (which differs from the use in Ireland) ... - -# From Petronella Sibeene (2007-03-30): -# http://allafrica.com/stories/200703300178.html -# While the entire country changes its time, Katima Mulilo and other -# settlements in Caprivi unofficially will not because the sun there -# rises and sets earlier compared to other regions. Chief of -# Forecasting Riaan van Zyl explained that the far eastern parts of -# the country are close to 40 minutes earlier in sunrise than the rest -# of the country. -# -# From Paul Eggert (2017-02-22): -# Although the Zambezi Region (formerly known as Caprivi) informally -# observes Botswana time, we have no details about historical practice. -# In the meantime people there can use Africa/Gaborone. -# See: Immanuel S. The Namibian. 2017-02-23. -# https://www.namibian.com.na/51480/read/Time-change-divides-lawmakers - -# From Steffen Thorsen (2017-08-09): -# Namibia is going to change their time zone to what is now their DST: -# https://www.newera.com.na/2017/02/23/namibias-winter-time-might-be-repealed/ -# This video is from the government decision: -# https://www.nbc.na/news/na-passes-namibia-time-bill-repealing-1994-namibia-time-act.8665 -# We have made the assumption so far that they will change their time zone at -# the same time they would normally start DST, the first Sunday in September: -# https://www.timeanddate.com/news/time/namibia-new-time-zone.html - -# From Paul Eggert (2017-04-09): -# Before the change, summer and winter time were both standard time legally. -# However in common parlance, winter time was considered to be DST. See, e.g.: -# http://www.nbc.na/news/namibias-winter-time-could-be-scrapped.2706 -# https://zone.my.na/news/times-are-changing-in-namibia -# https://www.newera.com.na/2017/02/23/namibias-winter-time-might-be-repealed/ -# Use plain "WAT" and "CAT" for the time zone abbreviations, to be compatible -# with Namibia's neighbors. - -# RULE NAME FROM TO TYPE IN ON AT SAVE LETTER/S -# Vanguard section, for zic and other parsers that support negative DST. -Rule Namibia 1994 only - Mar 21 0:00 -1:00 WAT -Rule Namibia 1994 2017 - Sep Sun>=1 2:00 0 CAT -Rule Namibia 1995 2017 - Apr Sun>=1 2:00 -1:00 WAT -# Rearguard section, for parsers that do not support negative DST. -#Rule Namibia 1994 only - Mar 21 0:00 0 WAT -#Rule Namibia 1994 2017 - Sep Sun>=1 2:00 1:00 CAT -#Rule Namibia 1995 2017 - Apr Sun>=1 2:00 0 WAT -# End of rearguard section. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Windhoek 1:08:24 - LMT 1892 Feb 8 - 1:30 - +0130 1903 Mar - 2:00 - SAST 1942 Sep 20 2:00 - 2:00 1:00 SAST 1943 Mar 21 2:00 - 2:00 - SAST 1990 Mar 21 # independence -# Vanguard section, for zic and other parsers that support negative DST. - 2:00 Namibia %s -# Rearguard section, for parsers that do not support negative DST. -# 2:00 - CAT 1994 Mar 21 0:00 -# From Paul Eggert (2017-04-07): -# The official date of the 2017 rule change was 2017-10-24. See: -# http://www.lac.org.na/laws/annoSTAT/Namibian%20Time%20Act%209%20of%202017.pdf -# 1:00 Namibia %s 2017 Oct 24 -# 2:00 - CAT -# End of rearguard section. - -# Niger -# See Africa/Lagos. - -# Nigeria -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Lagos 0:13:36 - LMT 1919 Sep - 1:00 - WAT -Link Africa/Lagos Africa/Bangui # Central African Republic -Link Africa/Lagos Africa/Brazzaville # Rep. of the Congo -Link Africa/Lagos Africa/Douala # Cameroon -Link Africa/Lagos Africa/Kinshasa # Dem. Rep. of the Congo (west) -Link Africa/Lagos Africa/Libreville # Gabon -Link Africa/Lagos Africa/Luanda # Angola -Link Africa/Lagos Africa/Malabo # Equatorial Guinea -Link Africa/Lagos Africa/Niamey # Niger -Link Africa/Lagos Africa/Porto-Novo # Benin - -# Réunion -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Indian/Reunion 3:41:52 - LMT 1911 Jun # Saint-Denis - 4:00 - +04 -# -# Crozet Islands also observes Réunion time; see the 'antarctica' file. -# -# Scattered Islands (Îles Éparses) administered from Réunion are as follows. -# The following information about them is taken from -# Îles Éparses (, 1997-07-22, -# in French; no longer available as of 1999-08-17). -# We have no info about their time zone histories. -# -# Bassas da India - uninhabited -# Europa Island - inhabited from 1905 to 1910 by two families -# Glorioso Is - inhabited until at least 1958 -# Juan de Nova - uninhabited -# Tromelin - inhabited until at least 1958 - -# Rwanda -# See Africa/Maputo. - -# St Helena -# See Africa/Abidjan. -# The other parts of the St Helena territory are similar: -# Tristan da Cunha: on GMT, say Whitman and the CIA -# Ascension: on GMT, say the USNO (1995-12-21) and the CIA -# Gough (scientific station since 1955; sealers wintered previously): -# on GMT, says the CIA -# Inaccessible, Nightingale: uninhabited - -# São Tomé and Príncipe - -# See Europe/Lisbon for info about the 1912 transition. - -# From Steffen Thorsen (2018-01-08): -# Multiple sources tell that São Tomé changed from UTC to UTC+1 as -# they entered the year 2018. -# From Michael Deckers (2018-01-08): -# the switch is from 01:00 to 02:00 ... [Decree No. 25/2017] -# http://www.mnec.gov.st/index.php/publicacoes/documentos/file/90-decreto-lei-n-25-2017 - -Zone Africa/Sao_Tome 0:26:56 - LMT 1884 - -0:36:45 - LMT 1912 Jan 1 00:00u # Lisbon MT - 0:00 - GMT 2018 Jan 1 01:00 - 1:00 - WAT - -# Senegal -# See Africa/Abidjan. - -# Seychelles -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Indian/Mahe 3:41:48 - LMT 1906 Jun # Victoria - 4:00 - +04 -# From Paul Eggert (2001-05-30): -# Aldabra, Farquhar, and Desroches, originally dependencies of the -# Seychelles, were transferred to the British Indian Ocean Territory -# in 1965 and returned to Seychelles control in 1976. We don't know -# whether this affected their time zone, so omit this for now. -# Possibly the islands were uninhabited. - -# Sierra Leone -# See Africa/Abidjan. - -# Somalia -# See Africa/Nairobi. - -# South Africa -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule SA 1942 1943 - Sep Sun>=15 2:00 1:00 - -Rule SA 1943 1944 - Mar Sun>=15 2:00 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Johannesburg 1:52:00 - LMT 1892 Feb 8 - 1:30 - SAST 1903 Mar - 2:00 SA SAST -Link Africa/Johannesburg Africa/Maseru # Lesotho -Link Africa/Johannesburg Africa/Mbabane # Swaziland -# -# Marion and Prince Edward Is -# scientific station since 1947 -# no information - -# Sudan - -# From -# Sudan News Agency (2000-01-13), -# also reported by Michaël De Beukelaer-Dossche via Steffen Thorsen: -# Clocks will be moved ahead for 60 minutes all over the Sudan as of noon -# Saturday.... This was announced Thursday by Caretaker State Minister for -# Manpower Abdul-Rahman Nur-Eddin. - -# From Ahmed Atyya, National Telecommunications Corp. (NTC), Sudan (2017-10-17): -# ... the Republic of Sudan is going to change the time zone from (GMT+3:00) -# to (GMT+ 2:00) starting from Wednesday 1 November 2017. -# -# From Paul Eggert (2017-10-18): -# A scanned copy (in Arabic) of Cabinet Resolution No. 352 for the -# year 2017 can be found as an attachment in email today from Yahia -# Abdalla of NTC, archived at: -# https://mm.icann.org/pipermail/tz/2017-October/025333.html - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Sudan 1970 only - May 1 0:00 1:00 S -Rule Sudan 1970 1985 - Oct 15 0:00 0 - -Rule Sudan 1971 only - Apr 30 0:00 1:00 S -Rule Sudan 1972 1985 - Apr lastSun 0:00 1:00 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Khartoum 2:10:08 - LMT 1931 - 2:00 Sudan CA%sT 2000 Jan 15 12:00 - 3:00 - EAT 2017 Nov 1 - 2:00 - CAT - -# South Sudan -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Juba 2:06:28 - LMT 1931 - 2:00 Sudan CA%sT 2000 Jan 15 12:00 - 3:00 - EAT - -# Swaziland -# See Africa/Johannesburg. - -# Tanzania -# See Africa/Nairobi. - -# Togo -# See Africa/Abidjan. - -# Tunisia - -# From Gwillim Law (2005-04-30): -# My correspondent, Risto Nykänen, has alerted me to another adoption of DST, -# this time in Tunisia. According to Yahoo France News -# , in a story attributed to AP -# and dated 2005-04-26, "Tunisia has decided to advance its official time by -# one hour, starting on Sunday, May 1. Henceforth, Tunisian time will be -# UTC+2 instead of UTC+1. The change will take place at 23:00 UTC next -# Saturday." (My translation) -# -# From Oscar van Vlijmen (2005-05-02): -# La Presse, the first national daily newspaper ... -# http://www.lapresse.tn/archives/archives280405/actualites/lheure.html -# ... DST for 2005: on: Sun May 1 0h standard time, off: Fri Sept. 30, -# 1h standard time. -# -# From Atef Loukil (2006-03-28): -# The daylight saving time will be the same each year: -# Beginning : the last Sunday of March at 02:00 -# Ending : the last Sunday of October at 03:00 ... -# http://www.tap.info.tn/en/index.php?option=com_content&task=view&id=1188&Itemid=50 - -# From Steffen Thorsen (2009-03-16): -# According to several news sources, Tunisia will not observe DST this year. -# (Arabic) -# http://www.elbashayer.com/?page=viewn&nid=42546 -# https://www.babnet.net/kiwidetail-15295.asp -# -# We have also confirmed this with the US embassy in Tunisia. -# We have a wrap-up about this on the following page: -# https://www.timeanddate.com/news/time/tunisia-cancels-dst-2009.html - -# From Alexander Krivenyshev (2009-03-17): -# Here is a link to Tunis Afrique Presse News Agency -# -# Standard time to be kept the whole year long (tap.info.tn): -# -# (in English) -# http://www.tap.info.tn/en/index.php?option=com_content&task=view&id=26813&Itemid=157 -# -# (in Arabic) -# http://www.tap.info.tn/ar/index.php?option=com_content&task=view&id=61240&Itemid=1 - -# From Arthur David Olson (2009-03-18): -# The Tunis Afrique Presse News Agency notice contains this: "This measure is -# due to the fact that the fasting month of Ramadan coincides with the period -# concerned by summer time. Therefore, the standard time will be kept -# unchanged the whole year long." So foregoing DST seems to be an exception -# (albeit one that may be repeated in the future). - -# From Alexander Krivenyshev (2010-03-27): -# According to some news reports Tunis confirmed not to use DST in 2010 -# -# (translation): -# "The Tunisian government has decided to abandon DST, which was scheduled on -# Sunday... -# Tunisian authorities had suspended the DST for the first time last year also -# coincided with the month of Ramadan..." -# -# (in Arabic) -# http://www.moheet.com/show_news.aspx?nid=358861&pg=1 -# http://www.almadenahnews.com/newss/news.php?c=118&id=38036 -# http://www.worldtimezone.com/dst_news/dst_news_tunis02.html - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Tunisia 1939 only - Apr 15 23:00s 1:00 S -Rule Tunisia 1939 only - Nov 18 23:00s 0 - -Rule Tunisia 1940 only - Feb 25 23:00s 1:00 S -Rule Tunisia 1941 only - Oct 6 0:00 0 - -Rule Tunisia 1942 only - Mar 9 0:00 1:00 S -Rule Tunisia 1942 only - Nov 2 3:00 0 - -Rule Tunisia 1943 only - Mar 29 2:00 1:00 S -Rule Tunisia 1943 only - Apr 17 2:00 0 - -Rule Tunisia 1943 only - Apr 25 2:00 1:00 S -Rule Tunisia 1943 only - Oct 4 2:00 0 - -Rule Tunisia 1944 1945 - Apr Mon>=1 2:00 1:00 S -Rule Tunisia 1944 only - Oct 8 0:00 0 - -Rule Tunisia 1945 only - Sep 16 0:00 0 - -Rule Tunisia 1977 only - Apr 30 0:00s 1:00 S -Rule Tunisia 1977 only - Sep 24 0:00s 0 - -Rule Tunisia 1978 only - May 1 0:00s 1:00 S -Rule Tunisia 1978 only - Oct 1 0:00s 0 - -Rule Tunisia 1988 only - Jun 1 0:00s 1:00 S -Rule Tunisia 1988 1990 - Sep lastSun 0:00s 0 - -Rule Tunisia 1989 only - Mar 26 0:00s 1:00 S -Rule Tunisia 1990 only - May 1 0:00s 1:00 S -Rule Tunisia 2005 only - May 1 0:00s 1:00 S -Rule Tunisia 2005 only - Sep 30 1:00s 0 - -Rule Tunisia 2006 2008 - Mar lastSun 2:00s 1:00 S -Rule Tunisia 2006 2008 - Oct lastSun 2:00s 0 - - -# Shanks & Pottenger give 0:09:20 for Paris Mean Time; go with Howse's -# more precise 0:09:21. -# Shanks & Pottenger say the 1911 switch was on Mar 9; go with Howse's Mar 11. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Tunis 0:40:44 - LMT 1881 May 12 - 0:09:21 - PMT 1911 Mar 11 # Paris Mean Time - 1:00 Tunisia CE%sT - -# Uganda -# See Africa/Nairobi. - -# Zambia -# Zimbabwe -# See Africa/Maputo. diff --git a/date_time/antarctica b/date_time/antarctica deleted file mode 100644 index 866cf4fc22..0000000000 --- a/date_time/antarctica +++ /dev/null @@ -1,341 +0,0 @@ -# This file is in the public domain, so clarified as of -# 2009-05-17 by Arthur David Olson. - -# From Paul Eggert (1999-11-15): -# To keep things manageable, we list only locations occupied year-round; see -# COMNAP - Stations and Bases -# http://www.comnap.aq/comnap/comnap.nsf/P/Stations/ -# and -# Summary of the Peri-Antarctic Islands (1998-07-23) -# http://www.spri.cam.ac.uk/bob/periant.htm -# for information. -# Unless otherwise specified, we have no time zone information. - -# FORMAT is '-00' and GMTOFF is 0 for locations while uninhabited. - -# Argentina - year-round bases -# Belgrano II, Confin Coast, -770227-0343737, since 1972-02-05 -# Carlini, Potter Cove, King George Island, -6414-0602320, since 1982-01 -# Esperanza, Hope Bay, -6323-05659, since 1952-12-17 -# Marambio, -6414-05637, since 1969-10-29 -# Orcadas, Laurie I, -6016-04444, since 1904-02-22 -# San Martín, Barry I, -6808-06706, since 1951-03-21 -# (except 1960-03 / 1976-03-21) - -# Australia - territories -# Heard Island, McDonald Islands (uninhabited) -# previously sealers and scientific personnel wintered -# Margaret Turner reports -# https://web.archive.org/web/20021204222245/http://www.dstc.qut.edu.au/DST/marg/daylight.html -# (1999-09-30) that they're UT +05, with no DST; -# presumably this is when they have visitors. -# -# year-round bases -# Casey, Bailey Peninsula, -6617+11032, since 1969 -# Davis, Vestfold Hills, -6835+07759, since 1957-01-13 -# (except 1964-11 - 1969-02) -# Mawson, Holme Bay, -6736+06253, since 1954-02-13 - -# From Steffen Thorsen (2009-03-11): -# Three Australian stations in Antarctica have changed their time zone: -# Casey moved from UTC+8 to UTC+11 -# Davis moved from UTC+7 to UTC+5 -# Mawson moved from UTC+6 to UTC+5 -# The changes occurred on 2009-10-18 at 02:00 (local times). -# -# Government source: (Australian Antarctic Division) -# http://www.aad.gov.au/default.asp?casid=37079 -# -# We have more background information here: -# https://www.timeanddate.com/news/time/antarctica-new-times.html - -# From Steffen Thorsen (2010-03-10): -# We got these changes from the Australian Antarctic Division: ... -# -# - Casey station reverted to its normal time of UTC+8 on 5 March 2010. -# The change to UTC+11 is being considered as a regular summer thing but -# has not been decided yet. -# -# - Davis station will revert to its normal time of UTC+7 at 10 March 2010 -# 20:00 UTC. -# -# - Mawson station stays on UTC+5. -# -# Background: -# https://www.timeanddate.com/news/time/antartica-time-changes-2010.html - -# From Steffen Thorsen (2016-10-28): -# Australian Antarctica Division informed us that Casey changed time -# zone to UTC+11 in "the morning of 22nd October 2016". - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Antarctica/Casey 0 - -00 1969 - 8:00 - +08 2009 Oct 18 2:00 - 11:00 - +11 2010 Mar 5 2:00 - 8:00 - +08 2011 Oct 28 2:00 - 11:00 - +11 2012 Feb 21 17:00u - 8:00 - +08 2016 Oct 22 - 11:00 - +11 2018 Mar 11 4:00 - 8:00 - +08 -Zone Antarctica/Davis 0 - -00 1957 Jan 13 - 7:00 - +07 1964 Nov - 0 - -00 1969 Feb - 7:00 - +07 2009 Oct 18 2:00 - 5:00 - +05 2010 Mar 10 20:00u - 7:00 - +07 2011 Oct 28 2:00 - 5:00 - +05 2012 Feb 21 20:00u - 7:00 - +07 -Zone Antarctica/Mawson 0 - -00 1954 Feb 13 - 6:00 - +06 2009 Oct 18 2:00 - 5:00 - +05 -# References: -# Casey Weather (1998-02-26) -# http://www.antdiv.gov.au/aad/exop/sfo/casey/casey_aws.html -# Davis Station, Antarctica (1998-02-26) -# http://www.antdiv.gov.au/aad/exop/sfo/davis/video.html -# Mawson Station, Antarctica (1998-02-25) -# http://www.antdiv.gov.au/aad/exop/sfo/mawson/video.html - -# Belgium - year-round base -# Princess Elisabeth, Queen Maud Land, -713412+0231200, since 2007 - -# Brazil - year-round base -# Ferraz, King George Island, -6205+05824, since 1983/4 - -# Bulgaria - year-round base -# St. Kliment Ohridski, Livingston Island, -623829-0602153, since 1988 - -# Chile - year-round bases and towns -# Escudero, South Shetland Is, -621157-0585735, since 1994 -# Frei Montalva, King George Island, -6214-05848, since 1969-03-07 -# O'Higgins, Antarctic Peninsula, -6319-05704, since 1948-02 -# Prat, -6230-05941 -# Villa Las Estrellas (a town), around the Frei base, since 1984-04-09 -# These locations employ Region of Magallanes time; use -# TZ='America/Punta_Arenas'. - -# China - year-round bases -# Great Wall, King George Island, -6213-05858, since 1985-02-20 -# Zhongshan, Larsemann Hills, Prydz Bay, -6922+07623, since 1989-02-26 - -# France - year-round bases (also see "France & Italy") -# -# From Antoine Leca (1997-01-20): -# Time data entries are from Nicole Pailleau at the IFRTP -# (French Institute for Polar Research and Technology). -# She confirms that French Southern Territories and Terre Adélie bases -# don't observe daylight saving time, even if Terre Adélie supplies came -# from Tasmania. -# -# French Southern Territories with year-round inhabitants -# -# Alfred Faure, Possession Island, Crozet Islands, -462551+0515152, since 1964; -# sealing & whaling stations operated variously 1802/1911+; -# see Indian/Reunion. -# -# Martin-de-Viviès, Amsterdam Island, -374105+0773155, since 1950 -# Port-aux-Français, Kerguelen Islands, -492110+0701303, since 1951; -# whaling & sealing station operated 1908/1914, 1920/1929, and 1951/1956 -# -# St Paul Island - near Amsterdam, uninhabited -# fishing stations operated variously 1819/1931 -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Indian/Kerguelen 0 - -00 1950 # Port-aux-Français - 5:00 - +05 -# -# year-round base in the main continent -# Dumont d'Urville, Île des Pétrels, -6640+14001, since 1956-11 -# (2005-12-05) -# -# Another base at Port-Martin, 50km east, began operation in 1947. -# It was destroyed by fire on 1952-01-14. -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Antarctica/DumontDUrville 0 - -00 1947 - 10:00 - +10 1952 Jan 14 - 0 - -00 1956 Nov - 10:00 - +10 - -# France & Italy - year-round base -# Concordia, -750600+1232000, since 2005 - -# Germany - year-round base -# Neumayer III, -704080-0081602, since 2009 - -# India - year-round bases -# Bharati, -692428+0761114, since 2012 -# Maitri, -704558+0114356, since 1989 - -# Italy - year-round base (also see "France & Italy") -# Zuchelli, Terra Nova Bay, -744140+1640647, since 1986 - -# Japan - year-round bases -# Syowa (also known as Showa), -690022+0393524, since 1957 -# -# From Hideyuki Suzuki (1999-02-06): -# In all Japanese stations, +0300 is used as the standard time. -# -# Syowa station, which is the first antarctic station of Japan, -# was established on 1957-01-29. Since Syowa station is still the main -# station of Japan, it's appropriate for the principal location. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Antarctica/Syowa 0 - -00 1957 Jan 29 - 3:00 - +03 -# See: -# NIPR Antarctic Research Activities (1999-08-17) -# http://www.nipr.ac.jp/english/ara01.html - -# S Korea - year-round base -# Jang Bogo, Terra Nova Bay, -743700+1641205 since 2014 -# King Sejong, King George Island, -6213-05847, since 1988 - -# New Zealand - claims -# Balleny Islands (never inhabited) -# Scott Island (never inhabited) -# -# year-round base -# Scott Base, Ross Island, since 1957-01. -# See Pacific/Auckland. - -# Norway - territories -# Bouvet (never inhabited) -# -# claims -# Peter I Island (never inhabited) -# -# year-round base -# Troll, Queen Maud Land, -720041+0023206, since 2005-02-12 -# -# From Paul-Inge Flakstad (2014-03-10): -# I recently had a long dialog about this with the developer of timegenie.com. -# In the absence of specific dates, he decided to choose some likely ones: -# GMT +1 - From March 1 to the last Sunday in March -# GMT +2 - From the last Sunday in March until the last Sunday in October -# GMT +1 - From the last Sunday in October until November 7 -# GMT +0 - From November 7 until March 1 -# The dates for switching to and from UTC+0 will probably not be absolutely -# correct, but they should be quite close to the actual dates. -# -# From Paul Eggert (2014-03-21): -# The CET-switching Troll rules require zic from tz 2014b or later, so as -# suggested by Bengt-Inge Larsson comment them out for now, and approximate -# with only UTC and CEST. Uncomment them when 2014b is more prevalent. -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -#Rule Troll 2005 max - Mar 1 1:00u 1:00 +01 -Rule Troll 2005 max - Mar lastSun 1:00u 2:00 +02 -#Rule Troll 2005 max - Oct lastSun 1:00u 1:00 +01 -#Rule Troll 2004 max - Nov 7 1:00u 0:00 +00 -# Remove the following line when uncommenting the above '#Rule' lines. -Rule Troll 2004 max - Oct lastSun 1:00u 0:00 +00 -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Antarctica/Troll 0 - -00 2005 Feb 12 - 0:00 Troll %s - -# Poland - year-round base -# Arctowski, King George Island, -620945-0582745, since 1977 - -# Romania - year-bound base -# Law-Racoviță, Larsemann Hills, -692319+0762251, since 1986 - -# Russia - year-round bases -# Bellingshausen, King George Island, -621159-0585337, since 1968-02-22 -# Mirny, Davis coast, -6633+09301, since 1956-02 -# Molodezhnaya, Alasheyev Bay, -6740+04551, -# year-round from 1962-02 to 1999-07-01 -# Novolazarevskaya, Queen Maud Land, -7046+01150, -# year-round from 1960/61 to 1992 - -# Vostok, since 1957-12-16, temporarily closed 1994-02/1994-11 -# From Craig Mundell (1994-12-15): -# http://quest.arc.nasa.gov/antarctica/QA/computers/Directions,Time,ZIP -# Vostok, which is one of the Russian stations, is set on the same -# time as Moscow, Russia. -# -# From Lee Hotz (2001-03-08): -# I queried the folks at Columbia who spent the summer at Vostok and this is -# what they had to say about time there: -# "in the US Camp (East Camp) we have been on New Zealand (McMurdo) -# time, which is 12 hours ahead of GMT. The Russian Station Vostok was -# 6 hours behind that (although only 2 miles away, i.e. 6 hours ahead -# of GMT). This is a time zone I think two hours east of Moscow. The -# natural time zone is in between the two: 8 hours ahead of GMT." -# -# From Paul Eggert (2001-05-04): -# This seems to be hopelessly confusing, so I asked Lee Hotz about it -# in person. He said that some Antarctic locations set their local -# time so that noon is the warmest part of the day, and that this -# changes during the year and does not necessarily correspond to mean -# solar noon. So the Vostok time might have been whatever the clocks -# happened to be during their visit. So we still don't really know what time -# it is at Vostok. But we'll guess +06. -# -Zone Antarctica/Vostok 0 - -00 1957 Dec 16 - 6:00 - +06 - -# S Africa - year-round bases -# Marion Island, -4653+03752 -# SANAE IV, Vesleskarvet, Queen Maud Land, -714022-0025026, since 1997 - -# Ukraine - year-round base -# Vernadsky (formerly Faraday), Galindez Island, -651445-0641526, since 1954 - -# United Kingdom -# -# British Antarctic Territories (BAT) claims -# South Orkney Islands -# scientific station from 1903 -# whaling station at Signy I 1920/1926 -# South Shetland Islands -# -# year-round bases -# Bird Island, South Georgia, -5400-03803, since 1983 -# Deception Island, -6259-06034, whaling station 1912/1931, -# scientific station 1943/1967, -# previously sealers and a scientific expedition wintered by accident, -# and a garrison was deployed briefly -# Halley, Coates Land, -7535-02604, since 1956-01-06 -# Halley is on a moving ice shelf and is periodically relocated -# so that it is never more than 10km from its nominal location. -# Rothera, Adelaide Island, -6734-6808, since 1976-12-01 -# -# From Paul Eggert (2002-10-22) -# says Rothera is -03 all year. -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Antarctica/Rothera 0 - -00 1976 Dec 1 - -3:00 - -03 - -# Uruguay - year round base -# Artigas, King George Island, -621104-0585107 - -# USA - year-round bases -# -# Palmer, Anvers Island, since 1965 (moved 2 miles in 1968) -# See 'southamerica' for Antarctica/Palmer, since it uses South American DST. -# -# McMurdo Station, Ross Island, since 1955-12 -# Amundsen-Scott South Pole Station, continuously occupied since 1956-11-20 -# -# From Chris Carrier (1996-06-27): -# Siple, the first commander of the South Pole station, -# stated that he would have liked to have kept GMT at the station, -# but that he found it more convenient to keep GMT+12 -# as supplies for the station were coming from McMurdo Sound, -# which was on GMT+12 because New Zealand was on GMT+12 all year -# at that time (1957). (Source: Siple's book 90 Degrees South.) -# -# From Susan Smith -# http://www.cybertours.com/whs/pole10.html -# (1995-11-13 16:24:56 +1300, no longer available): -# We use the same time as McMurdo does. -# And they use the same time as Christchurch, NZ does.... -# One last quirk about South Pole time. -# All the electric clocks are usually wrong. -# Something about the generators running at 60.1hertz or something -# makes all of the clocks run fast. So every couple of days, -# we have to go around and set them back 5 minutes or so. -# Maybe if we let them run fast all of the time, we'd get to leave here sooner!! -# -# See 'australasia' for Antarctica/McMurdo. diff --git a/date_time/asia b/date_time/asia deleted file mode 100644 index 3d30864772..0000000000 --- a/date_time/asia +++ /dev/null @@ -1,3163 +0,0 @@ -# This file is in the public domain, so clarified as of -# 2009-05-17 by Arthur David Olson. - -# This file is by no means authoritative; if you think you know better, -# go ahead and edit the file (and please send any changes to -# tz@iana.org for general use in the future). For more, please see -# the file CONTRIBUTING in the tz distribution. - -# From Paul Eggert (2017-01-13): -# -# Unless otherwise specified, the source for data through 1990 is: -# Thomas G. Shanks and Rique Pottenger, The International Atlas (6th edition), -# San Diego: ACS Publications, Inc. (2003). -# Unfortunately this book contains many errors and cites no sources. -# -# Many years ago Gwillim Law wrote that a good source -# for time zone data was the International Air Transport -# Association's Standard Schedules Information Manual (IATA SSIM), -# published semiannually. Law sent in several helpful summaries -# of the IATA's data after 1990. Except where otherwise noted, -# IATA SSIM is the source for entries after 1990. -# -# Another source occasionally used is Edward W. Whitman, World Time Differences, -# Whitman Publishing Co, 2 Niagara Av, Ealing, London (undated), which -# I found in the UCLA library. -# -# For data circa 1899, a common source is: -# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94. -# https://www.jstor.org/stable/1774359 -# -# For Russian data circa 1919, a source is: -# Byalokoz EL. New Counting of Time in Russia since July 1, 1919. -# (See the 'europe' file for a fuller citation.) -# -# A reliable and entertaining source about time zones is -# Derek Howse, Greenwich time and longitude, Philip Wilson Publishers (1997). -# -# The following alphabetic abbreviations appear in these tables: -# std dst -# LMT Local Mean Time -# 2:00 EET EEST Eastern European Time -# 2:00 IST IDT Israel -# 5:30 IST India -# 7:00 WIB west Indonesia (Waktu Indonesia Barat) -# 8:00 WITA central Indonesia (Waktu Indonesia Tengah) -# 8:00 CST China -# 8:30 KST KDT Korea when at +0830 -# 9:00 WIT east Indonesia (Waktu Indonesia Timur) -# 9:00 JST JDT Japan -# 9:00 KST KDT Korea when at +09 -# 9:30 ACST Australian Central Standard Time -# Otherwise, these tables typically use numeric abbreviations like +03 -# and +0330 for integer hour and minute UT offsets. Although earlier -# editions invented alphabetic time zone abbreviations for every -# offset, this did not reflect common practice. -# -# See the 'europe' file for Russia and Turkey in Asia. - -# From Guy Harris: -# Incorporates data for Singapore from Robert Elz' asia 1.1, as well as -# additional information from Tom Yap, Sun Microsystems Intercontinental -# Technical Support (including a page from the Official Airline Guide - -# Worldwide Edition). - -############################################################################### - -# These rules are stolen from the 'europe' file. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule EUAsia 1981 max - Mar lastSun 1:00u 1:00 S -Rule EUAsia 1979 1995 - Sep lastSun 1:00u 0 - -Rule EUAsia 1996 max - Oct lastSun 1:00u 0 - -Rule E-EurAsia 1981 max - Mar lastSun 0:00 1:00 - -Rule E-EurAsia 1979 1995 - Sep lastSun 0:00 0 - -Rule E-EurAsia 1996 max - Oct lastSun 0:00 0 - -Rule RussiaAsia 1981 1984 - Apr 1 0:00 1:00 - -Rule RussiaAsia 1981 1983 - Oct 1 0:00 0 - -Rule RussiaAsia 1984 1995 - Sep lastSun 2:00s 0 - -Rule RussiaAsia 1985 2010 - Mar lastSun 2:00s 1:00 - -Rule RussiaAsia 1996 2010 - Oct lastSun 2:00s 0 - - -# Afghanistan -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Kabul 4:36:48 - LMT 1890 - 4:00 - +04 1945 - 4:30 - +0430 - -# Armenia -# From Paul Eggert (2006-03-22): -# Shanks & Pottenger have Yerevan switching to 3:00 (with Russian DST) -# in spring 1991, then to 4:00 with no DST in fall 1995, then -# readopting Russian DST in 1997. Go with Shanks & Pottenger, even -# when they disagree with others. Edgar Der-Danieliantz -# reported (1996-05-04) that Yerevan probably wouldn't use DST -# in 1996, though it did use DST in 1995. IATA SSIM (1991/1998) reports that -# Armenia switched from 3:00 to 4:00 in 1998 and observed DST after 1991, -# but started switching at 3:00s in 1998. - -# From Arthur David Olson (2011-06-15): -# While Russia abandoned DST in 2011, Armenia may choose to -# follow Russia's "old" rules. - -# From Alexander Krivenyshev (2012-02-10): -# According to News Armenia, on Feb 9, 2012, -# http://newsarmenia.ru/society/20120209/42609695.html -# -# The Armenia National Assembly adopted final reading of Amendments to the -# Law "On procedure of calculation time on the territory of the Republic of -# Armenia" according to which Armenia [is] abolishing Daylight Saving Time. -# or -# (brief) -# http://www.worldtimezone.com/dst_news/dst_news_armenia03.html -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Armenia 2011 only - Mar lastSun 2:00s 1:00 - -Rule Armenia 2011 only - Oct lastSun 2:00s 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Yerevan 2:58:00 - LMT 1924 May 2 - 3:00 - +03 1957 Mar - 4:00 RussiaAsia +04/+05 1991 Mar 31 2:00s - 3:00 RussiaAsia +03/+04 1995 Sep 24 2:00s - 4:00 - +04 1997 - 4:00 RussiaAsia +04/+05 2011 - 4:00 Armenia +04/+05 - -# Azerbaijan - -# From Rustam Aliyev of the Azerbaijan Internet Forum (2005-10-23): -# According to the resolution of Cabinet of Ministers, 1997 -# From Paul Eggert (2015-09-17): It was Resolution No. 21 (1997-03-17). -# http://code.az/files/daylight_res.pdf - -# From Steffen Thorsen (2016-03-17): -# ... the Azerbaijani Cabinet of Ministers has cancelled switching to -# daylight saving time.... -# https://www.azernews.az/azerbaijan/94137.html -# http://vestnikkavkaza.net/news/Azerbaijani-Cabinet-of-Ministers-cancels-daylight-saving-time.html -# http://en.apa.az/xeber_azerbaijan_abolishes_daylight_savings_ti_240862.html - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Azer 1997 2015 - Mar lastSun 4:00 1:00 - -Rule Azer 1997 2015 - Oct lastSun 5:00 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Baku 3:19:24 - LMT 1924 May 2 - 3:00 - +03 1957 Mar - 4:00 RussiaAsia +04/+05 1991 Mar 31 2:00s - 3:00 RussiaAsia +03/+04 1992 Sep lastSun 2:00s - 4:00 - +04 1996 - 4:00 EUAsia +04/+05 1997 - 4:00 Azer +04/+05 - -# Bahrain -# See Asia/Qatar. - -# Bangladesh -# From Alexander Krivenyshev (2009-05-13): -# According to newspaper Asian Tribune (May 6, 2009) Bangladesh may introduce -# Daylight Saving Time from June 16 to Sept 30 -# -# Bangladesh to introduce daylight saving time likely from June 16 -# http://www.asiantribune.com/?q=node/17288 -# http://www.worldtimezone.com/dst_news/dst_news_bangladesh02.html -# -# "... Bangladesh government has decided to switch daylight saving time from -# June -# 16 till September 30 in a bid to ensure maximum use of daylight to cope with -# crippling power crisis. " -# -# The switch will remain in effect from June 16 to Sept 30 (2009) but if -# implemented the next year, it will come in force from April 1, 2010 - -# From Steffen Thorsen (2009-06-02): -# They have finally decided now, but changed the start date to midnight between -# the 19th and 20th, and they have not set the end date yet. -# -# Some sources: -# https://in.reuters.com/article/southAsiaNews/idINIndia-40017620090601 -# http://bdnews24.com/details.php?id=85889&cid=2 -# -# Our wrap-up: -# https://www.timeanddate.com/news/time/bangladesh-daylight-saving-2009.html - -# From A. N. M. Kamrus Saadat (2009-06-15): -# Finally we've got the official mail regarding DST start time where DST start -# time is mentioned as Jun 19 2009, 23:00 from BTRC (Bangladesh -# Telecommunication Regulatory Commission). -# -# No DST end date has been announced yet. - -# From Alexander Krivenyshev (2009-09-25): -# Bangladesh won't go back to Standard Time from October 1, 2009, -# instead it will continue DST measure till the cabinet makes a fresh decision. -# -# Following report by same newspaper-"The Daily Star Friday": -# "DST change awaits cabinet decision-Clock won't go back by 1-hr from Oct 1" -# http://www.thedailystar.net/newDesign/news-details.php?nid=107021 -# http://www.worldtimezone.com/dst_news/dst_news_bangladesh04.html - -# From Steffen Thorsen (2009-10-13): -# IANS (Indo-Asian News Service) now reports: -# Bangladesh has decided that the clock advanced by an hour to make -# maximum use of daylight hours as an energy saving measure would -# "continue for an indefinite period." -# -# One of many places where it is published: -# http://www.thaindian.com/newsportal/business/bangladesh-to-continue-indefinitely-with-advanced-time_100259987.html - -# From Alexander Krivenyshev (2009-12-24): -# According to Bangladesh newspaper "The Daily Star," -# Bangladesh will change its clock back to Standard Time on Dec 31, 2009. -# -# Clock goes back 1-hr on Dec 31 night. -# http://www.thedailystar.net/newDesign/news-details.php?nid=119228 -# http://www.worldtimezone.com/dst_news/dst_news_bangladesh05.html -# -# "...The government yesterday decided to put the clock back by one hour -# on December 31 midnight and the new time will continue until March 31, -# 2010 midnight. The decision came at a cabinet meeting at the Prime -# Minister's Office last night..." - -# From Alexander Krivenyshev (2010-03-22): -# According to Bangladesh newspaper "The Daily Star," -# Cabinet cancels Daylight Saving Time -# http://www.thedailystar.net/newDesign/latest_news.php?nid=22817 -# http://www.worldtimezone.com/dst_news/dst_news_bangladesh06.html - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Dhaka 2009 only - Jun 19 23:00 1:00 - -Rule Dhaka 2009 only - Dec 31 24:00 0 - - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Dhaka 6:01:40 - LMT 1890 - 5:53:20 - HMT 1941 Oct # Howrah Mean Time? - 6:30 - +0630 1942 May 15 - 5:30 - +0530 1942 Sep - 6:30 - +0630 1951 Sep 30 - 6:00 - +06 2009 - 6:00 Dhaka +06/+07 - -# Bhutan -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Thimphu 5:58:36 - LMT 1947 Aug 15 # or Thimbu - 5:30 - +0530 1987 Oct - 6:00 - +06 - -# British Indian Ocean Territory -# Whitman and the 1995 CIA time zone map say 5:00, but the -# 1997 and later maps say 6:00. Assume the switch occurred in 1996. -# We have no information as to when standard time was introduced; -# assume it occurred in 1907, the same year as Mauritius (which -# then contained the Chagos Archipelago). -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Indian/Chagos 4:49:40 - LMT 1907 - 5:00 - +05 1996 - 6:00 - +06 - -# Brunei -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Brunei 7:39:40 - LMT 1926 Mar # Bandar Seri Begawan - 7:30 - +0730 1933 - 8:00 - +08 - -# Burma / Myanmar - -# Milne says 6:24:40 was the meridian of the time ball observatory at Rangoon. - -# From Paul Eggert (2017-04-20): -# Page 27 of Reed & Low (cited for Asia/Kolkata) says "Rangoon local time is -# used upon the railways and telegraphs of Burma, and is 6h. 24m. 47s. ahead -# of Greenwich." This refers to the period before Burma's transition to +0630, -# a transition for which Shanks is the only source. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Yangon 6:24:47 - LMT 1880 # or Rangoon - 6:24:47 - RMT 1920 # Rangoon local time - 6:30 - +0630 1942 May - 9:00 - +09 1945 May 3 - 6:30 - +0630 - -# Cambodia -# See Asia/Bangkok. - - -# China - -# From Guy Harris: -# People's Republic of China. Yes, they really have only one time zone. - -# From Bob Devine (1988-01-28): -# No they don't. See TIME mag, 1986-02-17 p.52. Even though -# China is across 4 physical time zones, before Feb 1, 1986 only the -# Peking (Beijing) time zone was recognized. Since that date, China -# has two of 'em - Peking's and Ürümqi (named after the capital of -# the Xinjiang Uyghur Autonomous Region). I don't know about DST for it. -# -# . . .I just deleted the DST table and this editor makes it too -# painful to suck in another copy. So, here is what I have for -# DST start/end dates for Peking's time zone (info from AP): -# -# 1986 May 4 - Sept 14 -# 1987 mid-April - ?? - -# From U. S. Naval Observatory (1989-01-19): -# CHINA 8 H AHEAD OF UTC ALL OF CHINA, INCL TAIWAN -# CHINA 9 H AHEAD OF UTC APR 17 - SEP 10 - -# From Paul Eggert (2008-02-11): -# Jim Mann, "A clumsy embrace for another western custom: China on daylight -# time - sort of", Los Angeles Times, 1986-05-05 ... [says] that China began -# observing daylight saving time in 1986. - -# From Paul Eggert (2014-06-30): -# Shanks & Pottenger have China switching to a single time zone in 1980, but -# this doesn't seem to be correct. They also write that China observed summer -# DST from 1986 through 1991, which seems to match the above commentary, so -# go with them for DST rules as follows: -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Shang 1940 only - Jun 3 0:00 1:00 D -Rule Shang 1940 1941 - Oct 1 0:00 0 S -Rule Shang 1941 only - Mar 16 0:00 1:00 D -Rule PRC 1986 only - May 4 0:00 1:00 D -Rule PRC 1986 1991 - Sep Sun>=11 0:00 0 S -Rule PRC 1987 1991 - Apr Sun>=10 0:00 1:00 D - -# From Anthony Fok (2001-12-20): -# BTW, I did some research on-line and found some info regarding these five -# historic timezones from some Taiwan websites. And yes, there are official -# Chinese names for these locales (before 1949). -# -# From Jesper Nørgaard Welen (2006-07-14): -# I have investigated the timezones around 1970 on the -# https://www.astro.com/atlas site [with provinces and county -# boundaries summarized below].... A few other exceptions were two -# counties on the Sichuan side of the Xizang-Sichuan border, -# counties Dege and Baiyu which lies on the Sichuan side and are -# therefore supposed to be GMT+7, Xizang region being GMT+6, but Dege -# county is GMT+8 according to astro.com while Baiyu county is GMT+6 -# (could be true), for the moment I am assuming that those two -# counties are mistakes in the astro.com data. - -# From Paul Eggert (2017-01-05): -# Alois Treindl kindly sent me translations of the following two sources: -# -# (1) -# Guo Qingsheng (National Time-Service Center, CAS, Xi'an 710600, China) -# Beijing Time at the Beginning of the PRC -# China Historical Materials of Science and Technology -# (Zhongguo ke ji shi liao, 中国科技史料), Vol. 24, No. 1 (2003) -# It gives evidence that at the beginning of the PRC, Beijing time was -# officially apparent solar time! However, Guo also says that the -# evidence is dubious, as the relevant institute of astronomy had not -# been taken over by the PRC yet. It's plausible that apparent solar -# time was announced but never implemented, and that people continued -# to use UT+8. As the Shanghai radio station (and I presume the -# observatory) was still under control of French missionaries, it -# could well have ignored any such mandate. -# -# (2) -# Guo Qing-sheng (Shaanxi Astronomical Observatory, CAS, Xi'an 710600, China) -# A Study on the Standard Time Changes for the Past 100 Years in China -# [undated and unknown publication location] -# It says several things: -# * The Qing dynasty used local apparent solar time throughout China. -# * The Republic of China instituted Beijing mean solar time effective -# the official calendar book of 1914. -# * The French Concession in Shanghai set up signal stations in -# French docks in the 1890s, controlled by Xujiahui (Zikawei) -# Observatory and set to local mean time. -# * "From the end of the 19th century" it changed to UT+8. -# * Chinese Customs (by then reduced to a tool of foreign powers) -# eventually standardized on this time for all ports, and it -# became used by railways as well. -# * In 1918 the Central Observatory proposed dividing China into -# five time zones (see below for details). This caught on -# at first only in coastal areas observing UT+8. -# * During WWII all of China was in theory was at UT+7. In practice -# this was ignored in the west, and I presume was ignored in -# Japanese-occupied territory. -# * Japanese-occupied Manchuria was at UT+9, i.e., Japan time. -# * The five-zone plan was resurrected after WWII and officially put into -# place (with some modifications) in March 1948. It's not clear -# how well it was observed in areas under Nationalist control. -# * The People's Liberation Army used UT+8 during the civil war. -# -# An AP article "Shanghai Internat'l Area Little Changed" in the -# Lewiston (ME) Daily Sun (1939-05-29), p 17, said "Even the time is -# different - the occupied districts going by Tokyo time, an hour -# ahead of that prevailing in the rest of Shanghai." Guess that the -# Xujiahui Observatory was under French control and stuck with UT +08. -# -# In earlier versions of this file, China had many separate Zone entries, but -# this was based on what were apparently incorrect data in Shanks & Pottenger. -# This has now been simplified to the two entries Asia/Shanghai and -# Asia/Urumqi, with the others being links for backward compatibility. -# Proposed in 1918 and theoretically in effect until 1949 (although in practice -# mainly observed in coastal areas), the five zones were: -# -# Changbai Time ("Long-white Time", Long-white = Heilongjiang area) UT +08:30 -# Now part of Asia/Shanghai; its pre-1970 times are not recorded here. -# Heilongjiang (except Mohe county), Jilin -# -# Zhongyuan Time ("Central plain Time") UT +08 -# Now part of Asia/Shanghai. -# most of China -# Milne gives 8:05:43.2 for Xujiahui Observatory time; round to nearest. -# Guo says Shanghai switched to UT +08 "from the end of the 19th century". -# -# Long-shu Time (probably as Long and Shu were two names of the area) UT +07 -# Now part of Asia/Shanghai; its pre-1970 times are not recorded here. -# Guangxi, Guizhou, Hainan, Ningxia, Sichuan, Shaanxi, and Yunnan; -# most of Gansu; west Inner Mongolia; east Qinghai; and the Guangdong -# counties Deqing, Enping, Kaiping, Luoding, Taishan, Xinxing, -# Yangchun, Yangjiang, Yu'nan, and Yunfu. -# -# Xin-zang Time ("Xinjiang-Tibet Time") UT +06 -# This region is now part of either Asia/Urumqi or Asia/Shanghai with -# current boundaries uncertain; times before 1970 for areas that -# disagree with Ürümqi or Shanghai are not recorded here. -# The Gansu counties Aksay, Anxi, Dunhuang, Subei; west Qinghai; -# the Guangdong counties Xuwen, Haikang, Suixi, Lianjiang, -# Zhanjiang, Wuchuan, Huazhou, Gaozhou, Maoming, Dianbai, and Xinyi; -# east Tibet, including Lhasa, Chamdo, Shigaise, Jimsar, Shawan and Hutubi; -# east Xinjiang, including Ürümqi, Turpan, Karamay, Korla, Minfeng, Jinghe, -# Wusu, Qiemo, Xinyan, Wulanwusu, Jinghe, Yumin, Tacheng, Tuoli, Emin, -# Shihezi, Changji, Yanqi, Heshuo, Tuokexun, Tulufan, Shanshan, Hami, -# Fukang, Kuitun, Kumukuli, Miquan, Qitai, and Turfan. -# -# Kunlun Time UT +05:30 -# This region is now in the same status as Xin-zang Time (see above). -# West Tibet, including Pulan, Aheqi, Shufu, Shule; -# West Xinjiang, including Aksu, Atushi, Yining, Hetian, Cele, Luopu, Nileke, -# Zhaosu, Tekesi, Gongliu, Chabuchaer, Huocheng, Bole, Pishan, Suiding, -# and Yarkand. - -# From Luther Ma (2009-10-17): -# Almost all (>99.9%) ethnic Chinese (properly ethnic Han) living in -# Xinjiang use Chinese Standard Time. Some are aware of Xinjiang time, -# but have no need of it. All planes, trains, and schools function on -# what is called "Beijing time." When Han make an appointment in Chinese -# they implicitly use Beijing time. -# -# On the other hand, ethnic Uyghurs, who make up about half the -# population of Xinjiang, typically use "Xinjiang time" which is two -# hours behind Beijing time, or UT +06. The government of the Xinjiang -# Uyghur Autonomous Region, (XAUR, or just Xinjiang for short) as well as -# local governments such as the Ürümqi city government use both times in -# publications, referring to what is popularly called Xinjiang time as -# "Ürümqi time." When Uyghurs make an appointment in the Uyghur language -# they almost invariably use Xinjiang time. -# -# (Their ethnic Han compatriots would typically have no clue of its -# widespread use, however, because so extremely few of them are fluent in -# Uyghur, comparable to the number of Anglo-Americans fluent in Navajo.) -# -# (...As with the rest of China there was a brief interval ending in 1990 -# or 1991 when summer time was in use. The confusion was severe, with -# the province not having dual times but four times in use at the same -# time. Some areas remained on standard Xinjiang time or Beijing time and -# others moving their clocks ahead.) - -# From Luther Ma (2009-11-19): -# With the risk of being redundant to previous answers these are the most common -# English "transliterations" (w/o using non-English symbols): -# -# 1. Wulumuqi... -# 2. Kashi... -# 3. Urumqi... -# 4. Kashgar... -# ... -# 5. It seems that Uyghurs in Ürümqi has been using Xinjiang since at least the -# 1960's. I know of one Han, now over 50, who grew up in the surrounding -# countryside and used Xinjiang time as a child. -# -# 6. Likewise for Kashgar and the rest of south Xinjiang I don't know of any -# start date for Xinjiang time. -# -# Without having access to local historical records, nor the ability to legally -# publish them, I would go with October 1, 1949, when Xinjiang became the Uyghur -# Autonomous Region under the PRC. (Before that Uyghurs, of course, would also -# not be using Beijing time, but some local time.) - -# From David Cochrane (2014-03-26): -# Just a confirmation that Ürümqi time was implemented in Ürümqi on 1 Feb 1986: -# https://content.time.com/time/magazine/article/0,9171,960684,00.html - -# From Luther Ma (2014-04-22): -# I have interviewed numerous people of various nationalities and from -# different localities in Xinjiang and can confirm the information in Guo's -# report regarding Xinjiang, as well as the Time article reference by David -# Cochrane. Whether officially recognized or not (and both are officially -# recognized), two separate times have been in use in Xinjiang since at least -# the Cultural Revolution: Xinjiang Time (XJT), aka Ürümqi Time or local time; -# and Beijing Time. There is no confusion in Xinjiang as to which name refers -# to which time. Both are widely used in the province, although in some -# population groups might be use one to the exclusion of the other. The only -# problem is that computers and smart phones list Ürümqi (or Kashgar) as -# having the same time as Beijing. - -# From Paul Eggert (2014-06-30): -# In the early days of the PRC, Tibet was given its own time zone (UT +06) -# but this was withdrawn in 1959 and never reinstated; see Tubten Khétsun, -# Memories of life in Lhasa under Chinese Rule, Columbia U Press, ISBN -# 978-0231142861 (2008), translator's introduction by Matthew Akester, p x. -# As this is before our 1970 cutoff, Tibet doesn't need a separate zone. -# -# Xinjiang Time is well-documented as being officially recognized. E.g., see -# "The Working-Calendar for The Xinjiang Uygur Autonomous Region Government" -# (2014-04-22). -# Unfortunately, we have no good records of time in Xinjiang before 1986. -# During the 20th century parts of Xinjiang were ruled by the Qing dynasty, -# the Republic of China, various warlords, the First and Second East Turkestan -# Republics, the Soviet Union, the Kuomintang, and the People's Republic of -# China, and tracking down all these organizations' timekeeping rules would be -# quite a trick. Approximate this lost history by a transition from LMT to -# UT +06 at the start of 1928, the year of accession of the warlord Jin Shuren, -# which happens to be the date given by Shanks & Pottenger (no doubt as a -# guess) as the transition from LMT. Ignore the usage of +08 before -# 1986-02-01 under the theory that the transition date to +08 is unknown and -# that the sort of users who prefer Asia/Urumqi now typically ignored the -# +08 mandate back then. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -# Beijing time, used throughout China; represented by Shanghai. -Zone Asia/Shanghai 8:05:43 - LMT 1901 - 8:00 Shang C%sT 1949 - 8:00 PRC C%sT -# Xinjiang time, used by many in western China; represented by Ürümqi / Ürümchi -# / Wulumuqi. (Please use Asia/Shanghai if you prefer Beijing time.) -Zone Asia/Urumqi 5:50:20 - LMT 1928 - 6:00 - +06 - - -# Hong Kong (Xianggang) - -# Milne gives 7:36:41.7; round this. - -# From Lee Yiu Chung (2009-10-24): -# I found there are some mistakes for the...DST rule for Hong -# Kong. [According] to the DST record from Hong Kong Observatory (actually, -# it is not [an] observatory, but the official meteorological agency of HK, -# and also serves as the official timing agency), there are some missing -# and incorrect rules. Although the exact switch over time is missing, I -# think 3:30 is correct. The official DST record for Hong Kong can be -# obtained from -# http://www.hko.gov.hk/gts/time/Summertime.htm - -# From Arthur David Olson (2009-10-28): -# Here are the dates given at -# http://www.hko.gov.hk/gts/time/Summertime.htm -# as of 2009-10-28: -# Year Period -# 1941 1 Apr to 30 Sep -# 1942 Whole year -# 1943 Whole year -# 1944 Whole year -# 1945 Whole year -# 1946 20 Apr to 1 Dec -# 1947 13 Apr to 30 Dec -# 1948 2 May to 31 Oct -# 1949 3 Apr to 30 Oct -# 1950 2 Apr to 29 Oct -# 1951 1 Apr to 28 Oct -# 1952 6 Apr to 25 Oct -# 1953 5 Apr to 1 Nov -# 1954 21 Mar to 31 Oct -# 1955 20 Mar to 6 Nov -# 1956 18 Mar to 4 Nov -# 1957 24 Mar to 3 Nov -# 1958 23 Mar to 2 Nov -# 1959 22 Mar to 1 Nov -# 1960 20 Mar to 6 Nov -# 1961 19 Mar to 5 Nov -# 1962 18 Mar to 4 Nov -# 1963 24 Mar to 3 Nov -# 1964 22 Mar to 1 Nov -# 1965 18 Apr to 17 Oct -# 1966 17 Apr to 16 Oct -# 1967 16 Apr to 22 Oct -# 1968 21 Apr to 20 Oct -# 1969 20 Apr to 19 Oct -# 1970 19 Apr to 18 Oct -# 1971 18 Apr to 17 Oct -# 1972 16 Apr to 22 Oct -# 1973 22 Apr to 21 Oct -# 1973/74 30 Dec 73 to 20 Oct 74 -# 1975 20 Apr to 19 Oct -# 1976 18 Apr to 17 Oct -# 1977 Nil -# 1978 Nil -# 1979 13 May to 21 Oct -# 1980 to Now Nil -# The page does not give start or end times of day. -# The page does not give a start date for 1942. -# The page does not givw an end date for 1945. -# The Japanese occupation of Hong Kong began on 1941-12-25. -# The Japanese surrender of Hong Kong was signed 1945-09-15. -# For lack of anything better, use start of those days as the transition times. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule HK 1941 only - Apr 1 3:30 1:00 S -Rule HK 1941 only - Sep 30 3:30 0 - -Rule HK 1946 only - Apr 20 3:30 1:00 S -Rule HK 1946 only - Dec 1 3:30 0 - -Rule HK 1947 only - Apr 13 3:30 1:00 S -Rule HK 1947 only - Dec 30 3:30 0 - -Rule HK 1948 only - May 2 3:30 1:00 S -Rule HK 1948 1951 - Oct lastSun 3:30 0 - -Rule HK 1952 only - Oct 25 3:30 0 - -Rule HK 1949 1953 - Apr Sun>=1 3:30 1:00 S -Rule HK 1953 only - Nov 1 3:30 0 - -Rule HK 1954 1964 - Mar Sun>=18 3:30 1:00 S -Rule HK 1954 only - Oct 31 3:30 0 - -Rule HK 1955 1964 - Nov Sun>=1 3:30 0 - -Rule HK 1965 1976 - Apr Sun>=16 3:30 1:00 S -Rule HK 1965 1976 - Oct Sun>=16 3:30 0 - -Rule HK 1973 only - Dec 30 3:30 1:00 S -Rule HK 1979 only - May Sun>=8 3:30 1:00 S -Rule HK 1979 only - Oct Sun>=16 3:30 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Hong_Kong 7:36:42 - LMT 1904 Oct 30 - 8:00 HK HK%sT 1941 Dec 25 - 9:00 - JST 1945 Sep 15 - 8:00 HK HK%sT - -############################################################################### - -# Taiwan - -# From smallufo (2010-04-03): -# According to Taiwan's CWB [Central Weather Bureau], -# http://www.cwb.gov.tw/V6/astronomy/cdata/summert.htm -# Taipei has DST in 1979 between July 1st and Sep 30. - -# From Yu-Cheng Chuang (2013-07-12): -# On Dec 28, 1895, the Meiji Emperor announced Ordinance No. 167 of -# Meiji Year 28 "The clause about standard time", mentioned that -# Taiwan and Penghu Islands, as well as Yaeyama and Miyako Islands -# (both in Okinawa) adopt the Western Standard Time which is based on -# 120E. The adoption began from Jan 1, 1896. The original text can be -# found on Wikisource: -# https://ja.wikisource.org/wiki/標準時ニ關スル件_(公布時) -# ... This could be the first adoption of time zone in Taiwan, because -# during the Qing Dynasty, it seems that there was no time zone -# declared officially. -# -# Later, in the beginning of World War II, on Sep 25, 1937, the Showa -# Emperor announced Ordinance No. 529 of Showa Year 12 "The clause of -# revision in the ordinance No. 167 of Meiji year 28 about standard -# time", in which abolished the adoption of Western Standard Time in -# western islands (listed above), which means the whole Japan -# territory, including later occupations, adopt Japan Central Time -# (UT+9). The adoption began on Oct 1, 1937. The original text can -# be found on Wikisource: -# https://ja.wikisource.org/wiki/明治二十八年勅令第百六十七號標準時ニ關スル件中改正ノ件 -# -# That is, the time zone of Taipei switched to UT+9 on Oct 1, 1937. - -# From Yu-Cheng Chuang (2014-07-02): -# I've found more evidence about when the time zone was switched from UT+9 -# back to UT+8 after WW2. I believe it was on Sep 21, 1945. In a document -# during Japanese era [1] in which the officer told the staff to change time -# zone back to Western Standard Time (UT+8) on Sep 21. And in another -# history page of National Cheng Kung University [2], on Sep 21 there is a -# note "from today, switch back to Western Standard Time". From these two -# materials, I believe that the time zone change happened on Sep 21. And -# today I have found another monthly journal called "The Astronomical Herald" -# from The Astronomical Society of Japan [3] in which it mentioned the fact -# that: -# -# 1. Standard Time of the Country (Japan) was adopted on Jan 1, 1888, using -# the time at 135E (GMT+9) -# -# 2. Standard Time of the Country was renamed to Central Standard Time, on Jan -# 1, 1898, and on the same day, the new territories Taiwan and Penghu islands, -# as well as Yaeyama and Miyako islands, adopted a new time zone called -# Western Standard Time, which is in GMT+8. -# -# 3. Western Standard Time was deprecated on Sep 30, 1937. From then all the -# territories of Japan adopted the same time zone, which is Central Standard -# Time. -# -# [1] Academica Historica, Taiwan: -# http://163.29.208.22:8080/govsaleShowImage/connect_img.php?s=00101738900090036&e=00101738900090037 -# [2] Nat'l Cheng Kung University 70th Anniversary Special Site: -# http://www.ncku.edu.tw/~ncku70/menu/001/01_01.htm -# [3] Yukio Niimi, The Standard Time in Japan (1997), p.475: -# http://www.asj.or.jp/geppou/archive_open/1997/pdf/19971001c.pdf - -# Yu-Cheng Chuang (2014-07-03): -# I finally have found the real official gazette about changing back to -# Western Standard Time on Sep 21 in Taiwan. It's Taiwan Governor-General -# Bulletin No. 386 in Showa 20 years (1945), published on Sep 19, 1945. [1] ... -# [It] abolishes Bulletin No. 207 in Showa 12 years (1937), which is a local -# bulletin in Taiwan for that Ordinance No. 529. It also mentioned that 1am on -# Sep 21, 1945 will be 12am on Sep 21. I think this bulletin is much more -# official than the one I mentioned in my first mail, because it's from the -# top-level government in Taiwan. If you're going to quote any resource, this -# would be a good one. -# [1] Taiwan Governor-General Gazette, No. 1018, Sep 19, 1945: -# http://db2.th.gov.tw/db2/view/viewImg.php?imgcode=0072031018a&num=19&bgn=019&end=019&otherImg=&type=gener - -# From Yu-Cheng Chuang (2014-07-02): -# In 1946, DST in Taiwan was from May 15 and ended on Sep 30. The info from -# Central Weather Bureau website was not correct. -# -# Original Bulletin: -# http://subtpg.tpg.gov.tw/og/image2.asp?f=03502F0AKM1AF -# http://subtpg.tpg.gov.tw/og/image2.asp?f=0350300AKM1B0 (cont.) -# -# In 1947, DST in Taiwan was expanded to Oct 31. There is a backup of that -# telegram announcement from Taiwan Province Government: -# -# http://subtpg.tpg.gov.tw/og/image2.asp?f=0360310AKZ431 -# -# Here is a brief translation: -# -# The Summer Time this year is adopted from midnight Apr 15 until Sep 20 -# midnight. To save (energy?) consumption, we're expanding Summer Time -# adoption till Oct 31 midnight. -# -# The Central Weather Bureau website didn't mention that, however it can -# be found from historical government announcement database. - -# From Paul Eggert (2014-07-03): -# As per Yu-Cheng Chuang, say that Taiwan was at UT +09 from 1937-10-01 -# until 1945-09-21 at 01:00, overriding Shanks & Pottenger. -# Likewise, use Yu-Cheng Chuang's data for DST in Taiwan. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Taiwan 1946 only - May 15 0:00 1:00 D -Rule Taiwan 1946 only - Oct 1 0:00 0 S -Rule Taiwan 1947 only - Apr 15 0:00 1:00 D -Rule Taiwan 1947 only - Nov 1 0:00 0 S -Rule Taiwan 1948 1951 - May 1 0:00 1:00 D -Rule Taiwan 1948 1951 - Oct 1 0:00 0 S -Rule Taiwan 1952 only - Mar 1 0:00 1:00 D -Rule Taiwan 1952 1954 - Nov 1 0:00 0 S -Rule Taiwan 1953 1959 - Apr 1 0:00 1:00 D -Rule Taiwan 1955 1961 - Oct 1 0:00 0 S -Rule Taiwan 1960 1961 - Jun 1 0:00 1:00 D -Rule Taiwan 1974 1975 - Apr 1 0:00 1:00 D -Rule Taiwan 1974 1975 - Oct 1 0:00 0 S -Rule Taiwan 1979 only - Jul 1 0:00 1:00 D -Rule Taiwan 1979 only - Oct 1 0:00 0 S - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -# Taipei or Taibei or T'ai-pei -Zone Asia/Taipei 8:06:00 - LMT 1896 Jan 1 - 8:00 - CST 1937 Oct 1 - 9:00 - JST 1945 Sep 21 1:00 - 8:00 Taiwan C%sT - -# Macau (Macao, Aomen) -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Macau 1961 1962 - Mar Sun>=16 3:30 1:00 D -Rule Macau 1961 1964 - Nov Sun>=1 3:30 0 S -Rule Macau 1963 only - Mar Sun>=16 0:00 1:00 D -Rule Macau 1964 only - Mar Sun>=16 3:30 1:00 D -Rule Macau 1965 only - Mar Sun>=16 0:00 1:00 D -Rule Macau 1965 only - Oct 31 0:00 0 S -Rule Macau 1966 1971 - Apr Sun>=16 3:30 1:00 D -Rule Macau 1966 1971 - Oct Sun>=16 3:30 0 S -Rule Macau 1972 1974 - Apr Sun>=15 0:00 1:00 D -Rule Macau 1972 1973 - Oct Sun>=15 0:00 0 S -Rule Macau 1974 1977 - Oct Sun>=15 3:30 0 S -Rule Macau 1975 1977 - Apr Sun>=15 3:30 1:00 D -Rule Macau 1978 1980 - Apr Sun>=15 0:00 1:00 D -Rule Macau 1978 1980 - Oct Sun>=15 0:00 0 S -# See Europe/Lisbon for info about the 1912 transition. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Macau 7:34:20 - LMT 1911 Dec 31 16:00u - 8:00 Macau C%sT - - -############################################################################### - -# Cyprus - -# Milne says the Eastern Telegraph Company used 2:14:00. Stick with LMT. -# IATA SSIM (1998-09) has Cyprus using EU rules for the first time. - -# From Paul Eggert (2016-09-09): -# Yesterday's Cyprus Mail reports that Northern Cyprus followed Turkey's -# lead and switched from +02/+03 to +03 year-round. -# http://cyprus-mail.com/2016/09/08/two-time-zones-cyprus-turkey-will-not-turn-clocks-back-next-month/ -# -# From Even Scharning (2016-10-31): -# Looks like the time zone split in Cyprus went through last night. -# http://cyprus-mail.com/2016/10/30/cyprus-new-division-two-time-zones-now-reality/ - -# From Paul Eggert (2017-10-18): -# Northern Cyprus will reinstate winter time on October 29, thus -# staying in sync with the rest of Cyprus. See: Anastasiou A. -# Cyprus to remain united in time. Cyprus Mail 2017-10-17. -# https://cyprus-mail.com/2017/10/17/cyprus-remain-united-time/ - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Cyprus 1975 only - Apr 13 0:00 1:00 S -Rule Cyprus 1975 only - Oct 12 0:00 0 - -Rule Cyprus 1976 only - May 15 0:00 1:00 S -Rule Cyprus 1976 only - Oct 11 0:00 0 - -Rule Cyprus 1977 1980 - Apr Sun>=1 0:00 1:00 S -Rule Cyprus 1977 only - Sep 25 0:00 0 - -Rule Cyprus 1978 only - Oct 2 0:00 0 - -Rule Cyprus 1979 1997 - Sep lastSun 0:00 0 - -Rule Cyprus 1981 1998 - Mar lastSun 0:00 1:00 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Nicosia 2:13:28 - LMT 1921 Nov 14 - 2:00 Cyprus EE%sT 1998 Sep - 2:00 EUAsia EE%sT -Zone Asia/Famagusta 2:15:48 - LMT 1921 Nov 14 - 2:00 Cyprus EE%sT 1998 Sep - 2:00 EUAsia EE%sT 2016 Sep 8 - 3:00 - +03 2017 Oct 29 1:00u - 2:00 EUAsia EE%sT - -# Classically, Cyprus belongs to Asia; e.g. see Herodotus, Histories, I.72. -# However, for various reasons many users expect to find it under Europe. -Link Asia/Nicosia Europe/Nicosia - -# Georgia -# From Paul Eggert (1994-11-19): -# Today's _Economist_ (p 60) reports that Georgia moved its clocks forward -# an hour recently, due to a law proposed by Zurab Murvanidze, -# an MP who went on a hunger strike for 11 days to force discussion about it! -# We have no details, but we'll guess they didn't move the clocks back in fall. -# -# From Mathew Englander, quoting AP (1996-10-23 13:05-04): -# Instead of putting back clocks at the end of October, Georgia -# will stay on daylight savings time this winter to save energy, -# President Eduard Shevardnadze decreed Wednesday. -# -# From the BBC via Joseph S. Myers (2004-06-27): -# -# Georgia moved closer to Western Europe on Sunday... The former Soviet -# republic has changed its time zone back to that of Moscow. As a result it -# is now just four hours ahead of Greenwich Mean Time, rather than five hours -# ahead. The switch was decreed by the pro-Western president of Georgia, -# Mikheil Saakashvili, who said the change was partly prompted by the process -# of integration into Europe. - -# From Teimuraz Abashidze (2005-11-07): -# Government of Georgia ... decided to NOT CHANGE daylight savings time on -# [Oct.] 30, as it was done before during last more than 10 years. -# Currently, we are in fact GMT +4:00, as before 30 October it was GMT -# +3:00.... The problem is, there is NO FORMAL LAW or governmental document -# about it. As far as I can find, I was told, that there is no document, -# because we just DIDN'T ISSUE document about switching to winter time.... -# I don't know what can be done, especially knowing that some years ago our -# DST rules where changed THREE TIMES during one month. - -# Milne 1899 says Tbilisi (Tiflis) time was 2:59:05.7. -# Byalokoz 1919 says Georgia was 2:59:11. -# Go with Byalokoz. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Tbilisi 2:59:11 - LMT 1880 - 2:59:11 - TBMT 1924 May 2 # Tbilisi Mean Time - 3:00 - +03 1957 Mar - 4:00 RussiaAsia +04/+05 1991 Mar 31 2:00s - 3:00 RussiaAsia +03/+04 1992 - 3:00 E-EurAsia +03/+04 1994 Sep lastSun - 4:00 E-EurAsia +04/+05 1996 Oct lastSun - 4:00 1:00 +05 1997 Mar lastSun - 4:00 E-EurAsia +04/+05 2004 Jun 27 - 3:00 RussiaAsia +03/+04 2005 Mar lastSun 2:00 - 4:00 - +04 - -# East Timor - -# See Indonesia for the 1945 transition. - -# From João Carrascalão, brother of the former governor of East Timor, in -# East Timor may be late for its millennium -# (1999-12-26/31): -# Portugal tried to change the time forward in 1974 because the sun -# rises too early but the suggestion raised a lot of problems with the -# Timorese and I still don't think it would work today because it -# conflicts with their way of life. - -# From Paul Eggert (2000-12-04): -# We don't have any record of the above attempt. -# Most likely our records are incomplete, but we have no better data. - -# From Manoel de Almeida e Silva, Deputy Spokesman for the UN Secretary-General -# http://www.hri.org/news/world/undh/2000/00-08-16.undh.html -# (2000-08-16): -# The Cabinet of the East Timor Transition Administration decided -# today to advance East Timor's time by one hour. The time change, -# which will be permanent, with no seasonal adjustment, will happen at -# midnight on Saturday, September 16. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Dili 8:22:20 - LMT 1912 Jan 1 - 8:00 - +08 1942 Feb 21 23:00 - 9:00 - +09 1976 May 3 - 8:00 - +08 2000 Sep 17 0:00 - 9:00 - +09 - -# India - -# From Ian P. Beacock, in "A brief history of (modern) time", The Atlantic -# https://www.theatlantic.com/technology/archive/2015/12/the-creation-of-modern-time/421419/ -# (2015-12-22): -# In January 1906, several thousand cotton-mill workers rioted on the -# outskirts of Bombay.... They were protesting the proposed abolition of -# local time in favor of Indian Standard Time.... Journalists called this -# dispute the "Battle of the Clocks." It lasted nearly half a century. - -# From Paul Eggert (2017-04-20): -# Good luck trying to nail down old timekeeping records in India. -# "... in the nineteenth century ... Madras Observatory took its magnetic -# measurements on Göttingen time, its meteorological measurements on Madras -# (local) time, dropped its time ball on Greenwich (ocean navigator's) time, -# and distributed civil (local time)." -- Bartky IR. Selling the true time: -# 19th-century timekeeping in america. Stanford U Press (2000), 247 note 19. -# "A more potent cause of resistance to the general adoption of the present -# standard time lies in the fact that it is Madras time. The citizen of -# Bombay, proud of being 'primus in Indis' and of Calcutta, equally proud of -# his city being the Capital of India, and - for a part of the year - the Seat -# of the Supreme Government, alike look down on Madras, and refuse to change -# the time they are using, for that of what they regard as a benighted -# Presidency; while Madras, having for long given the standard time to the -# rest of India, would resist the adoption of any other Indian standard in its -# place." -- Oldham RD. On Time in India: a suggestion for its improvement. -# Proceedings of the Asiatic Society of Bengal (April 1899), 49-55. -# -# "In 1870 ... Madras time - 'now used by the telegraph and regulated from the -# only government observatory' - was suggested as a standard railway time, -# first to be adopted on the Great Indian Peninsular Railway (GIPR).... -# Calcutta, Bombay, and Karachi, were to be allowed to continue with their -# local time for civil purposes." - Prasad R. Tracks of Change: Railways and -# Everyday Life in Colonial India. Cambridge University Press (2016), 145. -# -# Reed S, Low F. The Indian Year Book 1936-37. Bennett, Coleman, pp 27-8. -# https://archive.org/details/in.ernet.dli.2015.282212 -# This lists +052110 as Madras local time used in railways, and says that on -# 1906-01-01 railways and telegraphs in India switched to +0530. Some -# municipalities retained their former time, and the time in Calcutta -# continued to depend on whether you were at the railway station or at -# government offices. Government time was at +055320 (according to Shanks) or -# at +0554 (according to the Indian Year Book). Railway time is more -# appropriate for our purposes, as it was better documented, it is what we do -# elsewhere (e.g., Europe/London before 1880), and after 1906 it was -# consistent in the region now identified by Asia/Kolkata. So, use railway -# time for 1870-1941. Shanks is our only (and dubious) source for the -# 1941-1945 data. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Kolkata 5:53:28 - LMT 1854 Jun 28 # Kolkata - 5:53:20 - HMT 1870 # Howrah Mean Time? - 5:21:10 - MMT 1906 Jan 1 # Madras local time - 5:30 - IST 1941 Oct - 5:30 1:00 +0630 1942 May 15 - 5:30 - IST 1942 Sep - 5:30 1:00 +0630 1945 Oct 15 - 5:30 - IST -# Since 1970 the following are like Asia/Kolkata: -# Andaman Is -# Lakshadweep (Laccadive, Minicoy and Amindivi Is) -# Nicobar Is - -# Indonesia -# -# From Paul Eggert (2014-09-06): -# The 1876 Report of the Secretary of the [US] Navy, p 306 says that Batavia -# civil time was 7:07:12.5; round to even for Jakarta. -# -# From Gwillim Law (2001-05-28), overriding Shanks & Pottenger: -# http://www.sumatera-inc.com/go_to_invest/about_indonesia.asp#standtime -# says that Indonesia's time zones changed on 1988-01-01. Looking at some -# time zone maps, I think that must refer to Western Borneo (Kalimantan Barat -# and Kalimantan Tengah) switching from UTC+8 to UTC+7. -# -# From Paul Eggert (2007-03-10): -# Here is another correction to Shanks & Pottenger. -# JohnTWB writes that Japanese forces did not surrender control in -# Indonesia until 1945-09-01 00:00 at the earliest (in Jakarta) and -# other formal surrender ceremonies were September 9, 11, and 13, plus -# September 12 for the regional surrender to Mountbatten in Singapore. -# These would be the earliest possible times for a change. -# Régimes horaires pour le monde entier, by Henri Le Corre, (Éditions -# Traditionnelles, 1987, Paris) says that Java and Madura switched -# from UT +09 to +07:30 on 1945-09-23, and gives 1944-09-01 for Jayapura -# (Hollandia). For now, assume all Indonesian locations other than Jayapura -# switched on 1945-09-23. -# -# From Paul Eggert (2013-08-11): -# Normally the tz database uses English-language abbreviations, but in -# Indonesia it's typical to use Indonesian-language abbreviations even -# when writing in English. For example, see the English-language -# summary published by the Time and Frequency Laboratory of the -# Research Center for Calibration, Instrumentation and Metrology, -# Indonesia, (2006-09-29). -# The time zone abbreviations and UT offsets are: -# -# WIB - +07 - Waktu Indonesia Barat (Indonesia western time) -# WITA - +08 - Waktu Indonesia Tengah (Indonesia central time) -# WIT - +09 - Waktu Indonesia Timur (Indonesia eastern time) -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -# Java, Sumatra -Zone Asia/Jakarta 7:07:12 - LMT 1867 Aug 10 -# Shanks & Pottenger say the next transition was at 1924 Jan 1 0:13, -# but this must be a typo. - 7:07:12 - BMT 1923 Dec 31 23:47:12 # Batavia - 7:20 - +0720 1932 Nov - 7:30 - +0730 1942 Mar 23 - 9:00 - +09 1945 Sep 23 - 7:30 - +0730 1948 May - 8:00 - +08 1950 May - 7:30 - +0730 1964 - 7:00 - WIB -# west and central Borneo -Zone Asia/Pontianak 7:17:20 - LMT 1908 May - 7:17:20 - PMT 1932 Nov # Pontianak MT - 7:30 - +0730 1942 Jan 29 - 9:00 - +09 1945 Sep 23 - 7:30 - +0730 1948 May - 8:00 - +08 1950 May - 7:30 - +0730 1964 - 8:00 - WITA 1988 Jan 1 - 7:00 - WIB -# Sulawesi, Lesser Sundas, east and south Borneo -Zone Asia/Makassar 7:57:36 - LMT 1920 - 7:57:36 - MMT 1932 Nov # Macassar MT - 8:00 - +08 1942 Feb 9 - 9:00 - +09 1945 Sep 23 - 8:00 - WITA -# Maluku Islands, West Papua, Papua -Zone Asia/Jayapura 9:22:48 - LMT 1932 Nov - 9:00 - +09 1944 Sep 1 - 9:30 - +0930 1964 - 9:00 - WIT - -# Iran - -# From Roozbeh Pournader (2003-03-15): -# This is an English translation of what I just found (originally in Persian). -# The Gregorian dates in brackets are mine: -# -# Official Newspaper No. 13548-1370/6/25 [1991-09-16] -# No. 16760/T233 H 1370/6/10 [1991-09-01] -# -# The Rule About Change of the Official Time of the Country -# -# The Board of Ministers, in the meeting dated 1370/5/23 [1991-08-14], -# based on the suggestion number 2221/D dated 1370/4/22 [1991-07-13] -# of the Country's Organization for Official and Employment Affairs, -# and referring to the law for equating the working hours of workers -# and officers in the whole country dated 1359/4/23 [1980-07-14], and -# for synchronizing the official times of the country, agreed that: -# -# The official time of the country will should move forward one hour -# at the 24[:00] hours of the first day of Farvardin and should return -# to its previous state at the 24[:00] hours of the 30th day of -# Shahrivar. -# -# First Deputy to the President - Hassan Habibi -# -# From personal experience, that agrees with what has been followed -# for at least the last 5 years. Before that, for a few years, the -# date used was the first Thursday night of Farvardin and the last -# Thursday night of Shahrivar, but I can't give exact dates.... -# -# From Roozbeh Pournader (2005-04-05): -# The text of the Iranian law, in effect since 1925, clearly mentions -# that the true solar year is the measure, and there is no arithmetic -# leap year calculation involved. There has never been any serious -# plan to change that law.... -# -# From Paul Eggert (2006-03-22): -# Go with Shanks & Pottenger before Sept. 1991, and with Pournader thereafter. -# I used Ed Reingold's cal-persia in GNU Emacs 21.2 to check Persian dates, -# stopping after 2037 when 32-bit time_t's overflow. -# That cal-persia used Birashk's approximation, which disagrees with the solar -# calendar predictions for the year 2025, so I corrected those dates by hand. -# -# From Oscar van Vlijmen (2005-03-30), writing about future -# discrepancies between cal-persia and the Iranian calendar: -# For 2091 solar-longitude-after yields 2091-03-20 08:40:07.7 UT for -# the vernal equinox and that gets so close to 12:00 some local -# Iranian time that the definition of the correct location needs to be -# known exactly, amongst other factors. 2157 is even closer: -# 2157-03-20 08:37:15.5 UT. But the Gregorian year 2025 should give -# no interpretation problem whatsoever. By the way, another instant -# in the near future where there will be a discrepancy between -# arithmetical and astronomical Iranian calendars will be in 2058: -# vernal equinox on 2058-03-20 09:03:05.9 UT. The Java version of -# Reingold's/Dershowitz' calculator gives correctly the Gregorian date -# 2058-03-21 for 1 Farvardin 1437 (astronomical). -# -# From Steffen Thorsen (2006-03-22): -# Several of my users have reported that Iran will not observe DST anymore: -# http://www.irna.ir/en/news/view/line-17/0603193812164948.htm -# -# From Reuters (2007-09-16), with a heads-up from Jesper Nørgaard Welen: -# ... the Guardian Council ... approved a law on Sunday to re-introduce -# daylight saving time ... -# https://uk.reuters.com/article/oilRpt/idUKBLA65048420070916 -# -# From Roozbeh Pournader (2007-11-05): -# This is quoted from Official Gazette of the Islamic Republic of -# Iran, Volume 63, No. 18242, dated Tuesday 1386/6/24 -# [2007-10-16]. I am doing the best translation I can:... -# The official time of the country will be moved forward for one hour -# on the 24 hours of the first day of the month of Farvardin and will -# be changed back to its previous state on the 24 hours of the -# thirtieth day of Shahrivar. -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Iran 1978 1980 - Mar 21 0:00 1:00 - -Rule Iran 1978 only - Oct 21 0:00 0 - -Rule Iran 1979 only - Sep 19 0:00 0 - -Rule Iran 1980 only - Sep 23 0:00 0 - -Rule Iran 1991 only - May 3 0:00 1:00 - -Rule Iran 1992 1995 - Mar 22 0:00 1:00 - -Rule Iran 1991 1995 - Sep 22 0:00 0 - -Rule Iran 1996 only - Mar 21 0:00 1:00 - -Rule Iran 1996 only - Sep 21 0:00 0 - -Rule Iran 1997 1999 - Mar 22 0:00 1:00 - -Rule Iran 1997 1999 - Sep 22 0:00 0 - -Rule Iran 2000 only - Mar 21 0:00 1:00 - -Rule Iran 2000 only - Sep 21 0:00 0 - -Rule Iran 2001 2003 - Mar 22 0:00 1:00 - -Rule Iran 2001 2003 - Sep 22 0:00 0 - -Rule Iran 2004 only - Mar 21 0:00 1:00 - -Rule Iran 2004 only - Sep 21 0:00 0 - -Rule Iran 2005 only - Mar 22 0:00 1:00 - -Rule Iran 2005 only - Sep 22 0:00 0 - -Rule Iran 2008 only - Mar 21 0:00 1:00 - -Rule Iran 2008 only - Sep 21 0:00 0 - -Rule Iran 2009 2011 - Mar 22 0:00 1:00 - -Rule Iran 2009 2011 - Sep 22 0:00 0 - -Rule Iran 2012 only - Mar 21 0:00 1:00 - -Rule Iran 2012 only - Sep 21 0:00 0 - -Rule Iran 2013 2015 - Mar 22 0:00 1:00 - -Rule Iran 2013 2015 - Sep 22 0:00 0 - -Rule Iran 2016 only - Mar 21 0:00 1:00 - -Rule Iran 2016 only - Sep 21 0:00 0 - -Rule Iran 2017 2019 - Mar 22 0:00 1:00 - -Rule Iran 2017 2019 - Sep 22 0:00 0 - -Rule Iran 2020 only - Mar 21 0:00 1:00 - -Rule Iran 2020 only - Sep 21 0:00 0 - -Rule Iran 2021 2023 - Mar 22 0:00 1:00 - -Rule Iran 2021 2023 - Sep 22 0:00 0 - -Rule Iran 2024 only - Mar 21 0:00 1:00 - -Rule Iran 2024 only - Sep 21 0:00 0 - -Rule Iran 2025 2027 - Mar 22 0:00 1:00 - -Rule Iran 2025 2027 - Sep 22 0:00 0 - -Rule Iran 2028 2029 - Mar 21 0:00 1:00 - -Rule Iran 2028 2029 - Sep 21 0:00 0 - -Rule Iran 2030 2031 - Mar 22 0:00 1:00 - -Rule Iran 2030 2031 - Sep 22 0:00 0 - -Rule Iran 2032 2033 - Mar 21 0:00 1:00 - -Rule Iran 2032 2033 - Sep 21 0:00 0 - -Rule Iran 2034 2035 - Mar 22 0:00 1:00 - -Rule Iran 2034 2035 - Sep 22 0:00 0 - -# -# The following rules are approximations starting in the year 2038. -# These are the best post-2037 approximations available, given the -# restrictions of a single rule using a Gregorian-based data format. -# At some point this table will need to be extended, though quite -# possibly Iran will change the rules first. -Rule Iran 2036 max - Mar 21 0:00 1:00 - -Rule Iran 2036 max - Sep 21 0:00 0 - - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Tehran 3:25:44 - LMT 1916 - 3:25:44 - TMT 1946 # Tehran Mean Time - 3:30 - +0330 1977 Nov - 4:00 Iran +04/+05 1979 - 3:30 Iran +0330/+0430 - - -# Iraq -# -# From Jonathan Lennox (2000-06-12): -# An article in this week's Economist ("Inside the Saddam-free zone", p. 50 in -# the U.S. edition) on the Iraqi Kurds contains a paragraph: -# "The three northern provinces ... switched their clocks this spring and -# are an hour ahead of Baghdad." -# -# But Rives McDow (2000-06-18) quotes a contact in Iraqi-Kurdistan as follows: -# In the past, some Kurdish nationalists, as a protest to the Iraqi -# Government, did not adhere to daylight saving time. They referred -# to daylight saving as Saddam time. But, as of today, the time zone -# in Iraqi-Kurdistan is on standard time with Baghdad, Iraq. -# -# So we'll ignore the Economist's claim. - -# From Steffen Thorsen (2008-03-10): -# The cabinet in Iraq abolished DST last week, according to the following -# news sources (in Arabic): -# http://www.aljeeran.net/wesima_articles/news-20080305-98602.html -# http://www.aswataliraq.info/look/article.tpl?id=2047&IdLanguage=17&IdPublication=4&NrArticle=71743&NrIssue=1&NrSection=10 -# -# We have published a short article in English about the change: -# https://www.timeanddate.com/news/time/iraq-dumps-daylight-saving.html - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Iraq 1982 only - May 1 0:00 1:00 - -Rule Iraq 1982 1984 - Oct 1 0:00 0 - -Rule Iraq 1983 only - Mar 31 0:00 1:00 - -Rule Iraq 1984 1985 - Apr 1 0:00 1:00 - -Rule Iraq 1985 1990 - Sep lastSun 1:00s 0 - -Rule Iraq 1986 1990 - Mar lastSun 1:00s 1:00 - -# IATA SSIM (1991/1996) says Apr 1 12:01am UTC; guess the ':01' is a typo. -# Shanks & Pottenger say Iraq did not observe DST 1992/1997; ignore this. -# -Rule Iraq 1991 2007 - Apr 1 3:00s 1:00 - -Rule Iraq 1991 2007 - Oct 1 3:00s 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Baghdad 2:57:40 - LMT 1890 - 2:57:36 - BMT 1918 # Baghdad Mean Time? - 3:00 - +03 1982 May - 3:00 Iraq +03/+04 - - -############################################################################### - -# Israel - -# From Ephraim Silverberg (2001-01-11): -# -# I coined "IST/IDT" circa 1988. Until then there were three -# different abbreviations in use: -# -# JST Jerusalem Standard Time [Danny Braniss, Hebrew University] -# IZT Israel Zonal (sic) Time [Prof. Haim Papo, Technion] -# EEST Eastern Europe Standard Time [used by almost everyone else] -# -# Since timezones should be called by country and not capital cities, -# I ruled out JST. As Israel is in Asia Minor and not Eastern Europe, -# EEST was equally unacceptable. Since "zonal" was not compatible with -# any other timezone abbreviation, I felt that 'IST' was the way to go -# and, indeed, it has received almost universal acceptance in timezone -# settings in Israeli computers. -# -# In any case, I am happy to share timezone abbreviations with India, -# high on my favorite-country list (and not only because my wife's -# family is from India). - -# From Shanks & Pottenger: -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Zion 1940 only - Jun 1 0:00 1:00 D -Rule Zion 1942 1944 - Nov 1 0:00 0 S -Rule Zion 1943 only - Apr 1 2:00 1:00 D -Rule Zion 1944 only - Apr 1 0:00 1:00 D -Rule Zion 1945 only - Apr 16 0:00 1:00 D -Rule Zion 1945 only - Nov 1 2:00 0 S -Rule Zion 1946 only - Apr 16 2:00 1:00 D -Rule Zion 1946 only - Nov 1 0:00 0 S -Rule Zion 1948 only - May 23 0:00 2:00 DD -Rule Zion 1948 only - Sep 1 0:00 1:00 D -Rule Zion 1948 1949 - Nov 1 2:00 0 S -Rule Zion 1949 only - May 1 0:00 1:00 D -Rule Zion 1950 only - Apr 16 0:00 1:00 D -Rule Zion 1950 only - Sep 15 3:00 0 S -Rule Zion 1951 only - Apr 1 0:00 1:00 D -Rule Zion 1951 only - Nov 11 3:00 0 S -Rule Zion 1952 only - Apr 20 2:00 1:00 D -Rule Zion 1952 only - Oct 19 3:00 0 S -Rule Zion 1953 only - Apr 12 2:00 1:00 D -Rule Zion 1953 only - Sep 13 3:00 0 S -Rule Zion 1954 only - Jun 13 0:00 1:00 D -Rule Zion 1954 only - Sep 12 0:00 0 S -Rule Zion 1955 only - Jun 11 2:00 1:00 D -Rule Zion 1955 only - Sep 11 0:00 0 S -Rule Zion 1956 only - Jun 3 0:00 1:00 D -Rule Zion 1956 only - Sep 30 3:00 0 S -Rule Zion 1957 only - Apr 29 2:00 1:00 D -Rule Zion 1957 only - Sep 22 0:00 0 S -Rule Zion 1974 only - Jul 7 0:00 1:00 D -Rule Zion 1974 only - Oct 13 0:00 0 S -Rule Zion 1975 only - Apr 20 0:00 1:00 D -Rule Zion 1975 only - Aug 31 0:00 0 S -Rule Zion 1985 only - Apr 14 0:00 1:00 D -Rule Zion 1985 only - Sep 15 0:00 0 S -Rule Zion 1986 only - May 18 0:00 1:00 D -Rule Zion 1986 only - Sep 7 0:00 0 S -Rule Zion 1987 only - Apr 15 0:00 1:00 D -Rule Zion 1987 only - Sep 13 0:00 0 S - -# From Avigdor Finkelstein (2014-03-05): -# I check the Parliament (Knesset) records and there it's stated that the -# [1988] transition should take place on Saturday night, when the Sabbath -# ends and changes to Sunday. -Rule Zion 1988 only - Apr 10 0:00 1:00 D -Rule Zion 1988 only - Sep 4 0:00 0 S - -# From Ephraim Silverberg -# (1997-03-04, 1998-03-16, 1998-12-28, 2000-01-17, 2000-07-25, 2004-12-22, -# and 2005-02-17): - -# According to the Office of the Secretary General of the Ministry of -# Interior, there is NO set rule for Daylight-Savings/Standard time changes. -# One thing is entrenched in law, however: that there must be at least 150 -# days of daylight savings time annually. From 1993-1998, the change to -# daylight savings time was on a Friday morning from midnight IST to -# 1 a.m IDT; up until 1998, the change back to standard time was on a -# Saturday night from midnight daylight savings time to 11 p.m. standard -# time. 1996 is an exception to this rule where the change back to standard -# time took place on Sunday night instead of Saturday night to avoid -# conflicts with the Jewish New Year. In 1999, the change to -# daylight savings time was still on a Friday morning but from -# 2 a.m. IST to 3 a.m. IDT; furthermore, the change back to standard time -# was also on a Friday morning from 2 a.m. IDT to 1 a.m. IST for -# 1999 only. In the year 2000, the change to daylight savings time was -# similar to 1999, but although the change back will be on a Friday, it -# will take place from 1 a.m. IDT to midnight IST. Starting in 2001, all -# changes to/from will take place at 1 a.m. old time, but now there is no -# rule as to what day of the week it will take place in as the start date -# (except in 2003) is the night after the Passover Seder (i.e. the eve -# of the 16th of Nisan in the lunar Hebrew calendar) and the end date -# (except in 2002) is three nights before Yom Kippur [Day of Atonement] -# (the eve of the 7th of Tishrei in the lunar Hebrew calendar). - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Zion 1989 only - Apr 30 0:00 1:00 D -Rule Zion 1989 only - Sep 3 0:00 0 S -Rule Zion 1990 only - Mar 25 0:00 1:00 D -Rule Zion 1990 only - Aug 26 0:00 0 S -Rule Zion 1991 only - Mar 24 0:00 1:00 D -Rule Zion 1991 only - Sep 1 0:00 0 S -Rule Zion 1992 only - Mar 29 0:00 1:00 D -Rule Zion 1992 only - Sep 6 0:00 0 S -Rule Zion 1993 only - Apr 2 0:00 1:00 D -Rule Zion 1993 only - Sep 5 0:00 0 S - -# The dates for 1994-1995 were obtained from Office of the Spokeswoman for the -# Ministry of Interior, Jerusalem, Israel. The spokeswoman can be reached by -# calling the office directly at 972-2-6701447 or 972-2-6701448. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Zion 1994 only - Apr 1 0:00 1:00 D -Rule Zion 1994 only - Aug 28 0:00 0 S -Rule Zion 1995 only - Mar 31 0:00 1:00 D -Rule Zion 1995 only - Sep 3 0:00 0 S - -# The dates for 1996 were determined by the Minister of Interior of the -# time, Haim Ramon. The official announcement regarding 1996-1998 -# (with the dates for 1997-1998 no longer being relevant) can be viewed at: -# -# ftp://ftp.cs.huji.ac.il/pub/tz/announcements/1996-1998.ramon.ps.gz -# -# The dates for 1997-1998 were altered by his successor, Rabbi Eli Suissa. -# -# The official announcements for the years 1997-1999 can be viewed at: -# -# ftp://ftp.cs.huji.ac.il/pub/tz/announcements/YYYY.ps.gz -# -# where YYYY is the relevant year. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Zion 1996 only - Mar 15 0:00 1:00 D -Rule Zion 1996 only - Sep 16 0:00 0 S -Rule Zion 1997 only - Mar 21 0:00 1:00 D -Rule Zion 1997 only - Sep 14 0:00 0 S -Rule Zion 1998 only - Mar 20 0:00 1:00 D -Rule Zion 1998 only - Sep 6 0:00 0 S -Rule Zion 1999 only - Apr 2 2:00 1:00 D -Rule Zion 1999 only - Sep 3 2:00 0 S - -# The Knesset Interior Committee has changed the dates for 2000 for -# the third time in just over a year and have set new dates for the -# years 2001-2004 as well. -# -# The official announcement for the start date of 2000 can be viewed at: -# -# ftp://ftp.cs.huji.ac.il/pub/tz/announcements/2000-start.ps.gz -# -# The official announcement for the end date of 2000 and the dates -# for the years 2001-2004 can be viewed at: -# -# ftp://ftp.cs.huji.ac.il/pub/tz/announcements/2000-2004.ps.gz - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Zion 2000 only - Apr 14 2:00 1:00 D -Rule Zion 2000 only - Oct 6 1:00 0 S -Rule Zion 2001 only - Apr 9 1:00 1:00 D -Rule Zion 2001 only - Sep 24 1:00 0 S -Rule Zion 2002 only - Mar 29 1:00 1:00 D -Rule Zion 2002 only - Oct 7 1:00 0 S -Rule Zion 2003 only - Mar 28 1:00 1:00 D -Rule Zion 2003 only - Oct 3 1:00 0 S -Rule Zion 2004 only - Apr 7 1:00 1:00 D -Rule Zion 2004 only - Sep 22 1:00 0 S - -# The proposed law agreed upon by the Knesset Interior Committee on -# 2005-02-14 is that, for 2005 and beyond, DST starts at 02:00 the -# last Friday before April 2nd (i.e. the last Friday in March or April -# 1st itself if it falls on a Friday) and ends at 02:00 on the Saturday -# night _before_ the fast of Yom Kippur. -# -# Those who can read Hebrew can view the announcement at: -# -# ftp://ftp.cs.huji.ac.il/pub/tz/announcements/2005+beyond.ps - -# From Paul Eggert (2012-10-26): -# I used Ephraim Silverberg's dst-israel.el program -# (2005-02-20) -# along with Ed Reingold's cal-hebrew in GNU Emacs 21.4, -# to generate the transitions from 2005 through 2012. -# (I replaced "lastFri" with "Fri>=26" by hand.) -# The spring transitions all correspond to the following Rule: -# -# Rule Zion 2005 2012 - Mar Fri>=26 2:00 1:00 D -# -# but older zic implementations (e.g., Solaris 8) do not support -# "Fri>=26" to mean April 1 in years like 2005, so for now we list the -# springtime transitions explicitly. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Zion 2005 only - Apr 1 2:00 1:00 D -Rule Zion 2005 only - Oct 9 2:00 0 S -Rule Zion 2006 2010 - Mar Fri>=26 2:00 1:00 D -Rule Zion 2006 only - Oct 1 2:00 0 S -Rule Zion 2007 only - Sep 16 2:00 0 S -Rule Zion 2008 only - Oct 5 2:00 0 S -Rule Zion 2009 only - Sep 27 2:00 0 S -Rule Zion 2010 only - Sep 12 2:00 0 S -Rule Zion 2011 only - Apr 1 2:00 1:00 D -Rule Zion 2011 only - Oct 2 2:00 0 S -Rule Zion 2012 only - Mar Fri>=26 2:00 1:00 D -Rule Zion 2012 only - Sep 23 2:00 0 S - -# From Ephraim Silverberg (2013-06-27): -# On June 23, 2013, the Israeli government approved changes to the -# Time Decree Law. The next day, the changes passed the First Reading -# in the Knesset. The law is expected to pass the Second and Third -# (final) Readings by the beginning of September 2013. -# -# As of 2013, DST starts at 02:00 on the Friday before the last Sunday -# in March. DST ends at 02:00 on the last Sunday of October. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Zion 2013 max - Mar Fri>=23 2:00 1:00 D -Rule Zion 2013 max - Oct lastSun 2:00 0 S - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Jerusalem 2:20:54 - LMT 1880 - 2:20:40 - JMT 1918 # Jerusalem Mean Time? - 2:00 Zion I%sT - - - -############################################################################### - -# Japan - -# '9:00' and 'JST' is from Guy Harris. - -# From Paul Eggert (1995-03-06): -# Today's _Asahi Evening News_ (page 4) reports that Japan had -# daylight saving between 1948 and 1951, but "the system was discontinued -# because the public believed it would lead to longer working hours." - -# From Mayumi Negishi in the 2005-08-10 Japan Times: -# http://www.japantimes.co.jp/cgi-bin/getarticle.pl5?nn20050810f2.htm -# Occupation authorities imposed daylight-saving time on Japan on -# [1948-05-01].... But lack of prior debate and the execution of -# daylight-saving time just three days after the bill was passed generated -# deep hatred of the concept.... The Diet unceremoniously passed a bill to -# dump the unpopular system in October 1951, less than a month after the San -# Francisco Peace Treaty was signed. (A government poll in 1951 showed 53% -# of the Japanese wanted to scrap daylight-saving time, as opposed to 30% who -# wanted to keep it.) - -# From Takayuki Nikai (2018-01-19): -# The source of information is Japanese law. -# http://www.shugiin.go.jp/internet/itdb_housei.nsf/html/houritsu/00219480428029.htm -# http://www.shugiin.go.jp/internet/itdb_housei.nsf/html/houritsu/00719500331039.htm -# ... In summary, it is written as follows. From 24:00 on the first Saturday -# in May, until 0:00 on the day after the second Saturday in September. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Japan 1948 only - May Sat>=1 24:00 1:00 D -Rule Japan 1948 1951 - Sep Sun>=9 0:00 0 S -Rule Japan 1949 only - Apr Sat>=1 24:00 1:00 D -Rule Japan 1950 1951 - May Sat>=1 24:00 1:00 D - -# From Hideyuki Suzuki (1998-11-09): -# 'Tokyo' usually stands for the former location of Tokyo Astronomical -# Observatory: 139° 44' 40.90" E (9h 18m 58.727s), 35° 39' 16.0" N. -# This data is from 'Rika Nenpyou (Chronological Scientific Tables) 1996' -# edited by National Astronomical Observatory of Japan.... -# JST (Japan Standard Time) has been used since 1888-01-01 00:00 (JST). -# The law is enacted on 1886-07-07. - -# From Hideyuki Suzuki (1998-11-16): -# The ordinance No. 51 (1886) established "standard time" in Japan, -# which stands for the time on 135° E. -# In the ordinance No. 167 (1895), "standard time" was renamed to "central -# standard time". And the same ordinance also established "western standard -# time", which stands for the time on 120° E.... But "western standard -# time" was abolished in the ordinance No. 529 (1937). In the ordinance No. -# 167, there is no mention regarding for what place western standard time is -# standard.... -# -# I wrote "ordinance" above, but I don't know how to translate. -# In Japanese it's "chokurei", which means ordinance from emperor. - -# From Yu-Cheng Chuang (2013-07-12): -# ...the Meiji Emperor announced Ordinance No. 167 of Meiji Year 28 "The clause -# about standard time" ... The adoption began from Jan 1, 1896. -# https://ja.wikisource.org/wiki/標準時ニ關スル件_(公布時) -# -# ...the Showa Emperor announced Ordinance No. 529 of Showa Year 12 ... which -# means the whole Japan territory, including later occupations, adopt Japan -# Central Time (UT+9). The adoption began on Oct 1, 1937. -# https://ja.wikisource.org/wiki/明治二十八年勅令第百六十七號標準時ニ關スル件中改正ノ件 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Tokyo 9:18:59 - LMT 1887 Dec 31 15:00u - 9:00 Japan J%sT -# Since 1938, all Japanese possessions have been like Asia/Tokyo. - -# Jordan -# -# From -# Jordan Week (1999-07-01) via Steffen Thorsen (1999-09-09): -# Clocks in Jordan were forwarded one hour on Wednesday at midnight, -# in accordance with the government's decision to implement summer time -# all year round. -# -# From -# Jordan Week (1999-09-30) via Steffen Thorsen (1999-11-09): -# Winter time starts today Thursday, 30 September. Clocks will be turned back -# by one hour. This is the latest government decision and it's final! -# The decision was taken because of the increase in working hours in -# government's departments from six to seven hours. -# -# From Paul Eggert (2005-11-22): -# Starting 2003 transitions are from Steffen Thorsen's web site timeanddate.com. -# -# From Steffen Thorsen (2005-11-23): -# For Jordan I have received multiple independent user reports every year -# about DST end dates, as the end-rule is different every year. -# -# From Steffen Thorsen (2006-10-01), after a heads-up from Hilal Malawi: -# http://www.petranews.gov.jo/nepras/2006/Sep/05/4000.htm -# "Jordan will switch to winter time on Friday, October 27". -# - -# From Steffen Thorsen (2009-04-02): -# This single one might be good enough, (2009-03-24, Arabic): -# http://petra.gov.jo/Artical.aspx?Lng=2&Section=8&Artical=95279 -# -# Google's translation: -# -# > The Council of Ministers decided in 2002 to adopt the principle of timely -# > submission of the summer at 60 minutes as of midnight on the last Thursday -# > of the month of March of each year. -# -# So - this means the midnight between Thursday and Friday since 2002. - -# From Arthur David Olson (2009-04-06): -# We still have Jordan switching to DST on Thursdays in 2000 and 2001. - -# From Steffen Thorsen (2012-10-25): -# Yesterday the government in Jordan announced that they will not -# switch back to standard time this winter, so the will stay on DST -# until about the same time next year (at least). -# http://www.petra.gov.jo/Public_News/Nws_NewsDetails.aspx?NewsID=88950 - -# From Steffen Thorsen (2013-12-11): -# Jordan Times and other sources say that Jordan is going back to -# UTC+2 on 2013-12-19 at midnight: -# http://jordantimes.com/govt-decides-to-switch-back-to-wintertime -# Official, in Arabic: -# http://www.petra.gov.jo/public_news/Nws_NewsDetails.aspx?Menu_ID=&Site_Id=2&lang=1&NewsID=133230&CatID=14 -# ... Our background/permalink about it -# https://www.timeanddate.com/news/time/jordan-reverses-dst-decision.html -# ... -# http://www.petra.gov.jo/Public_News/Nws_NewsDetails.aspx?lang=2&site_id=1&NewsID=133313&Type=P -# ... says midnight for the coming one and 1:00 for the ones in the future -# (and they will use DST again next year, using the normal schedule). - -# From Paul Eggert (2013-12-11): -# As Steffen suggested, consider the past 21-month experiment to be DST. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Jordan 1973 only - Jun 6 0:00 1:00 S -Rule Jordan 1973 1975 - Oct 1 0:00 0 - -Rule Jordan 1974 1977 - May 1 0:00 1:00 S -Rule Jordan 1976 only - Nov 1 0:00 0 - -Rule Jordan 1977 only - Oct 1 0:00 0 - -Rule Jordan 1978 only - Apr 30 0:00 1:00 S -Rule Jordan 1978 only - Sep 30 0:00 0 - -Rule Jordan 1985 only - Apr 1 0:00 1:00 S -Rule Jordan 1985 only - Oct 1 0:00 0 - -Rule Jordan 1986 1988 - Apr Fri>=1 0:00 1:00 S -Rule Jordan 1986 1990 - Oct Fri>=1 0:00 0 - -Rule Jordan 1989 only - May 8 0:00 1:00 S -Rule Jordan 1990 only - Apr 27 0:00 1:00 S -Rule Jordan 1991 only - Apr 17 0:00 1:00 S -Rule Jordan 1991 only - Sep 27 0:00 0 - -Rule Jordan 1992 only - Apr 10 0:00 1:00 S -Rule Jordan 1992 1993 - Oct Fri>=1 0:00 0 - -Rule Jordan 1993 1998 - Apr Fri>=1 0:00 1:00 S -Rule Jordan 1994 only - Sep Fri>=15 0:00 0 - -Rule Jordan 1995 1998 - Sep Fri>=15 0:00s 0 - -Rule Jordan 1999 only - Jul 1 0:00s 1:00 S -Rule Jordan 1999 2002 - Sep lastFri 0:00s 0 - -Rule Jordan 2000 2001 - Mar lastThu 0:00s 1:00 S -Rule Jordan 2002 2012 - Mar lastThu 24:00 1:00 S -Rule Jordan 2003 only - Oct 24 0:00s 0 - -Rule Jordan 2004 only - Oct 15 0:00s 0 - -Rule Jordan 2005 only - Sep lastFri 0:00s 0 - -Rule Jordan 2006 2011 - Oct lastFri 0:00s 0 - -Rule Jordan 2013 only - Dec 20 0:00 0 - -Rule Jordan 2014 max - Mar lastThu 24:00 1:00 S -Rule Jordan 2014 max - Oct lastFri 0:00s 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Amman 2:23:44 - LMT 1931 - 2:00 Jordan EE%sT - - -# Kazakhstan - -# From Kazakhstan Embassy's News Bulletin No. 11 -# (2005-03-21): -# The Government of Kazakhstan passed a resolution March 15 abolishing -# daylight saving time citing lack of economic benefits and health -# complications coupled with a decrease in productivity. -# -# From Branislav Kojic (in Astana) via Gwillim Law (2005-06-28): -# ... what happened was that the former Kazakhstan Eastern time zone -# was "blended" with the Central zone. Therefore, Kazakhstan now has -# two time zones, and difference between them is one hour. The zone -# closer to UTC is the former Western zone (probably still called the -# same), encompassing four provinces in the west: Aqtöbe, Atyraū, -# Mangghystaū, and West Kazakhstan. The other zone encompasses -# everything else.... I guess that would make Kazakhstan time zones -# de jure UTC+5 and UTC+6 respectively. - -# From Stepan Golosunov (2016-03-27): -# Review of the linked documents from http://adilet.zan.kz/ -# produced the following data for post-1991 Kazakhstan: -# -# 0. Act of the Cabinet of Ministers of the USSR -# from 1991-02-04 No. 20 -# http://pravo.gov.ru/proxy/ips/?docbody=&nd=102010545 -# removed the extra hour ("decree time") on the territory of the USSR -# starting with the last Sunday of March 1991. -# It also allowed (but not mandated) Kazakh SSR, Kirghiz SSR, Tajik SSR, -# Turkmen SSR and Uzbek SSR to not have "summer" time. -# -# The 1992-01-13 act also refers to the act of the Cabinet of Ministers -# of the Kazakh SSR from 1991-03-20 No. 170 "About the act of the Cabinet -# of Ministers of the USSR from 1991-02-04 No. 20" but I didn't found its -# text. -# -# According to Izvestia newspaper No. 68 (23334) from 1991-03-20 -# (page 6; available at http://libinfo.org/newsr/newsr2574.djvu via -# http://libinfo.org/index.php?id=58564) on 1991-03-31 at 2:00 during -# transition to "summer" time: -# Republic of Georgia, Latvian SSR, Lithuanian SSR, SSR Moldova, -# Estonian SSR; Komi ASSR; Kaliningrad oblast; Nenets autonomous okrug -# were to move clocks 1 hour forward. -# Kazakh SSR (excluding Uralsk oblast); Republic of Kyrgyzstan, Tajik -# SSR; Andijan, Jizzakh, Namangan, Sirdarya, Tashkent, Fergana oblasts -# of the Uzbek SSR were to move clocks 1 hour backwards. -# Other territories were to not move clocks. -# When the "summer" time would end on 1991-09-29, clocks were to be -# moved 1 hour backwards on the territory of the USSR excluding -# Kazakhstan, Kirghizia, Uzbekistan, Turkmenia, Tajikistan. -# -# Apparently there were last minute changes. Apparently Kazakh act No. 170 -# was one of such changes. -# -# https://ru.wikipedia.org/wiki/Декретное время -# claims that Sovetskaya Rossiya newspaper on 1991-03-29 published that -# Nenets autonomous okrug, Komi and Kazakhstan (excluding Uralsk oblast) -# were to not move clocks and Uralsk oblast was to move clocks -# forward; on 1991-09-29 Kazakhstan was to move clocks backwards. -# (Probably there were changes even after that publication. There is an -# article claiming that Kaliningrad oblast decided on 1991-03-29 to not -# move clocks.) -# -# This implies that on 1991-03-31 Asia/Oral remained on +04/+05 while -# the rest of Kazakhstan switched from +06/+07 to +05/06 or from +05/06 -# to +04/+05. It's unclear how Qyzylorda oblast moved into the fifth -# time belt. (By switching from +04/+05 to +05/+06 on 1991-09-29?) ... -# -# 1. Act of the Cabinet of Ministers of the Republic of Kazakhstan -# from 1992-01-13 No. 28 -# http://adilet.zan.kz/rus/docs/P920000028_ -# (text includes modification from the 1996 act) -# introduced new rules for calculation of time, mirroring Russian -# 1992-01-08 act. It specified that time would be calculated -# according to time belts plus extra hour ("decree time"), moved clocks -# on the whole territory of Kazakhstan 1 hour forward on 1992-01-19 at -# 2:00, specified DST rules. It acknowledged that Kazakhstan was -# located in the fourth and the fifth time belts and specified the -# border between them to be located east of Qostanay and Aktyubinsk -# oblasts (notably including Turgai and Qyzylorda oblasts into the fifth -# time belt). -# -# This means switch on 1992-01-19 at 2:00 from +04/+05 to +05/+06 for -# Asia/Aqtau, Asia/Aqtobe, Asia/Oral, Atyraū and Qostanay oblasts; from -# +05/+06 to +06/+07 for Asia/Almaty and Asia/Qyzylorda (and Arkalyk).... -# -# 2. Act of the Cabinet of Ministers of the Republic of Kazakhstan -# from 1992-03-27 No. 284 -# http://adilet.zan.kz/rus/docs/P920000284_ -# cancels extra hour ("decree time") for Uralsk and Qyzylorda oblasts -# since the last Sunday of March 1992, while keeping them in the fourth -# and the fifth time belts respectively. -# -# 3. Order of the Prime Minister of the Republic of Kazakhstan -# from 1994-09-23 No. 384 -# http://adilet.zan.kz/rus/docs/R940000384_ -# cancels the extra hour ("decree time") on the territory of Mangghystaū -# oblast since the last Sunday of September 1994 (saying that time on -# the territory would correspond to the third time belt as a -# result).... -# -# 4. Act of the Government of the Republic of Kazakhstan -# from 1996-05-08 No. 575 -# http://adilet.zan.kz/rus/docs/P960000575_ -# amends the 1992-01-13 act to end summer time in October instead -# of September, mirroring identical Russian change from 1996-04-23 act. -# -# 5. Act of the Government of the Republic of Kazakhstan -# from 1999-03-26 No. 305 -# http://adilet.zan.kz/rus/docs/P990000305_ -# cancels the extra hour ("decree time") for Atyraū oblast since the -# last Sunday of March 1999 while retaining the oblast in the fourth -# time belt. -# -# This means change from +05/+06 to +04/+05.... -# -# 6. Act of the Government of the Republic of Kazakhstan -# from 2000-11-23 No. 1749 -# http://adilet.zan.kz/rus/archive/docs/P000001749_/23.11.2000 -# replaces the previous five documents. -# -# The only changes I noticed are in definition of the border between the -# fourth and the fifth time belts. They account for changes in spelling -# and administrative division (splitting of Turgai oblast in 1997 -# probably changed time in territories incorporated into Qostanay oblast -# (including Arkalyk) from +06/+07 to +05/+06) and move Qyzylorda oblast -# from being in the fifth time belt and not using decree time into the -# fourth time belt (no change in practice). -# -# 7. Act of the Government of the Republic of Kazakhstan -# from 2003-12-29 No. 1342 -# http://adilet.zan.kz/rus/docs/P030001342_ -# modified the 2000-11-23 act. No relevant changes, apparently. -# -# 8. Act of the Government of the Republic of Kazakhstan -# from 2004-07-20 No. 775 -# http://adilet.zan.kz/rus/archive/docs/P040000775_/20.07.2004 -# modified the 2000-11-23 act to move Qostanay and Qyzylorda oblasts into -# the fifth time belt and add Aktobe oblast to the list of regions not -# using extra hour ("decree time"), leaving Kazakhstan with only 2 time -# zones (+04/+05 and +06/+07). The changes were to be implemented -# during DST transitions in 2004 and 2005 but the acts got radically -# amended before implementation happened. -# -# 9. Act of the Government of the Republic of Kazakhstan -# from 2004-09-15 No. 1059 -# http://adilet.zan.kz/rus/docs/P040001059_ -# modified the 2000-11-23 act to remove exceptions from the "decree time" -# (leaving Kazakhstan in +05/+06 and +06/+07 zones), amended the -# 2004-07-20 act to implement changes for Atyraū, West Kazakhstan, -# Qostanay, Qyzylorda and Mangghystaū oblasts by not moving clocks -# during the 2004 transition to "winter" time. -# -# This means transition from +04/+05 to +05/+06 for Atyraū oblast (no -# zone currently), Asia/Oral, Asia/Aqtau and transition from +05/+06 to -# +06/+07 for Qostanay oblast (Qostanay and Arkalyk, no zones currently) -# and Asia/Qyzylorda on 2004-10-31 at 3:00.... -# -# 10. Act of the Government of the Republic of Kazakhstan -# from 2005-03-15 No. 231 -# http://adilet.zan.kz/rus/docs/P050000231_ -# removes DST provisions from the 2000-11-23 act, removes most of the -# (already implemented) provisions from the 2004-07-20 and 2004-09-15 -# acts, comes into effect 10 days after official publication. -# The only practical effect seems to be the abolition of the summer -# time. -# -# Unamended version of the act of the Government of the Russian Federation -# No. 23 from 1992-01-08 [See 'europe' file for details]. -# Kazakh 1992-01-13 act appears to provide the same rules and 1992-03-27 -# act was to be enacted on the last Sunday of March 1992. - -# From Stepan Golosunov (2016-11-08): -# Turgai reorganization should affect only southern part of Qostanay -# oblast. Which should probably be separated into Asia/Arkalyk zone. -# (There were also 1970, 1988 and 1990 Turgai oblast reorganizations -# according to wikipedia.) -# -# [For Qostanay] http://www.ng.kz/gazeta/195/hranit/ -# suggests that clocks were to be moved 40 minutes backwards on -# 1920-01-01 to the fourth time belt. But I do not understand -# how that could happen.... -# -# [For Atyrau and Oral] 1919 decree -# (http://www.worldtimezone.com/dst_news/dst_news_russia-1919-02-08.html -# and in Byalokoz) lists Ural river (plus 10 versts on its left bank) in -# the third time belt (before 1930 this means +03). - -# From Paul Eggert (2016-12-06): -# The tables below reflect Golosunov's remarks, with exceptions as noted. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -# -# Almaty (formerly Alma-Ata), representing most locations in Kazakhstan -# This includes KZ-AKM, KZ-ALA, KZ-ALM, KZ-AST, KZ-BAY, KZ-VOS, KZ-ZHA, -# KZ-KAR, KZ-SEV, KZ-PAV, and KZ-YUZ. -Zone Asia/Almaty 5:07:48 - LMT 1924 May 2 # or Alma-Ata - 5:00 - +05 1930 Jun 21 - 6:00 RussiaAsia +06/+07 1991 Mar 31 2:00s - 5:00 RussiaAsia +05/+06 1992 Jan 19 2:00s - 6:00 RussiaAsia +06/+07 2004 Oct 31 2:00s - 6:00 - +06 -# Qyzylorda (aka Kyzylorda, Kizilorda, Kzyl-Orda, etc.) (KZ-KZY) -# This currently includes Qostanay (aka Kostanay, Kustanay) (KZ-KUS); -# see comments below. -Zone Asia/Qyzylorda 4:21:52 - LMT 1924 May 2 - 4:00 - +04 1930 Jun 21 - 5:00 - +05 1981 Apr 1 - 5:00 1:00 +06 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00s - 4:00 RussiaAsia +04/+05 1991 Sep 29 2:00s - 5:00 RussiaAsia +05/+06 1992 Jan 19 2:00s - 6:00 RussiaAsia +06/+07 1992 Mar 29 2:00s - 5:00 RussiaAsia +05/+06 2004 Oct 31 2:00s - 6:00 - +06 -# The following zone is like Asia/Qyzylorda except for being one -# hour earlier from 1991-09-29 to 1992-03-29. The 1991/2 rules for -# Qostanay are unclear partly because of the 1997 Turgai -# reorganization, so this zone is commented out for now. -#Zone Asia/Qostanay 4:14:20 - LMT 1924 May 2 -# 4:00 - +04 1930 Jun 21 -# 5:00 - +05 1981 Apr 1 -# 5:00 1:00 +06 1981 Oct 1 -# 6:00 - +06 1982 Apr 1 -# 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00s -# 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00s -# 5:00 RussiaAsia +05/+06 2004 Oct 31 2:00s -# 6:00 - +06 -# -# Aqtöbe (aka Aktobe, formerly Aktyubinsk) (KZ-AKT) -Zone Asia/Aqtobe 3:48:40 - LMT 1924 May 2 - 4:00 - +04 1930 Jun 21 - 5:00 - +05 1981 Apr 1 - 5:00 1:00 +06 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00s - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00s - 5:00 RussiaAsia +05/+06 2004 Oct 31 2:00s - 5:00 - +05 -# Mangghystaū (KZ-MAN) -# Aqtau was not founded until 1963, but it represents an inhabited region, -# so include time stamps before 1963. -Zone Asia/Aqtau 3:21:04 - LMT 1924 May 2 - 4:00 - +04 1930 Jun 21 - 5:00 - +05 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00s - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00s - 5:00 RussiaAsia +05/+06 1994 Sep 25 2:00s - 4:00 RussiaAsia +04/+05 2004 Oct 31 2:00s - 5:00 - +05 -# Atyraū (KZ-ATY) is like Mangghystaū except it switched from -# +04/+05 to +05/+06 in spring 1999, not fall 1994. -Zone Asia/Atyrau 3:27:44 - LMT 1924 May 2 - 3:00 - +03 1930 Jun 21 - 5:00 - +05 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00s - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00s - 5:00 RussiaAsia +05/+06 1999 Mar 28 2:00s - 4:00 RussiaAsia +04/+05 2004 Oct 31 2:00s - 5:00 - +05 -# West Kazakhstan (KZ-ZAP) -# From Paul Eggert (2016-03-18): -# The 1989 transition is from USSR act No. 227 (1989-03-14). -Zone Asia/Oral 3:25:24 - LMT 1924 May 2 # or Ural'sk - 3:00 - +03 1930 Jun 21 - 5:00 - +05 1981 Apr 1 - 5:00 1:00 +06 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1989 Mar 26 2:00s - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00s - 5:00 RussiaAsia +05/+06 1992 Mar 29 2:00s - 4:00 RussiaAsia +04/+05 2004 Oct 31 2:00s - 5:00 - +05 - -# Kyrgyzstan (Kirgizstan) -# Transitions through 1991 are from Shanks & Pottenger. - -# From Paul Eggert (2005-08-15): -# According to an article dated today in the Kyrgyzstan Development Gateway -# http://eng.gateway.kg/cgi-bin/page.pl?id=1&story_name=doc9979.shtml -# Kyrgyzstan is canceling the daylight saving time system. I take the article -# to mean that they will leave their clocks at 6 hours ahead of UTC. -# From Malik Abdugaliev (2005-09-21): -# Our government cancels daylight saving time 6th of August 2005. -# From 2005-08-12 our GMT-offset is +6, w/o any daylight saving. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Kyrgyz 1992 1996 - Apr Sun>=7 0:00s 1:00 - -Rule Kyrgyz 1992 1996 - Sep lastSun 0:00 0 - -Rule Kyrgyz 1997 2005 - Mar lastSun 2:30 1:00 - -Rule Kyrgyz 1997 2004 - Oct lastSun 2:30 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Bishkek 4:58:24 - LMT 1924 May 2 - 5:00 - +05 1930 Jun 21 - 6:00 RussiaAsia +06/+07 1991 Mar 31 2:00s - 5:00 RussiaAsia +05/+06 1991 Aug 31 2:00 - 5:00 Kyrgyz +05/+06 2005 Aug 12 - 6:00 - +06 - -############################################################################### - -# Korea (North and South) - -# From Annie I. Bang (2006-07-10): -# http://www.koreaherald.com/view.php?ud=200607100012 -# Korea ran a daylight saving program from 1949-61 but stopped it -# during the 1950-53 Korean War. The system was temporarily enforced -# between 1987 and 1988 ... - -# From Sanghyuk Jung (2014-10-29): -# https://mm.icann.org/pipermail/tz/2014-October/021830.html -# According to the Korean Wikipedia -# https://ko.wikipedia.org/wiki/한국_표준시 -# [oldid=12896437 2014-09-04 08:03 UTC] -# DST in Republic of Korea was as follows.... And I checked old -# newspapers in Korean, all articles correspond with data in Wikipedia. -# For example, the article in 1948 (Korean Language) proved that DST -# started at June 1 in that year. For another example, the article in -# 1988 said that DST started at 2:00 AM in that year. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule ROK 1948 only - Jun 1 0:00 1:00 D -Rule ROK 1948 only - Sep 13 0:00 0 S -Rule ROK 1949 only - Apr 3 0:00 1:00 D -Rule ROK 1949 1951 - Sep Sun>=8 0:00 0 S -Rule ROK 1950 only - Apr 1 0:00 1:00 D -Rule ROK 1951 only - May 6 0:00 1:00 D -Rule ROK 1955 only - May 5 0:00 1:00 D -Rule ROK 1955 only - Sep 9 0:00 0 S -Rule ROK 1956 only - May 20 0:00 1:00 D -Rule ROK 1956 only - Sep 30 0:00 0 S -Rule ROK 1957 1960 - May Sun>=1 0:00 1:00 D -Rule ROK 1957 1960 - Sep Sun>=18 0:00 0 S -Rule ROK 1987 1988 - May Sun>=8 2:00 1:00 D -Rule ROK 1987 1988 - Oct Sun>=8 3:00 0 S - -# From Paul Eggert (2016-08-23): -# The Korean Wikipedia entry gives the following sources for UT offsets: -# -# 1908: Official Journal Article No. 3994 (decree No. 5) -# 1912: Governor-General of Korea Official Gazette Issue No. 367 -# (Announcement No. 338) -# 1954: Presidential Decree No. 876 (1954-03-17) -# 1961: Law No. 676 (1961-08-07) -# -# (Another source "1987: Law No. 3919 (1986-12-31)" was in the 2014-10-30 -# edition of the Korean Wikipedia entry.) -# -# I guessed that time zone abbreviations through 1945 followed the same -# rules as discussed under Taiwan, with nominal switches from JST to KST -# when the respective cities were taken over by the Allies after WWII. -# -# For Pyongyang, guess no changes from World War II until 2015, as we -# have no information otherwise. - -# From Steffen Thorsen (2015-08-07): -# According to many news sources, North Korea is going to change to -# the 8:30 time zone on August 15, one example: -# http://www.bbc.com/news/world-asia-33815049 -# -# From Paul Eggert (2015-08-15): -# Bells rang out midnight (00:00) Friday as part of the celebrations. See: -# Talmadge E. North Korea celebrates new time zone, 'Pyongyang Time' -# http://news.yahoo.com/north-korea-celebrates-time-zone-pyongyang-time-164038128.html -# There is no common English-language abbreviation for this time zone. -# Use KST, as that's what we already use for 1954-1961 in ROK. - -# From Kang Seonghoon (2018-04-29): -# North Korea will revert its time zone from UTC+8:30 (PYT; Pyongyang -# Time) back to UTC+9 (KST; Korea Standard Time). -# -# From Seo Sanghyeon (2018-04-30): -# Rodong Sinmun 2018-04-30 announced Pyongyang Time transition plan. -# https://www.nknews.org/kcna/wp-content/uploads/sites/5/2018/04/rodong-2018-04-30.pdf -# ... the transition date is 2018-05-05 ... Citation should be Decree -# No. 2232 of April 30, 2018, of the Presidium of the Supreme People's -# Assembly, as published in Rodong Sinmun. -# From Tim Parenti (2018-04-29): -# It appears to be the front page story at the top in the right-most column. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Seoul 8:27:52 - LMT 1908 Apr 1 - 8:30 - KST 1912 Jan 1 - 9:00 - JST 1945 Sep 8 - 9:00 - KST 1954 Mar 21 - 8:30 ROK K%sT 1961 Aug 10 - 9:00 ROK K%sT -Zone Asia/Pyongyang 8:23:00 - LMT 1908 Apr 1 - 8:30 - KST 1912 Jan 1 - 9:00 - JST 1945 Aug 24 - 9:00 - KST 2015 Aug 15 00:00 - 8:30 - KST 2018 May 5 - 9:00 - KST - -############################################################################### - -# Kuwait -# See Asia/Riyadh. - -# Laos -# See Asia/Bangkok. - - -# Lebanon -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Lebanon 1920 only - Mar 28 0:00 1:00 S -Rule Lebanon 1920 only - Oct 25 0:00 0 - -Rule Lebanon 1921 only - Apr 3 0:00 1:00 S -Rule Lebanon 1921 only - Oct 3 0:00 0 - -Rule Lebanon 1922 only - Mar 26 0:00 1:00 S -Rule Lebanon 1922 only - Oct 8 0:00 0 - -Rule Lebanon 1923 only - Apr 22 0:00 1:00 S -Rule Lebanon 1923 only - Sep 16 0:00 0 - -Rule Lebanon 1957 1961 - May 1 0:00 1:00 S -Rule Lebanon 1957 1961 - Oct 1 0:00 0 - -Rule Lebanon 1972 only - Jun 22 0:00 1:00 S -Rule Lebanon 1972 1977 - Oct 1 0:00 0 - -Rule Lebanon 1973 1977 - May 1 0:00 1:00 S -Rule Lebanon 1978 only - Apr 30 0:00 1:00 S -Rule Lebanon 1978 only - Sep 30 0:00 0 - -Rule Lebanon 1984 1987 - May 1 0:00 1:00 S -Rule Lebanon 1984 1991 - Oct 16 0:00 0 - -Rule Lebanon 1988 only - Jun 1 0:00 1:00 S -Rule Lebanon 1989 only - May 10 0:00 1:00 S -Rule Lebanon 1990 1992 - May 1 0:00 1:00 S -Rule Lebanon 1992 only - Oct 4 0:00 0 - -Rule Lebanon 1993 max - Mar lastSun 0:00 1:00 S -Rule Lebanon 1993 1998 - Sep lastSun 0:00 0 - -Rule Lebanon 1999 max - Oct lastSun 0:00 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Beirut 2:22:00 - LMT 1880 - 2:00 Lebanon EE%sT - -# Malaysia -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule NBorneo 1935 1941 - Sep 14 0:00 0:20 - -Rule NBorneo 1935 1941 - Dec 14 0:00 0 - -# -# peninsular Malaysia -# taken from Mok Ly Yng (2003-10-30) -# http://www.math.nus.edu.sg/aslaksen/teaching/timezone.html -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Kuala_Lumpur 6:46:46 - LMT 1901 Jan 1 - 6:55:25 - SMT 1905 Jun 1 # Singapore M.T. - 7:00 - +07 1933 Jan 1 - 7:00 0:20 +0720 1936 Jan 1 - 7:20 - +0720 1941 Sep 1 - 7:30 - +0730 1942 Feb 16 - 9:00 - +09 1945 Sep 12 - 7:30 - +0730 1982 Jan 1 - 8:00 - +08 -# Sabah & Sarawak -# From Paul Eggert (2014-08-12): -# The data entries here are mostly from Shanks & Pottenger, but the 1942, 1945 -# and 1982 transition dates are from Mok Ly Yng. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Kuching 7:21:20 - LMT 1926 Mar - 7:30 - +0730 1933 - 8:00 NBorneo +08/+0820 1942 Feb 16 - 9:00 - +09 1945 Sep 12 - 8:00 - +08 - -# Maldives -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Indian/Maldives 4:54:00 - LMT 1880 # Malé - 4:54:00 - MMT 1960 # Malé Mean Time - 5:00 - +05 - -# Mongolia - -# Shanks & Pottenger say that Mongolia has three time zones, but -# The USNO (1995-12-21) and the CIA map Standard Time Zones of the World -# (2005-03) both say that it has just one. - -# From Oscar van Vlijmen (1999-12-11): -# General Information Mongolia -# (1999-09) -# "Time: Mongolia has two time zones. Three westernmost provinces of -# Bayan-Ölgii, Uvs, and Hovd are one hour earlier than the capital city, and -# the rest of the country follows the Ulaanbaatar time, which is UTC/GMT plus -# eight hours." - -# From Rives McDow (1999-12-13): -# Mongolia discontinued the use of daylight savings time in 1999; 1998 -# being the last year it was implemented. The dates of implementation I am -# unsure of, but most probably it was similar to Russia, except for the time -# of implementation may have been different.... -# Some maps in the past have indicated that there was an additional time -# zone in the eastern part of Mongolia, including the provinces of Dornod, -# Sükhbaatar, and possibly Khentii. - -# From Paul Eggert (1999-12-15): -# Naming and spelling is tricky in Mongolia. -# We'll use Hovd (also spelled Chovd and Khovd) to represent the west zone; -# the capital of the Hovd province is sometimes called Hovd, sometimes Dund-Us, -# and sometimes Jirgalanta (with variant spellings), but the name Hovd -# is good enough for our purposes. - -# From Rives McDow (2001-05-13): -# In addition to Mongolia starting daylight savings as reported earlier -# (adopted DST on 2001-04-27 02:00 local time, ending 2001-09-28), -# there are three time zones. -# -# Provinces [at 7:00]: Bayan-Ölgii, Uvs, Khovd, Zavkhan, Govi-Altai -# Provinces [at 8:00]: Khövsgöl, Bulgan, Arkhangai, Khentii, Töv, -# Bayankhongor, Övörkhangai, Dundgovi, Dornogovi, Ömnögovi -# Provinces [at 9:00]: Dornod, Sükhbaatar -# -# [The province of Selenge is omitted from the above lists.] - -# From Ganbold Ts., Ulaanbaatar (2004-04-17): -# Daylight saving occurs at 02:00 local time last Saturday of March. -# It will change back to normal at 02:00 local time last Saturday of -# September.... As I remember this rule was changed in 2001. -# -# From Paul Eggert (2004-04-17): -# For now, assume Rives McDow's informant got confused about Friday vs -# Saturday, and that his 2001 dates should have 1 added to them. - -# From Paul Eggert (2005-07-26): -# We have wildly conflicting information about Mongolia's time zones. -# Bill Bonnet (2005-05-19) reports that the US Embassy in Ulaanbaatar says -# there is only one time zone and that DST is observed, citing Microsoft -# Windows XP as the source. Risto Nykänen (2005-05-16) reports that -# travelmongolia.org says there are two time zones (UT +07, +08) with no DST. -# Oscar van Vlijmen (2005-05-20) reports that the Mongolian Embassy in -# Washington, DC says there are two time zones, with DST observed. -# He also found -# http://ubpost.mongolnews.mn/index.php?subaction=showcomments&id=1111634894&archive=&start_from=&ucat=1& -# which also says that there is DST, and which has a comment by "Toddius" -# (2005-03-31 06:05 +0700) saying "Mongolia actually has 3.5 time zones. -# The West (OLGII) is +7 GMT, most of the country is ULAT is +8 GMT -# and some Eastern provinces are +9 GMT but Sükhbaatar Aimag is SUHK +8.5 GMT. -# The SUKH timezone is new this year, it is one of the few things the -# parliament passed during the tumultuous winter session." -# For now, let's ignore this information, until we have more confirmation. - -# From Ganbold Ts. (2007-02-26): -# Parliament of Mongolia has just changed the daylight-saving rule in February. -# They decided not to adopt daylight-saving time.... -# http://www.mongolnews.mn/index.php?module=unuudur&sec=view&id=15742 - -# From Deborah Goldsmith (2008-03-30): -# We received a bug report claiming that the tz database UTC offset for -# Asia/Choibalsan (GMT+09:00) is incorrect, and that it should be GMT -# +08:00 instead. Different sources appear to disagree with the tz -# database on this, e.g.: -# -# https://www.timeanddate.com/worldclock/city.html?n=1026 -# http://www.worldtimeserver.com/current_time_in_MN.aspx -# -# both say GMT+08:00. - -# From Steffen Thorsen (2008-03-31): -# eznis airways, which operates several domestic flights, has a flight -# schedule here: -# http://www.eznis.com/Container.jsp?id=112 -# (click the English flag for English) -# -# There it appears that flights between Choibalsan and Ulaanbaatar arrive -# about 1:35 - 1:50 hours later in local clock time, no matter the -# direction, while Ulaanbaatar-Khovd takes 2 hours in the Eastern -# direction and 3:35 back, which indicates that Ulaanbaatar and Khovd are -# in different time zones (like we know about), while Choibalsan and -# Ulaanbaatar are in the same time zone (correction needed). - -# From Arthur David Olson (2008-05-19): -# Assume that Choibalsan is indeed offset by 8:00. -# XXX--in the absence of better information, assume that transition -# was at the start of 2008-03-31 (the day of Steffen Thorsen's report); -# this is almost surely wrong. - -# From Ganbold Tsagaankhuu (2015-03-10): -# It seems like yesterday Mongolian Government meeting has concluded to use -# daylight saving time in Mongolia.... Starting at 2:00AM of last Saturday of -# March 2015, daylight saving time starts. And 00:00AM of last Saturday of -# September daylight saving time ends. Source: -# http://zasag.mn/news/view/8969 - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Mongol 1983 1984 - Apr 1 0:00 1:00 - -Rule Mongol 1983 only - Oct 1 0:00 0 - -# Shanks & Pottenger and IATA SSIM say 1990s switches occurred at 00:00, -# but McDow says the 2001 switches occurred at 02:00. Also, IATA SSIM -# (1996-09) says 1996-10-25. Go with Shanks & Pottenger through 1998. -# -# Shanks & Pottenger say that the Sept. 1984 through Sept. 1990 switches -# in Choibalsan (more precisely, in Dornod and Sükhbaatar) took place -# at 02:00 standard time, not at 00:00 local time as in the rest of -# the country. That would be odd, and possibly is a result of their -# correction of 02:00 (in the previous edition) not being done correctly -# in the latest edition; so ignore it for now. - -# From Ganbold Tsagaankhuu (2017-02-09): -# Mongolian Government meeting has concluded today to cancel daylight -# saving time adoption in Mongolia. Source: http://zasag.mn/news/view/16192 - -Rule Mongol 1985 1998 - Mar lastSun 0:00 1:00 - -Rule Mongol 1984 1998 - Sep lastSun 0:00 0 - -# IATA SSIM (1999-09) says Mongolia no longer observes DST. -Rule Mongol 2001 only - Apr lastSat 2:00 1:00 - -Rule Mongol 2001 2006 - Sep lastSat 2:00 0 - -Rule Mongol 2002 2006 - Mar lastSat 2:00 1:00 - -Rule Mongol 2015 2016 - Mar lastSat 2:00 1:00 - -Rule Mongol 2015 2016 - Sep lastSat 0:00 0 - - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -# Hovd, a.k.a. Chovd, Dund-Us, Dzhargalant, Khovd, Jirgalanta -Zone Asia/Hovd 6:06:36 - LMT 1905 Aug - 6:00 - +06 1978 - 7:00 Mongol +07/+08 -# Ulaanbaatar, a.k.a. Ulan Bataar, Ulan Bator, Urga -Zone Asia/Ulaanbaatar 7:07:32 - LMT 1905 Aug - 7:00 - +07 1978 - 8:00 Mongol +08/+09 -# Choibalsan, a.k.a. Bajan Tümen, Bajan Tumen, Chojbalsan, -# Choybalsan, Sanbejse, Tchoibalsan -Zone Asia/Choibalsan 7:38:00 - LMT 1905 Aug - 7:00 - +07 1978 - 8:00 - +08 1983 Apr - 9:00 Mongol +09/+10 2008 Mar 31 - 8:00 Mongol +08/+09 - -# Nepal -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Kathmandu 5:41:16 - LMT 1920 - 5:30 - +0530 1986 - 5:45 - +0545 - -# Oman -# See Asia/Dubai. - -# Pakistan - -# From Rives McDow (2002-03-13): -# I have been advised that Pakistan has decided to adopt dst on a -# TRIAL basis for one year, starting 00:01 local time on April 7, 2002 -# and ending at 00:01 local time October 6, 2002. This is what I was -# told, but I believe that the actual time of change may be 00:00; the -# 00:01 was to make it clear which day it was on. - -# From Paul Eggert (2002-03-15): -# Jesper Nørgaard found this URL: -# http://www.pak.gov.pk/public/news/app/app06_dec.htm -# (dated 2001-12-06) which says that the Cabinet adopted a scheme "to -# advance the clocks by one hour on the night between the first -# Saturday and Sunday of April and revert to the original position on -# 15th October each year". This agrees with McDow's 04-07 at 00:00, -# but disagrees about the October transition, and makes it sound like -# it's not on a trial basis. Also, the "between the first Saturday -# and Sunday of April" phrase, if taken literally, means that the -# transition takes place at 00:00 on the first Sunday on or after 04-02. - -# From Paul Eggert (2003-02-09): -# DAWN reported on 2002-10-05 -# that 2002 DST ended that day at midnight. Go with McDow for now. - -# From Steffen Thorsen (2003-03-14): -# According to http://www.dawn.com/2003/03/07/top15.htm -# there will be no DST in Pakistan this year: -# -# ISLAMABAD, March 6: Information and Media Development Minister Sheikh -# Rashid Ahmed on Thursday said the cabinet had reversed a previous -# decision to advance clocks by one hour in summer and put them back by -# one hour in winter with the aim of saving light hours and energy. -# -# The minister told a news conference that the experiment had rather -# shown 8 per cent higher consumption of electricity. - -# From Alex Krivenyshev (2008-05-15): -# -# Here is an article that Pakistan plan to introduce Daylight Saving Time -# on June 1, 2008 for 3 months. -# -# "... The federal cabinet on Wednesday announced a new conservation plan to -# help reduce load shedding by approving the closure of commercial centres at -# 9pm and moving clocks forward by one hour for the next three months. ...." -# -# http://www.worldtimezone.com/dst_news/dst_news_pakistan01.html -# http://www.dailytimes.com.pk/default.asp?page=2008%5C05%5C15%5Cstory_15-5-2008_pg1_4 - -# From Arthur David Olson (2008-05-19): -# XXX--midnight transitions is a guess; 2008 only is a guess. - -# From Alexander Krivenyshev (2008-08-28): -# Pakistan government has decided to keep the watches one-hour advanced -# for another 2 months - plan to return to Standard Time on October 31 -# instead of August 31. -# -# http://www.worldtimezone.com/dst_news/dst_news_pakistan02.html -# http://dailymailnews.com/200808/28/news/dmbrn03.html - -# From Alexander Krivenyshev (2009-04-08): -# Based on previous media reports that "... proposed plan to -# advance clocks by one hour from May 1 will cause disturbance -# to the working schedules rather than bringing discipline in -# official working." -# http://www.thenews.com.pk/daily_detail.asp?id=171280 -# -# recent news that instead of May 2009 - Pakistan plan to -# introduce DST from April 15, 2009 -# -# FYI: Associated Press Of Pakistan -# April 08, 2009 -# Cabinet okays proposal to advance clocks by one hour from April 15 -# http://www.app.com.pk/en_/index.php?option=com_content&task=view&id=73043&Itemid=1 -# http://www.worldtimezone.com/dst_news/dst_news_pakistan05.html -# -# .... -# The Federal Cabinet on Wednesday approved the proposal to -# advance clocks in the country by one hour from April 15 to -# conserve energy" - -# From Steffen Thorsen (2009-09-17): -# "The News International," Pakistan reports that: "The Federal -# Government has decided to restore the previous time by moving the -# clocks backward by one hour from October 1. A formal announcement to -# this effect will be made after the Prime Minister grants approval in -# this regard." -# http://www.thenews.com.pk/updates.asp?id=87168 - -# From Alexander Krivenyshev (2009-09-28): -# According to Associated Press Of Pakistan, it is confirmed that -# Pakistan clocks across the country would be turned back by an hour from -# October 1, 2009. -# -# "Clocks to go back one hour from 1 Oct" -# http://www.app.com.pk/en_/index.php?option=com_content&task=view&id=86715&Itemid=2 -# http://www.worldtimezone.com/dst_news/dst_news_pakistan07.htm -# -# From Steffen Thorsen (2009-09-29): -# Now they seem to have changed their mind, November 1 is the new date: -# http://www.thenews.com.pk/top_story_detail.asp?Id=24742 -# "The country's clocks will be reversed by one hour on November 1. -# Officials of Federal Ministry for Interior told this to Geo News on -# Monday." -# -# And more importantly, it seems that these dates will be kept every year: -# "It has now been decided that clocks will be wound forward by one hour -# on April 15 and reversed by an hour on November 1 every year without -# obtaining prior approval, the officials added." -# -# We have confirmed this year's end date with both with the Ministry of -# Water and Power and the Pakistan Electric Power Company: -# https://www.timeanddate.com/news/time/pakistan-ends-dst09.html - -# From Christoph Göhre (2009-10-01): -# [T]he German Consulate General in Karachi reported me today that Pakistan -# will go back to standard time on 1st of November. - -# From Steffen Thorsen (2010-03-26): -# Steffen Thorsen wrote: -# > On Thursday (2010-03-25) it was announced that DST would start in -# > Pakistan on 2010-04-01. -# > -# > Then today, the president said that they might have to revert the -# > decision if it is not supported by the parliament. So at the time -# > being, it seems unclear if DST will be actually observed or not - but -# > April 1 could be a more likely date than April 15. -# Now, it seems that the decision to not observe DST in final: -# -# "Govt Withdraws Plan To Advance Clocks" -# http://www.apakistannews.com/govt-withdraws-plan-to-advance-clocks-172041 -# -# "People laud PM's announcement to end DST" -# http://www.app.com.pk/en_/index.php?option=com_content&task=view&id=99374&Itemid=2 - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Pakistan 2002 only - Apr Sun>=2 0:00 1:00 S -Rule Pakistan 2002 only - Oct Sun>=2 0:00 0 - -Rule Pakistan 2008 only - Jun 1 0:00 1:00 S -Rule Pakistan 2008 2009 - Nov 1 0:00 0 - -Rule Pakistan 2009 only - Apr 15 0:00 1:00 S - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Karachi 4:28:12 - LMT 1907 - 5:30 - +0530 1942 Sep - 5:30 1:00 +0630 1945 Oct 15 - 5:30 - +0530 1951 Sep 30 - 5:00 - +05 1971 Mar 26 - 5:00 Pakistan PK%sT # Pakistan Time - -# Palestine - -# From Amos Shapir (1998-02-15): -# -# From 1917 until 1948-05-15, all of Palestine, including the parts now -# known as the Gaza Strip and the West Bank, was under British rule. -# Therefore the rules given for Israel for that period, apply there too... -# -# The Gaza Strip was under Egyptian rule between 1948-05-15 until 1967-06-05 -# (except a short occupation by Israel from 1956-11 till 1957-03, but no -# time zone was affected then). It was never formally annexed to Egypt, -# though. -# -# The rest of Palestine was under Jordanian rule at that time, formally -# annexed in 1950 as the West Bank (and the word "Trans" was dropped from -# the country's previous name of "the Hashemite Kingdom of the -# Trans-Jordan"). So the rules for Jordan for that time apply. Major -# towns in that area are Nablus (Shchem), El-Halil (Hebron), Ramallah, and -# East Jerusalem. -# -# Both areas were occupied by Israel in June 1967, but not annexed (except -# for East Jerusalem). They were on Israel time since then; there might -# have been a Military Governor's order about time zones, but I'm not aware -# of any (such orders may have been issued semi-annually whenever summer -# time was in effect, but maybe the legal aspect of time was just neglected). -# -# The Palestinian Authority was established in 1993, and got hold of most -# towns in the West Bank and Gaza by 1995. I know that in order to -# demonstrate...independence, they have been switching to -# summer time and back on a different schedule than Israel's, but I don't -# know when this was started, or what algorithm is used (most likely the -# Jordanian one). -# -# To summarize, the table should probably look something like that: -# -# Area \ when | 1918-1947 | 1948-1967 | 1967-1995 | 1996- -# ------------+-----------+-----------+-----------+----------- -# Israel | Zion | Zion | Zion | Zion -# West bank | Zion | Jordan | Zion | Jordan -# Gaza | Zion | Egypt | Zion | Jordan -# -# I guess more info may be available from the PA's web page (if/when they -# have one). - -# From Paul Eggert (2006-03-22): -# Shanks & Pottenger write that Gaza did not observe DST until 1957, but go -# with Shapir and assume that it observed DST from 1940 through 1947, -# and that it used Jordanian rules starting in 1996. -# We don't yet need a separate entry for the West Bank, since -# the only differences between it and Gaza that we know about -# occurred before our cutoff date of 1970. -# However, as we get more information, we may need to add entries -# for parts of the West Bank as they transitioned from Israel's rules -# to Palestine's rules. - -# From IINS News Service - Israel - 1998-03-23 10:38:07 Israel time, -# forwarded by Ephraim Silverberg: -# -# Despite the fact that Israel changed over to daylight savings time -# last week, the PLO Authority (PA) has decided not to turn its clocks -# one-hour forward at this time. As a sign of independence from Israeli rule, -# the PA has decided to implement DST in April. - -# From Paul Eggert (1999-09-20): -# Daoud Kuttab writes in Holiday havoc -# http://www.jpost.com/com/Archive/22.Apr.1999/Opinion/Article-2.html -# (Jerusalem Post, 1999-04-22) that -# the Palestinian National Authority changed to DST on 1999-04-15. -# I vaguely recall that they switch back in October (sorry, forgot the source). -# For now, let's assume that the spring switch was at 24:00, -# and that they switch at 0:00 on the 3rd Fridays of April and October. - -# From Paul Eggert (2005-11-22): -# Starting 2004 transitions are from Steffen Thorsen's web site timeanddate.com. - -# From Steffen Thorsen (2005-11-23): -# A user from Gaza reported that Gaza made the change early because of -# the Ramadan. Next year Ramadan will be even earlier, so I think -# there is a good chance next year's end date will be around two weeks -# earlier - the same goes for Jordan. - -# From Steffen Thorsen (2006-08-17): -# I was informed by a user in Bethlehem that in Bethlehem it started the -# same day as Israel, and after checking with other users in the area, I -# was informed that they started DST one day after Israel. I was not -# able to find any authoritative sources at the time, nor details if -# Gaza changed as well, but presumed Gaza to follow the same rules as -# the West Bank. - -# From Steffen Thorsen (2006-09-26): -# according to the Palestine News Network (2006-09-19): -# http://english.pnn.ps/index.php?option=com_content&task=view&id=596&Itemid=5 -# > The Council of Ministers announced that this year its winter schedule -# > will begin early, as of midnight Thursday. It is also time to turn -# > back the clocks for winter. Friday will begin an hour late this week. -# I guess it is likely that next year's date will be moved as well, -# because of the Ramadan. - -# From Jesper Nørgaard Welen (2007-09-18): -# According to Steffen Thorsen's web site the Gaza Strip and the rest of the -# Palestinian territories left DST early on 13.th. of September at 2:00. - -# From Paul Eggert (2007-09-20): -# My understanding is that Gaza and the West Bank disagree even over when -# the weekend is (Thursday+Friday versus Friday+Saturday), so I'd be a bit -# surprised if they agreed about DST. But for now, assume they agree. -# For lack of better information, predict that future changes will be -# the 2nd Thursday of September at 02:00. - -# From Alexander Krivenyshev (2008-08-28): -# Here is an article, that Mideast running on different clocks at Ramadan. -# -# Gaza Strip (as Egypt) ended DST at midnight Thursday (Aug 28, 2008), while -# the West Bank will end Daylight Saving Time at midnight Sunday (Aug 31, 2008). -# -# http://www.guardian.co.uk/world/feedarticle/7759001 -# http://www.abcnews.go.com/International/wireStory?id=5676087 -# http://www.worldtimezone.com/dst_news/dst_news_gazastrip01.html - -# From Alexander Krivenyshev (2009-03-26): -# According to the Palestine News Network (arabic.pnn.ps), Palestinian -# government decided to start Daylight Time on Thursday night March -# 26 and continue until the night of 27 September 2009. -# -# (in Arabic) -# http://arabic.pnn.ps/index.php?option=com_content&task=view&id=50850 -# -# (English translation) -# http://www.worldtimezone.com/dst_news/dst_news_westbank01.html - -# From Steffen Thorsen (2009-08-31): -# Palestine's Council of Ministers announced that they will revert back to -# winter time on Friday, 2009-09-04. -# -# One news source: -# http://www.safa.ps/ara/?action=showdetail&seid=4158 -# (Palestinian press agency, Arabic), -# Google translate: "Decided that the Palestinian government in Ramallah -# headed by Salam Fayyad, the start of work in time for the winter of -# 2009, starting on Friday approved the fourth delay Sept. clock sixty -# minutes per hour as of Friday morning." -# -# We are not sure if Gaza will do the same, last year they had a different -# end date, we will keep this page updated: -# https://www.timeanddate.com/news/time/westbank-gaza-dst-2009.html - -# From Alexander Krivenyshev (2009-09-02): -# Seems that Gaza Strip will go back to Winter Time same date as West Bank. -# -# According to Palestinian Ministry Of Interior, West Bank and Gaza Strip plan -# to change time back to Standard time on September 4, 2009. -# -# "Winter time unite the West Bank and Gaza" -# (from Palestinian National Authority): -# http://www.moi.gov.ps/en/?page=633167343250594025&nid=11505 -# http://www.worldtimezone.com/dst_news/dst_news_gazastrip02.html - -# From Alexander Krivenyshev (2010-03-19): -# According to Voice of Palestine DST will last for 191 days, from March -# 26, 2010 till "the last Sunday before the tenth day of Tishri -# (October), each year" (October 03, 2010?) -# -# http://palvoice.org/forums/showthread.php?t=245697 -# (in Arabic) -# http://www.worldtimezone.com/dst_news/dst_news_westbank03.html - -# From Steffen Thorsen (2010-03-24): -# ...Ma'an News Agency reports that Hamas cabinet has decided it will -# start one day later, at 12:01am. Not sure if they really mean 12:01am or -# noon though: -# -# http://www.maannews.net/eng/ViewDetails.aspx?ID=271178 -# (Ma'an News Agency) -# "At 12:01am Friday, clocks in Israel and the West Bank will change to -# 1:01am, while Gaza clocks will change at 12:01am Saturday morning." - -# From Steffen Thorsen (2010-08-11): -# According to several sources, including -# http://www.maannews.net/eng/ViewDetails.aspx?ID=306795 -# the clocks were set back one hour at 2010-08-11 00:00:00 local time in -# Gaza and the West Bank. -# Some more background info: -# https://www.timeanddate.com/news/time/westbank-gaza-end-dst-2010.html - -# From Steffen Thorsen (2011-08-26): -# Gaza and the West Bank did go back to standard time in the beginning of -# August, and will now enter daylight saving time again on 2011-08-30 -# 00:00 (so two periods of DST in 2011). The pause was because of -# Ramadan. -# -# http://www.maannews.net/eng/ViewDetails.aspx?ID=416217 -# Additional info: -# https://www.timeanddate.com/news/time/palestine-dst-2011.html - -# From Alexander Krivenyshev (2011-08-27): -# According to the article in The Jerusalem Post: -# "...Earlier this month, the Palestinian government in the West Bank decided to -# move to standard time for 30 days, during Ramadan. The Palestinians in the -# Gaza Strip accepted the change and also moved their clocks one hour back. -# The Hamas government said on Saturday that it won't observe summertime after -# the Muslim feast of Id al-Fitr, which begins on Tuesday..." -# ... -# https://www.jpost.com/MiddleEast/Article.aspx?id=235650 -# http://www.worldtimezone.com/dst_news/dst_news_gazastrip05.html -# The rules for Egypt are stolen from the 'africa' file. - -# From Steffen Thorsen (2011-09-30): -# West Bank did end Daylight Saving Time this morning/midnight (2011-09-30 -# 00:00). -# So West Bank and Gaza now have the same time again. -# -# Many sources, including: -# http://www.maannews.net/eng/ViewDetails.aspx?ID=424808 - -# From Steffen Thorsen (2012-03-26): -# Palestinian news sources tell that both Gaza and West Bank will start DST -# on Friday (Thursday midnight, 2012-03-29 24:00). -# Some of many sources in Arabic: -# http://www.samanews.com/index.php?act=Show&id=122638 -# -# http://safa.ps/details/news/74352/%D8%A8%D8%AF%D8%A1-%D8%A7%D9%84%D8%AA%D9%88%D9%82%D9%8A%D8%AA-%D8%A7%D9%84%D8%B5%D9%8A%D9%81%D9%8A-%D8%A8%D8%A7%D9%84%D8%B6%D9%81%D8%A9-%D9%88%D8%BA%D8%B2%D8%A9-%D9%84%D9%8A%D9%84%D8%A9-%D8%A7%D9%84%D8%AC%D9%85%D8%B9%D8%A9.html -# -# Our brief summary: -# https://www.timeanddate.com/news/time/gaza-west-bank-dst-2012.html - -# From Steffen Thorsen (2013-03-26): -# The following news sources tells that Palestine will "start daylight saving -# time from midnight on Friday, March 29, 2013" (translated). -# [These are in Arabic and are for Gaza and for Ramallah, respectively.] -# http://www.samanews.com/index.php?act=Show&id=154120 -# http://safa.ps/details/news/99844/%D8%B1%D8%A7%D9%85-%D8%A7%D9%84%D9%84%D9%87-%D8%A8%D8%AF%D8%A1-%D8%A7%D9%84%D8%AA%D9%88%D9%82%D9%8A%D8%AA-%D8%A7%D9%84%D8%B5%D9%8A%D9%81%D9%8A-29-%D8%A7%D9%84%D8%AC%D8%A7%D8%B1%D9%8A.html - -# From Steffen Thorsen (2013-09-24): -# The Gaza and West Bank are ending DST Thursday at midnight -# (2013-09-27 00:00:00) (one hour earlier than last year...). -# This source in English, says "that winter time will go into effect -# at midnight on Thursday in the West Bank and Gaza Strip": -# http://english.wafa.ps/index.php?action=detail&id=23246 -# official source...: -# http://www.palestinecabinet.gov.ps/ar/Views/ViewDetails.aspx?pid=1252 - -# From Steffen Thorsen (2015-03-03): -# Sources such as http://www.alquds.com/news/article/view/id/548257 -# and https://www.raya.ps/ar/news/890705.html say Palestine areas will -# start DST on 2015-03-28 00:00 which is one day later than expected. -# -# From Paul Eggert (2015-03-03): -# https://www.timeanddate.com/time/change/west-bank/ramallah?year=2014 -# says that the fall 2014 transition was Oct 23 at 24:00. - -# From Hannah Kreitem (2016-03-09): -# http://www.palestinecabinet.gov.ps/WebSite/ar/ViewDetails?ID=31728 -# [Google translation]: "The Council also decided to start daylight -# saving in Palestine as of one o'clock on Saturday morning, -# 2016-03-26, to provide the clock 60 minutes ahead." - -# From Sharef Mustafa (2016-10-19): -# [T]he Palestinian cabinet decision (Mar 8th 2016) published on -# http://www.palestinecabinet.gov.ps/WebSite/Upload/Decree/GOV_17/16032016134830.pdf -# states that summer time will end on Oct 29th at 01:00. -# -# From Tim Parenti (2016-10-19): -# Predict fall transitions on October's last Saturday at 01:00 from now on. -# This is consistent with the 2016 transition as well as our spring -# predictions. -# -# From Paul Eggert (2016-10-19): -# It's also consistent with predictions in the following URLs today: -# https://www.timeanddate.com/time/change/gaza-strip/gaza -# https://www.timeanddate.com/time/change/west-bank/hebron - -# From Sharef Mustafa (2018-03-16): -# Palestine summer time will start on Mar 24th 2018 by advancing the -# clock by 60 minutes as per Palestinian cabinet decision published on -# the official website, though the decree did not specify the exact -# time of the time shift. -# http://www.palestinecabinet.gov.ps/Website/AR/NDecrees/ViewFile.ashx?ID=e7a42ab7-ee23-435a-b9c8-a4f7e81f3817 -# -# From Paul Eggert (2018-03-16): -# For 2016 on, predict spring transitions on March's fourth Saturday at 01:00. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule EgyptAsia 1957 only - May 10 0:00 1:00 S -Rule EgyptAsia 1957 1958 - Oct 1 0:00 0 - -Rule EgyptAsia 1958 only - May 1 0:00 1:00 S -Rule EgyptAsia 1959 1967 - May 1 1:00 1:00 S -Rule EgyptAsia 1959 1965 - Sep 30 3:00 0 - -Rule EgyptAsia 1966 only - Oct 1 3:00 0 - - -Rule Palestine 1999 2005 - Apr Fri>=15 0:00 1:00 S -Rule Palestine 1999 2003 - Oct Fri>=15 0:00 0 - -Rule Palestine 2004 only - Oct 1 1:00 0 - -Rule Palestine 2005 only - Oct 4 2:00 0 - -Rule Palestine 2006 2007 - Apr 1 0:00 1:00 S -Rule Palestine 2006 only - Sep 22 0:00 0 - -Rule Palestine 2007 only - Sep Thu>=8 2:00 0 - -Rule Palestine 2008 2009 - Mar lastFri 0:00 1:00 S -Rule Palestine 2008 only - Sep 1 0:00 0 - -Rule Palestine 2009 only - Sep Fri>=1 1:00 0 - -Rule Palestine 2010 only - Mar 26 0:00 1:00 S -Rule Palestine 2010 only - Aug 11 0:00 0 - -Rule Palestine 2011 only - Apr 1 0:01 1:00 S -Rule Palestine 2011 only - Aug 1 0:00 0 - -Rule Palestine 2011 only - Aug 30 0:00 1:00 S -Rule Palestine 2011 only - Sep 30 0:00 0 - -Rule Palestine 2012 2014 - Mar lastThu 24:00 1:00 S -Rule Palestine 2012 only - Sep 21 1:00 0 - -Rule Palestine 2013 only - Sep Fri>=21 0:00 0 - -Rule Palestine 2014 2015 - Oct Fri>=21 0:00 0 - -Rule Palestine 2015 only - Mar lastFri 24:00 1:00 S -Rule Palestine 2016 max - Mar Sat>=22 1:00 1:00 S -Rule Palestine 2016 max - Oct lastSat 1:00 0 - - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Gaza 2:17:52 - LMT 1900 Oct - 2:00 Zion EET/EEST 1948 May 15 - 2:00 EgyptAsia EE%sT 1967 Jun 5 - 2:00 Zion I%sT 1996 - 2:00 Jordan EE%sT 1999 - 2:00 Palestine EE%sT 2008 Aug 29 0:00 - 2:00 - EET 2008 Sep - 2:00 Palestine EE%sT 2010 - 2:00 - EET 2010 Mar 27 0:01 - 2:00 Palestine EE%sT 2011 Aug 1 - 2:00 - EET 2012 - 2:00 Palestine EE%sT - -Zone Asia/Hebron 2:20:23 - LMT 1900 Oct - 2:00 Zion EET/EEST 1948 May 15 - 2:00 EgyptAsia EE%sT 1967 Jun 5 - 2:00 Zion I%sT 1996 - 2:00 Jordan EE%sT 1999 - 2:00 Palestine EE%sT - -# Paracel Is -# no information - -# Philippines -# On 1844-08-16, Narciso Clavería, governor-general of the -# Philippines, issued a proclamation announcing that 1844-12-30 was to -# be immediately followed by 1845-01-01; see R.H. van Gent's -# History of the International Date Line -# https://www.staff.science.uu.nl/~gent0113/idl/idl_philippines.htm -# The rest of the data entries are from Shanks & Pottenger. - -# From Jesper Nørgaard Welen (2006-04-26): -# ... claims that Philippines had DST last time in 1990: -# http://story.philippinetimes.com/p.x/ct/9/id/145be20cc6b121c0/cid/3e5bbccc730d258c/ -# [a story dated 2006-04-25 by Cris Larano of Dow Jones Newswires, -# but no details] - -# From Paul Eggert (2014-08-14): -# The following source says DST may be instituted November-January and again -# March-June, but this is not definite. It also says DST was last proclaimed -# during the Ramos administration (1992-1998); but again, no details. -# Carcamo D. PNoy urged to declare use of daylight saving time. -# Philippine Star 2014-08-05 -# http://www.philstar.com/headlines/2014/08/05/1354152/pnoy-urged-declare-use-daylight-saving-time - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Phil 1936 only - Nov 1 0:00 1:00 - -Rule Phil 1937 only - Feb 1 0:00 0 - -Rule Phil 1954 only - Apr 12 0:00 1:00 - -Rule Phil 1954 only - Jul 1 0:00 0 - -Rule Phil 1978 only - Mar 22 0:00 1:00 - -Rule Phil 1978 only - Sep 21 0:00 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Manila -15:56:00 - LMT 1844 Dec 31 - 8:04:00 - LMT 1899 May 11 - 8:00 Phil +08/+09 1942 May - 9:00 - +09 1944 Nov - 8:00 Phil +08/+09 - -# Qatar -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Qatar 3:26:08 - LMT 1920 # Al Dawhah / Doha - 4:00 - +04 1972 Jun - 3:00 - +03 -Link Asia/Qatar Asia/Bahrain - -# Saudi Arabia -# -# From Paul Eggert (2014-07-15): -# Time in Saudi Arabia and other countries in the Arabian peninsula was not -# standardized until relatively recently; we don't know when, and possibly it -# has never been made official. Richard P Hunt, in "Islam city yielding to -# modern times", New York Times (1961-04-09), p 20, wrote that only airlines -# observed standard time, and that people in Jeddah mostly observed quasi-solar -# time, doing so by setting their watches at sunrise to 6 o'clock (or to 12 -# o'clock for "Arab" time). -# -# The TZ database cannot represent quasi-solar time; airline time is the best -# we can do. The 1946 foreign air news digest of the U.S. Civil Aeronautics -# Board (OCLC 42299995) reported that the "... Arabian Government, inaugurated -# a weekly Dhahran-Cairo service, via the Saudi Arabian cities of Riyadh and -# Jidda, on March 14, 1947". Shanks & Pottenger guessed 1950; go with the -# earlier date. -# -# Shanks & Pottenger also state that until 1968-05-01 Saudi Arabia had two -# time zones; the other zone, at UT +04, was in the far eastern part of -# the country. Ignore this, as it's before our 1970 cutoff. -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Riyadh 3:06:52 - LMT 1947 Mar 14 - 3:00 - +03 -Link Asia/Riyadh Asia/Aden # Yemen -Link Asia/Riyadh Asia/Kuwait - -# Singapore -# taken from Mok Ly Yng (2003-10-30) -# http://www.math.nus.edu.sg/aslaksen/teaching/timezone.html -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Singapore 6:55:25 - LMT 1901 Jan 1 - 6:55:25 - SMT 1905 Jun 1 # Singapore M.T. - 7:00 - +07 1933 Jan 1 - 7:00 0:20 +0720 1936 Jan 1 - 7:20 - +0720 1941 Sep 1 - 7:30 - +0730 1942 Feb 16 - 9:00 - +09 1945 Sep 12 - 7:30 - +0730 1982 Jan 1 - 8:00 - +08 - -# Spratly Is -# no information - -# Sri Lanka - -# From Paul Eggert (2013-02-21): -# Milne says "Madras mean time use from May 1, 1898. Prior to this Colombo -# mean time, 5h. 4m. 21.9s. F., was used." But 5:04:21.9 differs considerably -# from Colombo's meridian 5:19:24, so for now ignore Milne and stick with -# Shanks and Pottenger. - -# From Paul Eggert (1996-09-03): -# "Sri Lanka advances clock by an hour to avoid blackout" -# (, 1996-05-24, -# no longer available as of 1999-08-17) -# reported "the country's standard time will be put forward by one hour at -# midnight Friday (1830 GMT) 'in the light of the present power crisis'." -# -# From Dharmasiri Senanayake, Sri Lanka Media Minister (1996-10-24), as quoted -# by Shamindra in Daily News - Hot News Section -# (1996-10-26): -# With effect from 12.30 a.m. on 26th October 1996 -# Sri Lanka will be six (06) hours ahead of GMT. - -# From Jesper Nørgaard Welen (2006-04-14), quoting Sri Lanka News Online -# (2006-04-13): -# 0030 hrs on April 15, 2006 (midnight of April 14, 2006 +30 minutes) -# at present, become 2400 hours of April 14, 2006 (midnight of April 14, 2006). - -# From Peter Apps and Ranga Sirila of Reuters (2006-04-12) in: -# http://today.reuters.co.uk/news/newsArticle.aspx?type=scienceNews&storyID=2006-04-12T172228Z_01_COL295762_RTRIDST_0_SCIENCE-SRILANKA-TIME-DC.XML -# [The Tamil Tigers] never accepted the original 1996 time change and simply -# kept their clocks set five and a half hours ahead of Greenwich Mean -# Time (GMT), in line with neighbor India. -# From Paul Eggert (2006-04-18): -# People who live in regions under Tamil control can use [TZ='Asia/Kolkata'], -# as that zone has agreed with the Tamil areas since our cutoff date of 1970. - -# From Sadika Sumanapala (2016-10-19): -# According to http://www.sltime.org (maintained by Measurement Units, -# Standards & Services Department, Sri Lanka) abbreviation for Sri Lanka -# standard time is SLST. -# -# From Paul Eggert (2016-10-18): -# "SLST" seems to be reasonably recent and rarely-used outside time -# zone nerd sources. I searched Google News and found three uses of -# it in the International Business Times of India in February and -# March of this year when discussing cricket match times, but nothing -# since then (though there has been a lot of cricket) and nothing in -# other English-language news sources. Our old abbreviation "LKT" is -# even worse. For now, let's use a numeric abbreviation; we can -# switch to "SLST" if it catches on. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Colombo 5:19:24 - LMT 1880 - 5:19:32 - MMT 1906 # Moratuwa Mean Time - 5:30 - +0530 1942 Jan 5 - 5:30 0:30 +06 1942 Sep - 5:30 1:00 +0630 1945 Oct 16 2:00 - 5:30 - +0530 1996 May 25 0:00 - 6:30 - +0630 1996 Oct 26 0:30 - 6:00 - +06 2006 Apr 15 0:30 - 5:30 - +0530 - -# Syria -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Syria 1920 1923 - Apr Sun>=15 2:00 1:00 S -Rule Syria 1920 1923 - Oct Sun>=1 2:00 0 - -Rule Syria 1962 only - Apr 29 2:00 1:00 S -Rule Syria 1962 only - Oct 1 2:00 0 - -Rule Syria 1963 1965 - May 1 2:00 1:00 S -Rule Syria 1963 only - Sep 30 2:00 0 - -Rule Syria 1964 only - Oct 1 2:00 0 - -Rule Syria 1965 only - Sep 30 2:00 0 - -Rule Syria 1966 only - Apr 24 2:00 1:00 S -Rule Syria 1966 1976 - Oct 1 2:00 0 - -Rule Syria 1967 1978 - May 1 2:00 1:00 S -Rule Syria 1977 1978 - Sep 1 2:00 0 - -Rule Syria 1983 1984 - Apr 9 2:00 1:00 S -Rule Syria 1983 1984 - Oct 1 2:00 0 - -Rule Syria 1986 only - Feb 16 2:00 1:00 S -Rule Syria 1986 only - Oct 9 2:00 0 - -Rule Syria 1987 only - Mar 1 2:00 1:00 S -Rule Syria 1987 1988 - Oct 31 2:00 0 - -Rule Syria 1988 only - Mar 15 2:00 1:00 S -Rule Syria 1989 only - Mar 31 2:00 1:00 S -Rule Syria 1989 only - Oct 1 2:00 0 - -Rule Syria 1990 only - Apr 1 2:00 1:00 S -Rule Syria 1990 only - Sep 30 2:00 0 - -Rule Syria 1991 only - Apr 1 0:00 1:00 S -Rule Syria 1991 1992 - Oct 1 0:00 0 - -Rule Syria 1992 only - Apr 8 0:00 1:00 S -Rule Syria 1993 only - Mar 26 0:00 1:00 S -Rule Syria 1993 only - Sep 25 0:00 0 - -# IATA SSIM (1998-02) says 1998-04-02; -# (1998-09) says 1999-03-29 and 1999-09-29; (1999-02) says 1999-04-02, -# 2000-04-02, and 2001-04-02; (1999-09) says 2000-03-31 and 2001-03-31; -# (2006) says 2006-03-31 and 2006-09-22; -# for now ignore all these claims and go with Shanks & Pottenger, -# except for the 2006-09-22 claim (which seems right for Ramadan). -Rule Syria 1994 1996 - Apr 1 0:00 1:00 S -Rule Syria 1994 2005 - Oct 1 0:00 0 - -Rule Syria 1997 1998 - Mar lastMon 0:00 1:00 S -Rule Syria 1999 2006 - Apr 1 0:00 1:00 S -# From Stephen Colebourne (2006-09-18): -# According to IATA data, Syria will change DST on 21st September [21:00 UTC] -# this year [only].... This is probably related to Ramadan, like Egypt. -Rule Syria 2006 only - Sep 22 0:00 0 - -# From Paul Eggert (2007-03-29): -# Today the AP reported "Syria will switch to summertime at midnight Thursday." -# http://www.iht.com/articles/ap/2007/03/29/africa/ME-GEN-Syria-Time-Change.php -Rule Syria 2007 only - Mar lastFri 0:00 1:00 S -# From Jesper Nørgaard (2007-10-27): -# The sister center ICARDA of my work CIMMYT is confirming that Syria DST will -# not take place 1st November at 0:00 o'clock but 1st November at 24:00 or -# rather Midnight between Thursday and Friday. This does make more sense than -# having it between Wednesday and Thursday (two workdays in Syria) since the -# weekend in Syria is not Saturday and Sunday, but Friday and Saturday. So now -# it is implemented at midnight of the last workday before weekend... -# -# From Steffen Thorsen (2007-10-27): -# Jesper Nørgaard Welen wrote: -# -# > "Winter local time in Syria will be observed at midnight of Thursday 1 -# > November 2007, and the clock will be put back 1 hour." -# -# I found confirmation on this in this gov.sy-article (Arabic): -# http://wehda.alwehda.gov.sy/_print_veiw.asp?FileName=12521710520070926111247 -# -# which using Google's translate tools says: -# Council of Ministers also approved the commencement of work on -# identifying the winter time as of Friday, 2/11/2007 where the 60th -# minute delay at midnight Thursday 1/11/2007. -Rule Syria 2007 only - Nov Fri>=1 0:00 0 - - -# From Stephen Colebourne (2008-03-17): -# For everyone's info, I saw an IATA time zone change for [Syria] for -# this month (March 2008) in the last day or so.... -# Country Time Standard --- DST Start --- --- DST End --- DST -# Name Zone Variation Time Date Time Date -# Variation -# Syrian Arab -# Republic SY +0200 2200 03APR08 2100 30SEP08 +0300 -# 2200 02APR09 2100 30SEP09 +0300 -# 2200 01APR10 2100 30SEP10 +0300 - -# From Arthur David Olson (2008-03-17): -# Here's a link to English-language coverage by the Syrian Arab News -# Agency (SANA)... -# http://www.sana.sy/eng/21/2008/03/11/165173.htm -# ...which reads (in part) "The Cabinet approved the suggestion of the -# Ministry of Electricity to begin daylight savings time on Friday April -# 4th, advancing clocks one hour ahead on midnight of Thursday April 3rd." -# Since Syria is two hours east of UTC, the 2200 and 2100 transition times -# shown above match up with midnight in Syria. - -# From Arthur David Olson (2008-03-18): -# My best guess at a Syrian rule is "the Friday nearest April 1"; -# coding that involves either using a "Mar Fri>=29" construct that old time zone -# compilers can't handle or having multiple Rules (a la Israel). -# For now, use "Apr Fri>=1", and go with IATA on a uniform Sep 30 end. - -# From Steffen Thorsen (2008-10-07): -# Syria has now officially decided to end DST on 2008-11-01 this year, -# according to the following article in the Syrian Arab News Agency (SANA). -# -# The article is in Arabic, and seems to tell that they will go back to -# winter time on 2008-11-01 at 00:00 local daylight time (delaying/setting -# clocks back 60 minutes). -# -# http://sana.sy/ara/2/2008/10/07/195459.htm - -# From Steffen Thorsen (2009-03-19): -# Syria will start DST on 2009-03-27 00:00 this year according to many sources, -# two examples: -# -# http://www.sana.sy/eng/21/2009/03/17/217563.htm -# (English, Syrian Arab News # Agency) -# http://thawra.alwehda.gov.sy/_View_news2.asp?FileName=94459258720090318012209 -# (Arabic, gov-site) -# -# We have not found any sources saying anything about when DST ends this year. -# -# Our summary -# https://www.timeanddate.com/news/time/syria-dst-starts-march-27-2009.html - -# From Steffen Thorsen (2009-10-27): -# The Syrian Arab News Network on 2009-09-29 reported that Syria will -# revert back to winter (standard) time on midnight between Thursday -# 2009-10-29 and Friday 2009-10-30: -# http://www.sana.sy/ara/2/2009/09/29/247012.htm (Arabic) - -# From Arthur David Olson (2009-10-28): -# We'll see if future DST switching times turn out to be end of the last -# Thursday of the month or the start of the last Friday of the month or -# something else. For now, use the start of the last Friday. - -# From Steffen Thorsen (2010-03-17): -# The "Syrian News Station" reported on 2010-03-16 that the Council of -# Ministers has decided that Syria will start DST on midnight Thursday -# 2010-04-01: (midnight between Thursday and Friday): -# http://sns.sy/sns/?path=news/read/11421 (Arabic) - -# From Steffen Thorsen (2012-03-26): -# Today, Syria's government announced that they will start DST early on Friday -# (00:00). This is a bit earlier than the past two years. -# -# From Syrian Arab News Agency, in Arabic: -# http://www.sana.sy/ara/2/2012/03/26/408215.htm -# -# Our brief summary: -# https://www.timeanddate.com/news/time/syria-dst-2012.html - -# From Arthur David Olson (2012-03-27): -# Assume last Friday in March going forward XXX. - -Rule Syria 2008 only - Apr Fri>=1 0:00 1:00 S -Rule Syria 2008 only - Nov 1 0:00 0 - -Rule Syria 2009 only - Mar lastFri 0:00 1:00 S -Rule Syria 2010 2011 - Apr Fri>=1 0:00 1:00 S -Rule Syria 2012 max - Mar lastFri 0:00 1:00 S -Rule Syria 2009 max - Oct lastFri 0:00 0 - - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Damascus 2:25:12 - LMT 1920 # Dimashq - 2:00 Syria EE%sT - -# Tajikistan -# From Shanks & Pottenger. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Dushanbe 4:35:12 - LMT 1924 May 2 - 5:00 - +05 1930 Jun 21 - 6:00 RussiaAsia +06/+07 1991 Mar 31 2:00s - 5:00 1:00 +05/+06 1991 Sep 9 2:00s - 5:00 - +05 - -# Thailand -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Bangkok 6:42:04 - LMT 1880 - 6:42:04 - BMT 1920 Apr # Bangkok Mean Time - 7:00 - +07 -Link Asia/Bangkok Asia/Phnom_Penh # Cambodia -Link Asia/Bangkok Asia/Vientiane # Laos - -# Turkmenistan -# From Shanks & Pottenger. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Ashgabat 3:53:32 - LMT 1924 May 2 # or Ashkhabad - 4:00 - +04 1930 Jun 21 - 5:00 RussiaAsia +05/+06 1991 Mar 31 2:00 - 4:00 RussiaAsia +04/+05 1992 Jan 19 2:00 - 5:00 - +05 - -# United Arab Emirates -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Dubai 3:41:12 - LMT 1920 - 4:00 - +04 -Link Asia/Dubai Asia/Muscat # Oman - -# Uzbekistan -# Byalokoz 1919 says Uzbekistan was 4:27:53. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Samarkand 4:27:53 - LMT 1924 May 2 - 4:00 - +04 1930 Jun 21 - 5:00 - +05 1981 Apr 1 - 5:00 1:00 +06 1981 Oct 1 - 6:00 - +06 1982 Apr 1 - 5:00 RussiaAsia +05/+06 1992 - 5:00 - +05 -# Milne says Tashkent was 4:37:10.8; round to nearest. -Zone Asia/Tashkent 4:37:11 - LMT 1924 May 2 - 5:00 - +05 1930 Jun 21 - 6:00 RussiaAsia +06/+07 1991 Mar 31 2:00 - 5:00 RussiaAsia +05/+06 1992 - 5:00 - +05 - -# Vietnam - -# From Paul Eggert (2014-10-04): -# Milne gives 7:16:56 for the meridian of Saigon in 1899, as being -# used in Lower Laos, Cambodia, and Annam. But this is quite a ways -# from Saigon's location. For now, ignore this and stick with Shanks -# and Pottenger for LMT before 1906. - -# From Arthur David Olson (2008-03-18): -# The English-language name of Vietnam's most populous city is "Ho Chi Minh -# City"; use Ho_Chi_Minh below to avoid a name of more than 14 characters. - -# From Paul Eggert (2014-10-21) after a heads-up from Trần Ngọc Quân: -# Trần Tiến Bình's authoritative book "Lịch Việt Nam: thế kỷ XX-XXI (1901-2100)" -# (Nhà xuất bản Văn Hoá - Thông Tin, Hanoi, 2005), pp 49-50, -# is quoted verbatim in: -# http://www.thoigian.com.vn/?mPage=P80D01 -# is translated by Brian Inglis in: -# https://mm.icann.org/pipermail/tz/2014-October/021654.html -# and is the basis for the information below. -# -# The 1906 transition was effective July 1 and standardized Indochina to -# Phù Liễn Observatory, legally 104° 17' 17" east of Paris. -# It's unclear whether this meant legal Paris Mean Time (00:09:21) or -# the Paris Meridian (2° 20' 14.03" E); the former yields 07:06:30.1333... -# and the latter 07:06:29.333... so either way it rounds to 07:06:30, -# which is used below even though the modern-day Phù Liễn Observatory -# is closer to 07:06:31. Abbreviate Phù Liễn Mean Time as PLMT. -# -# The following transitions occurred in Indochina in general (before 1954) -# and in South Vietnam in particular (after 1954): -# To 07:00 on 1911-05-01. -# To 08:00 on 1942-12-31 at 23:00. -# To 09:00 in 1945-03-14 at 23:00. -# To 07:00 on 1945-09-02 in Vietnam. -# To 08:00 on 1947-04-01 in French-controlled Indochina. -# To 07:00 on 1955-07-01 in South Vietnam. -# To 08:00 on 1959-12-31 at 23:00 in South Vietnam. -# To 07:00 on 1975-06-13 in South Vietnam. -# -# Trần cites the following sources; it's unclear which supplied the info above. -# -# Hoàng Xuân Hãn: "Lịch và lịch Việt Nam". Tập san Khoa học Xã hội, -# No. 9, Paris, February 1982. -# -# Lê Thành Lân: "Lịch và niên biểu lịch sử hai mươi thế kỷ (0001-2010)", -# NXB Thống kê, Hanoi, 2000. -# -# Lê Thành Lân: "Lịch hai thế kỷ (1802-2010) và các lịch vĩnh cửu", -# NXB Thuận Hoá, Huế, 1995. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Ho_Chi_Minh 7:06:40 - LMT 1906 Jul 1 - 7:06:30 - PLMT 1911 May 1 # Phù Liễn MT - 7:00 - +07 1942 Dec 31 23:00 - 8:00 - +08 1945 Mar 14 23:00 - 9:00 - +09 1945 Sep 2 - 7:00 - +07 1947 Apr 1 - 8:00 - +08 1955 Jul 1 - 7:00 - +07 1959 Dec 31 23:00 - 8:00 - +08 1975 Jun 13 - 7:00 - +07 - -# Yemen -# See Asia/Riyadh. diff --git a/date_time/australasia b/date_time/australasia deleted file mode 100644 index e3e79f19bf..0000000000 --- a/date_time/australasia +++ /dev/null @@ -1,1816 +0,0 @@ -# This file is in the public domain, so clarified as of -# 2009-05-17 by Arthur David Olson. - -# This file also includes Pacific islands. - -# Notes are at the end of this file - -############################################################################### - -# Australia - -# Please see the notes below for the controversy about "EST" versus "AEST" etc. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Aus 1917 only - Jan 1 0:01 1:00 D -Rule Aus 1917 only - Mar 25 2:00 0 S -Rule Aus 1942 only - Jan 1 2:00 1:00 D -Rule Aus 1942 only - Mar 29 2:00 0 S -Rule Aus 1942 only - Sep 27 2:00 1:00 D -Rule Aus 1943 1944 - Mar lastSun 2:00 0 S -Rule Aus 1943 only - Oct 3 2:00 1:00 D -# Go with Whitman and the Australian National Standards Commission, which -# says W Australia didn't use DST in 1943/1944. Ignore Whitman's claim that -# 1944/1945 was just like 1943/1944. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -# Northern Territory -Zone Australia/Darwin 8:43:20 - LMT 1895 Feb - 9:00 - ACST 1899 May - 9:30 Aus AC%sT -# Western Australia -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule AW 1974 only - Oct lastSun 2:00s 1:00 D -Rule AW 1975 only - Mar Sun>=1 2:00s 0 S -Rule AW 1983 only - Oct lastSun 2:00s 1:00 D -Rule AW 1984 only - Mar Sun>=1 2:00s 0 S -Rule AW 1991 only - Nov 17 2:00s 1:00 D -Rule AW 1992 only - Mar Sun>=1 2:00s 0 S -Rule AW 2006 only - Dec 3 2:00s 1:00 D -Rule AW 2007 2009 - Mar lastSun 2:00s 0 S -Rule AW 2007 2008 - Oct lastSun 2:00s 1:00 D -Zone Australia/Perth 7:43:24 - LMT 1895 Dec - 8:00 Aus AW%sT 1943 Jul - 8:00 AW AW%sT -Zone Australia/Eucla 8:35:28 - LMT 1895 Dec - 8:45 Aus +0845/+0945 1943 Jul - 8:45 AW +0845/+0945 - -# Queensland -# -# From Alex Livingston (1996-11-01): -# I have heard or read more than once that some resort islands off the coast -# of Queensland chose to keep observing daylight-saving time even after -# Queensland ceased to. -# -# From Paul Eggert (1996-11-22): -# IATA SSIM (1993-02/1994-09) say that the Holiday Islands (Hayman, Lindeman, -# Hamilton) observed DST for two years after the rest of Queensland stopped. -# Hamilton is the largest, but there is also a Hamilton in Victoria, -# so use Lindeman. -# -# From J William Piggott (2016-02-20): -# There is no location named Holiday Islands in Queensland Australia; holiday -# islands is a colloquial term used globally. Hayman and Lindeman are at the -# north and south extremes of the Whitsunday Islands archipelago, and -# Hamilton is in between; it is reasonable to believe that this time zone -# applies to all of the Whitsundays. -# http://www.australia.gov.au/about-australia/australian-story/austn-islands -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule AQ 1971 only - Oct lastSun 2:00s 1:00 D -Rule AQ 1972 only - Feb lastSun 2:00s 0 S -Rule AQ 1989 1991 - Oct lastSun 2:00s 1:00 D -Rule AQ 1990 1992 - Mar Sun>=1 2:00s 0 S -Rule Holiday 1992 1993 - Oct lastSun 2:00s 1:00 D -Rule Holiday 1993 1994 - Mar Sun>=1 2:00s 0 S -Zone Australia/Brisbane 10:12:08 - LMT 1895 - 10:00 Aus AE%sT 1971 - 10:00 AQ AE%sT -Zone Australia/Lindeman 9:55:56 - LMT 1895 - 10:00 Aus AE%sT 1971 - 10:00 AQ AE%sT 1992 Jul - 10:00 Holiday AE%sT - -# South Australia -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule AS 1971 1985 - Oct lastSun 2:00s 1:00 D -Rule AS 1986 only - Oct 19 2:00s 1:00 D -Rule AS 1987 2007 - Oct lastSun 2:00s 1:00 D -Rule AS 1972 only - Feb 27 2:00s 0 S -Rule AS 1973 1985 - Mar Sun>=1 2:00s 0 S -Rule AS 1986 1990 - Mar Sun>=15 2:00s 0 S -Rule AS 1991 only - Mar 3 2:00s 0 S -Rule AS 1992 only - Mar 22 2:00s 0 S -Rule AS 1993 only - Mar 7 2:00s 0 S -Rule AS 1994 only - Mar 20 2:00s 0 S -Rule AS 1995 2005 - Mar lastSun 2:00s 0 S -Rule AS 2006 only - Apr 2 2:00s 0 S -Rule AS 2007 only - Mar lastSun 2:00s 0 S -Rule AS 2008 max - Apr Sun>=1 2:00s 0 S -Rule AS 2008 max - Oct Sun>=1 2:00s 1:00 D -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Australia/Adelaide 9:14:20 - LMT 1895 Feb - 9:00 - ACST 1899 May - 9:30 Aus AC%sT 1971 - 9:30 AS AC%sT - -# Tasmania -# -# From Paul Eggert (2005-08-16): -# http://www.bom.gov.au/climate/averages/tables/dst_times.shtml -# says King Island didn't observe DST from WWII until late 1971. -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule AT 1967 only - Oct Sun>=1 2:00s 1:00 D -Rule AT 1968 only - Mar lastSun 2:00s 0 S -Rule AT 1968 1985 - Oct lastSun 2:00s 1:00 D -Rule AT 1969 1971 - Mar Sun>=8 2:00s 0 S -Rule AT 1972 only - Feb lastSun 2:00s 0 S -Rule AT 1973 1981 - Mar Sun>=1 2:00s 0 S -Rule AT 1982 1983 - Mar lastSun 2:00s 0 S -Rule AT 1984 1986 - Mar Sun>=1 2:00s 0 S -Rule AT 1986 only - Oct Sun>=15 2:00s 1:00 D -Rule AT 1987 1990 - Mar Sun>=15 2:00s 0 S -Rule AT 1987 only - Oct Sun>=22 2:00s 1:00 D -Rule AT 1988 1990 - Oct lastSun 2:00s 1:00 D -Rule AT 1991 1999 - Oct Sun>=1 2:00s 1:00 D -Rule AT 1991 2005 - Mar lastSun 2:00s 0 S -Rule AT 2000 only - Aug lastSun 2:00s 1:00 D -Rule AT 2001 max - Oct Sun>=1 2:00s 1:00 D -Rule AT 2006 only - Apr Sun>=1 2:00s 0 S -Rule AT 2007 only - Mar lastSun 2:00s 0 S -Rule AT 2008 max - Apr Sun>=1 2:00s 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Australia/Hobart 9:49:16 - LMT 1895 Sep - 10:00 - AEST 1916 Oct 1 2:00 - 10:00 1:00 AEDT 1917 Feb - 10:00 Aus AE%sT 1967 - 10:00 AT AE%sT -Zone Australia/Currie 9:35:28 - LMT 1895 Sep - 10:00 - AEST 1916 Oct 1 2:00 - 10:00 1:00 AEDT 1917 Feb - 10:00 Aus AE%sT 1971 Jul - 10:00 AT AE%sT - -# Victoria -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule AV 1971 1985 - Oct lastSun 2:00s 1:00 D -Rule AV 1972 only - Feb lastSun 2:00s 0 S -Rule AV 1973 1985 - Mar Sun>=1 2:00s 0 S -Rule AV 1986 1990 - Mar Sun>=15 2:00s 0 S -Rule AV 1986 1987 - Oct Sun>=15 2:00s 1:00 D -Rule AV 1988 1999 - Oct lastSun 2:00s 1:00 D -Rule AV 1991 1994 - Mar Sun>=1 2:00s 0 S -Rule AV 1995 2005 - Mar lastSun 2:00s 0 S -Rule AV 2000 only - Aug lastSun 2:00s 1:00 D -Rule AV 2001 2007 - Oct lastSun 2:00s 1:00 D -Rule AV 2006 only - Apr Sun>=1 2:00s 0 S -Rule AV 2007 only - Mar lastSun 2:00s 0 S -Rule AV 2008 max - Apr Sun>=1 2:00s 0 S -Rule AV 2008 max - Oct Sun>=1 2:00s 1:00 D -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Australia/Melbourne 9:39:52 - LMT 1895 Feb - 10:00 Aus AE%sT 1971 - 10:00 AV AE%sT - -# New South Wales -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule AN 1971 1985 - Oct lastSun 2:00s 1:00 D -Rule AN 1972 only - Feb 27 2:00s 0 S -Rule AN 1973 1981 - Mar Sun>=1 2:00s 0 S -Rule AN 1982 only - Apr Sun>=1 2:00s 0 S -Rule AN 1983 1985 - Mar Sun>=1 2:00s 0 S -Rule AN 1986 1989 - Mar Sun>=15 2:00s 0 S -Rule AN 1986 only - Oct 19 2:00s 1:00 D -Rule AN 1987 1999 - Oct lastSun 2:00s 1:00 D -Rule AN 1990 1995 - Mar Sun>=1 2:00s 0 S -Rule AN 1996 2005 - Mar lastSun 2:00s 0 S -Rule AN 2000 only - Aug lastSun 2:00s 1:00 D -Rule AN 2001 2007 - Oct lastSun 2:00s 1:00 D -Rule AN 2006 only - Apr Sun>=1 2:00s 0 S -Rule AN 2007 only - Mar lastSun 2:00s 0 S -Rule AN 2008 max - Apr Sun>=1 2:00s 0 S -Rule AN 2008 max - Oct Sun>=1 2:00s 1:00 D -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Australia/Sydney 10:04:52 - LMT 1895 Feb - 10:00 Aus AE%sT 1971 - 10:00 AN AE%sT -Zone Australia/Broken_Hill 9:25:48 - LMT 1895 Feb - 10:00 - AEST 1896 Aug 23 - 9:00 - ACST 1899 May - 9:30 Aus AC%sT 1971 - 9:30 AN AC%sT 2000 - 9:30 AS AC%sT - -# Lord Howe Island -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule LH 1981 1984 - Oct lastSun 2:00 1:00 - -Rule LH 1982 1985 - Mar Sun>=1 2:00 0 - -Rule LH 1985 only - Oct lastSun 2:00 0:30 - -Rule LH 1986 1989 - Mar Sun>=15 2:00 0 - -Rule LH 1986 only - Oct 19 2:00 0:30 - -Rule LH 1987 1999 - Oct lastSun 2:00 0:30 - -Rule LH 1990 1995 - Mar Sun>=1 2:00 0 - -Rule LH 1996 2005 - Mar lastSun 2:00 0 - -Rule LH 2000 only - Aug lastSun 2:00 0:30 - -Rule LH 2001 2007 - Oct lastSun 2:00 0:30 - -Rule LH 2006 only - Apr Sun>=1 2:00 0 - -Rule LH 2007 only - Mar lastSun 2:00 0 - -Rule LH 2008 max - Apr Sun>=1 2:00 0 - -Rule LH 2008 max - Oct Sun>=1 2:00 0:30 - -Zone Australia/Lord_Howe 10:36:20 - LMT 1895 Feb - 10:00 - AEST 1981 Mar - 10:30 LH +1030/+1130 1985 Jul - 10:30 LH +1030/+11 - -# Australian miscellany -# -# Ashmore Is, Cartier -# no indigenous inhabitants; only seasonal caretakers -# no times are set -# -# Coral Sea Is -# no indigenous inhabitants; only meteorologists -# no times are set -# -# Macquarie -# Permanent occupation (scientific station) 1911-1915 and since 25 March 1948; -# sealing and penguin oil station operated Nov 1899 to Apr 1919. See the -# Tasmania Parks & Wildlife Service history of sealing at Macquarie Island -# http://www.parks.tas.gov.au/index.aspx?base=1828 -# http://www.parks.tas.gov.au/index.aspx?base=1831 -# Guess that it was like Australia/Hobart while inhabited before 2010. -# -# From Steffen Thorsen (2010-03-10): -# We got these changes from the Australian Antarctic Division: -# - Macquarie Island will stay on UTC+11 for winter and therefore not -# switch back from daylight savings time when other parts of Australia do -# on 4 April. -# -# From Arthur David Olson (2013-05-23): -# The 1919 transition is overspecified below so pre-2013 zics -# will produce a binary file with an [A]EST-type as the first 32-bit type; -# this is required for correct handling of times before 1916 by -# pre-2013 versions of localtime. -Zone Antarctica/Macquarie 0 - -00 1899 Nov - 10:00 - AEST 1916 Oct 1 2:00 - 10:00 1:00 AEDT 1917 Feb - 10:00 Aus AE%sT 1919 Apr 1 0:00s - 0 - -00 1948 Mar 25 - 10:00 Aus AE%sT 1967 - 10:00 AT AE%sT 2010 Apr 4 3:00 - 11:00 - +11 - -# Christmas -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Indian/Christmas 7:02:52 - LMT 1895 Feb - 7:00 - +07 - -# Cocos (Keeling) Is -# These islands were ruled by the Ross family from about 1830 to 1978. -# We don't know when standard time was introduced; for now, we guess 1900. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Indian/Cocos 6:27:40 - LMT 1900 - 6:30 - +0630 - - -# Fiji - -# Milne gives 11:55:44 for Suva. - -# From Alexander Krivenyshev (2009-11-10): -# According to Fiji Broadcasting Corporation, Fiji plans to re-introduce DST -# from November 29th 2009 to April 25th 2010. -# -# "Daylight savings to commence this month" -# http://www.radiofiji.com.fj/fullstory.php?id=23719 -# http://www.worldtimezone.com/dst_news/dst_news_fiji01.html - -# From Steffen Thorsen (2009-11-10): -# The Fiji Government has posted some more details about the approved -# amendments: -# http://www.fiji.gov.fj/publish/page_16198.shtml - -# From Steffen Thorsen (2010-03-03): -# The Cabinet in Fiji has decided to end DST about a month early, on -# 2010-03-28 at 03:00. -# The plan is to observe DST again, from 2010-10-24 to sometime in March -# 2011 (last Sunday a good guess?). -# -# Official source: -# http://www.fiji.gov.fj/index.php?option=com_content&view=article&id=1096:3310-cabinet-approves-change-in-daylight-savings-dates&catid=49:cabinet-releases&Itemid=166 -# -# A bit more background info here: -# https://www.timeanddate.com/news/time/fiji-dst-ends-march-2010.html - -# From Alexander Krivenyshev (2010-10-24): -# According to Radio Fiji and Fiji Times online, Fiji will end DST 3 -# weeks earlier than expected - on March 6, 2011, not March 27, 2011... -# Here is confirmation from Government of the Republic of the Fiji Islands, -# Ministry of Information (fiji.gov.fj) web site: -# http://www.fiji.gov.fj/index.php?option=com_content&view=article&id=2608:daylight-savings&catid=71:press-releases&Itemid=155 -# http://www.worldtimezone.com/dst_news/dst_news_fiji04.html - -# From Steffen Thorsen (2011-10-03): -# Now the dates have been confirmed, and at least our start date -# assumption was correct (end date was one week wrong). -# -# http://www.fiji.gov.fj/index.php?option=com_content&view=article&id=4966:daylight-saving-starts-in-fiji&catid=71:press-releases&Itemid=155 -# which says -# Members of the public are reminded to change their time to one hour in -# advance at 2am to 3am on October 23, 2011 and one hour back at 3am to -# 2am on February 26 next year. - -# From Ken Rylander (2011-10-24) -# Another change to the Fiji DST end date. In the TZ database the end date for -# Fiji DST 2012, is currently Feb 26. This has been changed to Jan 22. -# -# http://www.fiji.gov.fj/index.php?option=com_content&view=article&id=5017:amendments-to-daylight-savings&catid=71:press-releases&Itemid=155 -# states: -# -# The end of daylight saving scheduled initially for the 26th of February 2012 -# has been brought forward to the 22nd of January 2012. -# The commencement of daylight saving will remain unchanged and start -# on the 23rd of October, 2011. - -# From the Fiji Government Online Portal (2012-08-21) via Steffen Thorsen: -# The Minister for Labour, Industrial Relations and Employment Mr Jone Usamate -# today confirmed that Fiji will start daylight savings at 2 am on Sunday 21st -# October 2012 and end at 3 am on Sunday 20th January 2013. -# http://www.fiji.gov.fj/index.php?option=com_content&view=article&id=6702&catid=71&Itemid=155 - -# From the Fijian Government Media Center (2013-08-30) via David Wheeler: -# Fiji will start daylight savings on Sunday 27th October, 2013 ... -# move clocks forward by one hour from 2am -# http://www.fiji.gov.fj/Media-Center/Press-Releases/DAYLIGHT-SAVING-STARTS-ON-SUNDAY,-27th-OCTOBER-201.aspx - -# From Steffen Thorsen (2013-01-10): -# Fiji will end DST on 2014-01-19 02:00: -# http://www.fiji.gov.fj/Media-Center/Press-Releases/DAYLIGHT-SAVINGS-TO-END-THIS-MONTH-%281%29.aspx - -# From Ken Rylander (2014-10-20): -# DST will start Nov. 2 this year. -# http://www.fiji.gov.fj/Media-Center/Press-Releases/DAYLIGHT-SAVING-STARTS-ON-SUNDAY,-NOVEMBER-2ND.aspx - -# From a government order dated 2015-08-26 and published as Legal Notice No. 77 -# in the Government of Fiji Gazette Supplement No. 24 (2015-08-28), -# via Ken Rylander (2015-09-02): -# the daylight saving period is 1 hour in advance of the standard time -# commencing at 2.00 am on Sunday 1st November, 2015 and ending at -# 3.00 am on Sunday 17th January, 2016. - -# From Raymond Kumar (2016-10-04): -# http://www.fiji.gov.fj/Media-Center/Press-Releases/DAYLIGHT-SAVING-STARTS-ON-6th-NOVEMBER,-2016.aspx -# "Fiji's daylight savings will begin on Sunday, 6 November 2016, when -# clocks go forward an hour at 2am to 3am.... Daylight Saving will -# end at 3.00am on Sunday 15th January 2017." - -# From Paul Eggert (2017-08-21): -# Dominic Fok writes (2017-08-20) that DST ends 2018-01-14, citing -# Extraordinary Government of Fiji Gazette Supplement No. 21 (2017-08-27), -# [Legal Notice No. 41] of an order of the previous day by J Usamate. -# For now, guess DST from 02:00 the first Sunday in November to 03:00 -# the first Sunday on or after January 14. Although ad hoc, it matches -# transitions since late 2014 and seems more likely to match future -# practice than guessing no DST. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Fiji 1998 1999 - Nov Sun>=1 2:00 1:00 - -Rule Fiji 1999 2000 - Feb lastSun 3:00 0 - -Rule Fiji 2009 only - Nov 29 2:00 1:00 - -Rule Fiji 2010 only - Mar lastSun 3:00 0 - -Rule Fiji 2010 2013 - Oct Sun>=21 2:00 1:00 - -Rule Fiji 2011 only - Mar Sun>=1 3:00 0 - -Rule Fiji 2012 2013 - Jan Sun>=18 3:00 0 - -Rule Fiji 2014 only - Jan Sun>=18 2:00 0 - -Rule Fiji 2014 max - Nov Sun>=1 2:00 1:00 - -Rule Fiji 2015 max - Jan Sun>=14 3:00 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Fiji 11:55:44 - LMT 1915 Oct 26 # Suva - 12:00 Fiji +12/+13 - -# French Polynesia -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Gambier -8:59:48 - LMT 1912 Oct # Rikitea - -9:00 - -09 -Zone Pacific/Marquesas -9:18:00 - LMT 1912 Oct - -9:30 - -0930 -Zone Pacific/Tahiti -9:58:16 - LMT 1912 Oct # Papeete - -10:00 - -10 -# Clipperton (near North America) is administered from French Polynesia; -# it is uninhabited. - -# Guam -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Guam -14:21:00 - LMT 1844 Dec 31 - 9:39:00 - LMT 1901 # Agana - 10:00 - GST 2000 Dec 23 # Guam - 10:00 - ChST # Chamorro Standard Time -Link Pacific/Guam Pacific/Saipan # N Mariana Is - -# Kiribati -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Tarawa 11:32:04 - LMT 1901 # Bairiki - 12:00 - +12 -Zone Pacific/Enderbury -11:24:20 - LMT 1901 - -12:00 - -12 1979 Oct - -11:00 - -11 1994 Dec 31 - 13:00 - +13 -Zone Pacific/Kiritimati -10:29:20 - LMT 1901 - -10:40 - -1040 1979 Oct - -10:00 - -10 1994 Dec 31 - 14:00 - +14 - -# N Mariana Is -# See Pacific/Guam. - -# Marshall Is -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Majuro 11:24:48 - LMT 1901 - 11:00 - +11 1969 Oct - 12:00 - +12 -Zone Pacific/Kwajalein 11:09:20 - LMT 1901 - 11:00 - +11 1969 Oct - -12:00 - -12 1993 Aug 20 - 12:00 - +12 - -# Micronesia -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Chuuk 10:07:08 - LMT 1901 - 10:00 - +10 -Zone Pacific/Pohnpei 10:32:52 - LMT 1901 # Kolonia - 11:00 - +11 -Zone Pacific/Kosrae 10:51:56 - LMT 1901 - 11:00 - +11 1969 Oct - 12:00 - +12 1999 - 11:00 - +11 - -# Nauru -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Nauru 11:07:40 - LMT 1921 Jan 15 # Uaobe - 11:30 - +1130 1942 Mar 15 - 9:00 - +09 1944 Aug 15 - 11:30 - +1130 1979 May - 12:00 - +12 - -# New Caledonia -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule NC 1977 1978 - Dec Sun>=1 0:00 1:00 - -Rule NC 1978 1979 - Feb 27 0:00 0 - -Rule NC 1996 only - Dec 1 2:00s 1:00 - -# Shanks & Pottenger say the following was at 2:00; go with IATA. -Rule NC 1997 only - Mar 2 2:00s 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Noumea 11:05:48 - LMT 1912 Jan 13 # Nouméa - 11:00 NC +11/+12 - - -############################################################################### - -# New Zealand - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule NZ 1927 only - Nov 6 2:00 1:00 S -Rule NZ 1928 only - Mar 4 2:00 0 M -Rule NZ 1928 1933 - Oct Sun>=8 2:00 0:30 S -Rule NZ 1929 1933 - Mar Sun>=15 2:00 0 M -Rule NZ 1934 1940 - Apr lastSun 2:00 0 M -Rule NZ 1934 1940 - Sep lastSun 2:00 0:30 S -Rule NZ 1946 only - Jan 1 0:00 0 S -# Since 1957 Chatham has been 45 minutes ahead of NZ, but until 2018a -# there was no documented single notation for the date and time of this -# transition. Duplicate the Rule lines for now, to give the 2018a change -# time to percolate out. -Rule NZ 1974 only - Nov Sun>=1 2:00s 1:00 D -Rule Chatham 1974 only - Nov Sun>=1 2:45s 1:00 - -Rule NZ 1975 only - Feb lastSun 2:00s 0 S -Rule Chatham 1975 only - Feb lastSun 2:45s 0 - -Rule NZ 1975 1988 - Oct lastSun 2:00s 1:00 D -Rule Chatham 1975 1988 - Oct lastSun 2:45s 1:00 - -Rule NZ 1976 1989 - Mar Sun>=1 2:00s 0 S -Rule Chatham 1976 1989 - Mar Sun>=1 2:45s 0 - -Rule NZ 1989 only - Oct Sun>=8 2:00s 1:00 D -Rule Chatham 1989 only - Oct Sun>=8 2:45s 1:00 - -Rule NZ 1990 2006 - Oct Sun>=1 2:00s 1:00 D -Rule Chatham 1990 2006 - Oct Sun>=1 2:45s 1:00 - -Rule NZ 1990 2007 - Mar Sun>=15 2:00s 0 S -Rule Chatham 1990 2007 - Mar Sun>=15 2:45s 0 - -Rule NZ 2007 max - Sep lastSun 2:00s 1:00 D -Rule Chatham 2007 max - Sep lastSun 2:45s 1:00 - -Rule NZ 2008 max - Apr Sun>=1 2:00s 0 S -Rule Chatham 2008 max - Apr Sun>=1 2:45s 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Auckland 11:39:04 - LMT 1868 Nov 2 - 11:30 NZ NZ%sT 1946 Jan 1 - 12:00 NZ NZ%sT -Zone Pacific/Chatham 12:13:48 - LMT 1868 Nov 2 - 12:15 - +1215 1946 Jan 1 - 12:45 Chatham +1245/+1345 - -Link Pacific/Auckland Antarctica/McMurdo - -# Auckland Is -# uninhabited; Māori and Moriori, colonial settlers, pastoralists, sealers, -# and scientific personnel have wintered - -# Campbell I -# minor whaling stations operated 1909/1914 -# scientific station operated 1941/1995; -# previously whalers, sealers, pastoralists, and scientific personnel wintered -# was probably like Pacific/Auckland - -# Cook Is -# From Shanks & Pottenger: -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Cook 1978 only - Nov 12 0:00 0:30 - -Rule Cook 1979 1991 - Mar Sun>=1 0:00 0 - -Rule Cook 1979 1990 - Oct lastSun 0:00 0:30 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Rarotonga -10:39:04 - LMT 1901 # Avarua - -10:30 - -1030 1978 Nov 12 - -10:00 Cook -10/-0930 - -############################################################################### - - -# Niue -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Niue -11:19:40 - LMT 1901 # Alofi - -11:20 - -1120 1951 - -11:30 - -1130 1978 Oct 1 - -11:00 - -11 - -# Norfolk -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Norfolk 11:11:52 - LMT 1901 # Kingston - 11:12 - +1112 1951 - 11:30 - +1130 1974 Oct 27 02:00 - 11:30 1:00 +1230 1975 Mar 2 02:00 - 11:30 - +1130 2015 Oct 4 02:00 - 11:00 - +11 - -# Palau (Belau) -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Palau 8:57:56 - LMT 1901 # Koror - 9:00 - +09 - -# Papua New Guinea -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Port_Moresby 9:48:40 - LMT 1880 - 9:48:32 - PMMT 1895 # Port Moresby Mean Time - 10:00 - +10 -# -# From Paul Eggert (2014-10-13): -# Base the Bougainville entry on the Arawa-Kieta region, which appears to have -# the most people even though it was devastated in the Bougainville Civil War. -# -# Although Shanks gives 1942-03-15 / 1943-11-01 for UT +09, these dates -# are apparently rough guesswork from the starts of military campaigns. -# The World War II entries below are instead based on Arawa-Kieta. -# The Japanese occupied Kieta in July 1942, -# according to the Pacific War Online Encyclopedia -# https://pwencycl.kgbudge.com/B/o/Bougainville.htm -# and seem to have controlled it until their 1945-08-21 surrender. -# -# The Autonomous Region of Bougainville switched from UT +10 to +11 -# on 2014-12-28 at 02:00. They call +11 "Bougainville Standard Time". -# See: -# http://www.bougainville24.com/bougainville-issues/bougainville-gets-own-timezone/ -# -Zone Pacific/Bougainville 10:22:16 - LMT 1880 - 9:48:32 - PMMT 1895 - 10:00 - +10 1942 Jul - 9:00 - +09 1945 Aug 21 - 10:00 - +10 2014 Dec 28 2:00 - 11:00 - +11 - -# Pitcairn -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Pitcairn -8:40:20 - LMT 1901 # Adamstown - -8:30 - -0830 1998 Apr 27 0:00 - -8:00 - -08 - -# American Samoa -Zone Pacific/Pago_Pago 12:37:12 - LMT 1892 Jul 5 - -11:22:48 - LMT 1911 - -11:00 - SST # S=Samoa -Link Pacific/Pago_Pago Pacific/Midway # in US minor outlying islands - -# Samoa (formerly and also known as Western Samoa) - -# From Steffen Thorsen (2009-10-16): -# We have been in contact with the government of Samoa again, and received -# the following info: -# -# "Cabinet has now approved Daylight Saving to be effected next year -# commencing from the last Sunday of September 2010 and conclude first -# Sunday of April 2011." -# -# Background info: -# https://www.timeanddate.com/news/time/samoa-dst-plan-2009.html -# -# Samoa's Daylight Saving Time Act 2009 is available here, but does not -# contain any dates: -# http://www.parliament.gov.ws/documents/acts/Daylight%20Saving%20Act%20%202009%20%28English%29%20-%20Final%207-7-091.pdf - -# From Laupue Raymond Hughes (2010-10-07): -# Please see -# http://www.mcil.gov.ws -# the Ministry of Commerce, Industry and Labour (sideframe) "Last Sunday -# September 2010 (26/09/10) - adjust clocks forward from 12:00 midnight -# to 01:00am and First Sunday April 2011 (03/04/11) - adjust clocks -# backwards from 1:00am to 12:00am" - -# From Laupue Raymond Hughes (2011-03-07): -# [http://www.mcil.gov.ws/ftcd/daylight_saving_2011.pdf] -# -# ... when the standard time strikes the hour of four o'clock (4.00am -# or 0400 Hours) on the 2nd April 2011, then all instruments used to -# measure standard time are to be adjusted/changed to three o'clock -# (3:00am or 0300Hrs). - -# From David Zülke (2011-05-09): -# Subject: Samoa to move timezone from east to west of international date line -# -# http://www.morningstar.co.uk/uk/markets/newsfeeditem.aspx?id=138501958347963 - -# From Paul Eggert (2014-06-27): -# The International Date Line Act 2011 -# http://www.parliament.gov.ws/images/ACTS/International_Date_Line_Act__2011_-_Eng.pdf -# changed Samoa from UT -11 to +13, effective "12 o'clock midnight, on -# Thursday 29th December 2011". The International Date Line was adjusted -# accordingly. - -# From Laupue Raymond Hughes (2011-09-02): -# http://www.mcil.gov.ws/mcil_publications.html -# -# here is the official website publication for Samoa DST and dateline change -# -# DST -# Year End Time Start Time -# 2011 - - - - - - 24 September 3:00am to 4:00am -# 2012 01 April 4:00am to 3:00am - - - - - - -# -# Dateline Change skip Friday 30th Dec 2011 -# Thursday 29th December 2011 23:59:59 Hours -# Saturday 31st December 2011 00:00:00 Hours -# -# From Nicholas Pereira (2012-09-10): -# Daylight Saving Time commences on Sunday 30th September 2012 and -# ends on Sunday 7th of April 2013.... -# http://www.mcil.gov.ws/mcil_publications.html -# -# From Paul Eggert (2014-07-08): -# That web page currently lists transitions for 2012/3 and 2013/4. -# Assume the pattern instituted in 2012 will continue indefinitely. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule WS 2010 only - Sep lastSun 0:00 1 - -Rule WS 2011 only - Apr Sat>=1 4:00 0 - -Rule WS 2011 only - Sep lastSat 3:00 1 - -Rule WS 2012 max - Apr Sun>=1 4:00 0 - -Rule WS 2012 max - Sep lastSun 3:00 1 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Apia 12:33:04 - LMT 1892 Jul 5 - -11:26:56 - LMT 1911 - -11:30 - -1130 1950 - -11:00 WS -11/-10 2011 Dec 29 24:00 - 13:00 WS +13/+14 - -# Solomon Is -# excludes Bougainville, for which see Papua New Guinea -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Guadalcanal 10:39:48 - LMT 1912 Oct # Honiara - 11:00 - +11 - -# Tokelau -# -# From Gwillim Law (2011-12-29) -# A correspondent informed me that Tokelau, like Samoa, will be skipping -# December 31 this year ... -# -# From Steffen Thorsen (2012-07-25) -# ... we double checked by calling hotels and offices based in Tokelau asking -# about the time there, and they all told a time that agrees with UTC+13.... -# Shanks says UT-10 from 1901 [but] ... there is a good chance the change -# actually was to UT-11 back then. -# -# From Paul Eggert (2012-07-25) -# A Google Books snippet of Appendix to the Journals of the House of -# Representatives of New Zealand, Session 1948, -# , page 65, says Tokelau -# was "11 hours slow on G.M.T." Go with Thorsen and assume Shanks & Pottenger -# are off by an hour starting in 1901. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Fakaofo -11:24:56 - LMT 1901 - -11:00 - -11 2011 Dec 30 - 13:00 - +13 - -# Tonga -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Tonga 1999 only - Oct 7 2:00s 1:00 - -Rule Tonga 2000 only - Mar 19 2:00s 0 - -Rule Tonga 2000 2001 - Nov Sun>=1 2:00 1:00 - -Rule Tonga 2001 2002 - Jan lastSun 2:00 0 - -Rule Tonga 2016 only - Nov Sun>=1 2:00 1:00 - -Rule Tonga 2017 only - Jan Sun>=15 3:00 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Tongatapu 12:19:20 - LMT 1901 - 12:20 - +1220 1941 - 13:00 - +13 1999 - 13:00 Tonga +13/+14 - -# Tuvalu -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Funafuti 11:56:52 - LMT 1901 - 12:00 - +12 - - -# US minor outlying islands - -# Howland, Baker -# Howland was mined for guano by American companies 1857-1878 and British -# 1886-1891; Baker was similar but exact dates are not known. -# Inhabited by civilians 1935-1942; U.S. military bases 1943-1944; -# uninhabited thereafter. -# Howland observed Hawaii Standard Time (UT -10:30) in 1937; -# see page 206 of Elgen M. Long and Marie K. Long, -# Amelia Earhart: the Mystery Solved, Simon & Schuster (2000). -# So most likely Howland and Baker observed Hawaii Time from 1935 -# until they were abandoned after the war. - -# Jarvis -# Mined for guano by American companies 1857-1879 and British 1883?-1891?. -# Inhabited by civilians 1935-1942; IGY scientific base 1957-1958; -# uninhabited thereafter. -# no information; was probably like Pacific/Kiritimati - -# Johnston -# -# From Paul Eggert (2017-02-10): -# Sometimes Johnston kept Hawaii time, and sometimes it was an hour behind. -# Details are uncertain. We have no data for Johnston after 1970, so -# treat it like Hawaii for now. Since Johnston is now uninhabited, -# its link to Pacific/Honolulu is in the 'backward' file. -# -# In his memoirs of June 6th to October 4, 1945 -# (2005), Herbert C. Bach writes, -# "We started our letdown to Kwajalein Atoll and landed there at 5:00 AM -# Johnston time, 1:30 AM Kwajalein time." This was in June 1945, and -# confirms that Johnston kept the same time as Honolulu in summer 1945. -# -# From Lyle McElhaney (2014-03-11): -# [W]hen JI was being used for that [atomic bomb] testing, the time being used -# was not Hawaiian time but rather the same time being used on the ships, -# which had a GMT offset of -11 hours. This apparently applied to at least the -# time from Operation Newsreel (Hardtack I/Teak shot, 1958-08-01) to the last -# Operation Fishbowl shot (Tightrope, 1962-11-04).... [See] Herman Hoerlin, -# "The United States High-Altitude Test Experience: A Review Emphasizing the -# Impact on the Environment", Los Alamos LA-6405, Oct 1976. -# https://www.fas.org/sgp/othergov/doe/lanl/docs1/00322994.pdf -# See the table on page 4 where he lists GMT and local times for the tests; a -# footnote for the JI tests reads that local time is "JI time = Hawaii Time -# Minus One Hour". - -# Kingman -# uninhabited - -# Midway -# See Pacific/Pago_Pago. - -# Palmyra -# uninhabited since World War II; was probably like Pacific/Kiritimati - -# Wake -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Wake 11:06:28 - LMT 1901 - 12:00 - +12 - - -# Vanuatu -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Vanuatu 1983 only - Sep 25 0:00 1:00 - -Rule Vanuatu 1984 1991 - Mar Sun>=23 0:00 0 - -Rule Vanuatu 1984 only - Oct 23 0:00 1:00 - -Rule Vanuatu 1985 1991 - Sep Sun>=23 0:00 1:00 - -Rule Vanuatu 1992 1993 - Jan Sun>=23 0:00 0 - -Rule Vanuatu 1992 only - Oct Sun>=23 0:00 1:00 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Efate 11:13:16 - LMT 1912 Jan 13 # Vila - 11:00 Vanuatu +11/+12 - -# Wallis and Futuna -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Wallis 12:15:20 - LMT 1901 - 12:00 - +12 - -############################################################################### - -# NOTES - -# This file is by no means authoritative; if you think you know better, -# go ahead and edit the file (and please send any changes to -# tz@iana.org for general use in the future). For more, please see -# the file CONTRIBUTING in the tz distribution. - -# From Paul Eggert (2017-02-10): -# -# Unless otherwise specified, the source for data through 1990 is: -# Thomas G. Shanks and Rique Pottenger, The International Atlas (6th edition), -# San Diego: ACS Publications, Inc. (2003). -# Unfortunately this book contains many errors and cites no sources. -# -# Many years ago Gwillim Law wrote that a good source -# for time zone data was the International Air Transport -# Association's Standard Schedules Information Manual (IATA SSIM), -# published semiannually. Law sent in several helpful summaries -# of the IATA's data after 1990. Except where otherwise noted, -# IATA SSIM is the source for entries after 1990. -# -# Another source occasionally used is Edward W. Whitman, World Time Differences, -# Whitman Publishing Co, 2 Niagara Av, Ealing, London (undated), which -# I found in the UCLA library. -# -# For data circa 1899, a common source is: -# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94. -# https://www.jstor.org/stable/1774359 -# -# A reliable and entertaining source about time zones is -# Derek Howse, Greenwich time and longitude, Philip Wilson Publishers (1997). -# -# The following abbreviations are from other sources. -# Corrections are welcome! -# std dst -# LMT Local Mean Time -# 8:00 AWST AWDT Western Australia -# 9:30 ACST ACDT Central Australia -# 10:00 AEST AEDT Eastern Australia -# 10:00 GST Guam through 2000 -# 10:00 ChST Chamorro -# 11:30 NZMT NZST New Zealand through 1945 -# 12:00 NZST NZDT New Zealand 1946-present -# -11:00 SST Samoa -# -10:00 HST Hawaii -# -# See the 'northamerica' file for Hawaii. -# See the 'southamerica' file for Easter I and the Galápagos Is. - -############################################################################### - -# Australia - -# From Paul Eggert (2014-06-30): -# Daylight saving time has long been controversial in Australia, pitting -# region against region, rural against urban, and local against global. -# For example, in her review of Graeme Davison's _The Unforgiving -# Minute: how Australians learned to tell the time_ (1993), Perth native -# Phillipa J Martyr wrote, "The section entitled 'Saving Daylight' was -# very informative, but was (as can, sadly, only be expected from a -# Melbourne-based study) replete with the usual chuckleheaded -# Queenslanders and straw-chewing yokels from the West prattling fables -# about fading curtains and crazed farm animals." -# Electronic Journal of Australian and New Zealand History (1997-03-03) -# http://www.jcu.edu.au/aff/history/reviews/davison.htm - -# From Paul Eggert (2005-12-08): -# Implementation Dates of Daylight Saving Time within Australia -# http://www.bom.gov.au/climate/averages/tables/dst_times.shtml -# summarizes daylight saving issues in Australia. - -# From Arthur David Olson (2005-12-12): -# Lawlink NSW:Daylight Saving in New South Wales -# http://www.lawlink.nsw.gov.au/lawlink/Corporate/ll_agdinfo.nsf/pages/community_relations_daylight_saving -# covers New South Wales in particular. - -# From John Mackin (1991-03-06): -# We in Australia have _never_ referred to DST as 'daylight' time. -# It is called 'summer' time. Now by a happy coincidence, 'summer' -# and 'standard' happen to start with the same letter; hence, the -# abbreviation does _not_ change... -# The legislation does not actually define abbreviations, at least -# in this State, but the abbreviation is just commonly taken to be the -# initials of the phrase, and the legislation here uniformly uses -# the phrase 'summer time' and does not use the phrase 'daylight -# time'. -# Announcers on the Commonwealth radio network, the ABC (for Australian -# Broadcasting Commission), use the phrases 'Eastern Standard Time' -# or 'Eastern Summer Time'. (Note, though, that as I say in the -# current australasia file, there is really no such thing.) Announcers -# on its overseas service, Radio Australia, use the same phrases -# prefixed by the word 'Australian' when referring to local times; -# time announcements on that service, naturally enough, are made in UTC. - -# From Paul Eggert (2014-06-30): -# -# Inspired by Mackin's remarks quoted above, earlier versions of this -# file used "EST" for both Eastern Standard Time and Eastern Summer -# Time in Australia, and similarly for "CST", "CWST", and "WST". -# However, these abbreviations were confusing and were not common -# practice among Australians, and there were justifiable complaints -# about them, so I attempted to survey current Australian usage. -# For the tz database, the full English phrase is not that important; -# what matters is the abbreviation. It's difficult to survey the web -# directly for abbreviation usage, as there are so many false hits for -# strings like "EST" and "EDT", so I looked for pages that defined an -# abbreviation for eastern or central DST in Australia, and got the -# following numbers of unique hits for the listed Google queries: -# -# 10 "Eastern Daylight Time AEST" site:au [some are false hits] -# 10 "Eastern Summer Time AEST" site:au -# 10 "Summer Time AEDT" site:au -# 13 "EDST Eastern Daylight Saving Time" site:au -# 18 "Summer Time ESST" site:au -# 28 "Eastern Daylight Saving Time EDST" site:au -# 39 "EDT Eastern Daylight Time" site:au [some are false hits] -# 53 "Eastern Daylight Time EDT" site:au [some are false hits] -# 54 "AEDT Australian Eastern Daylight Time" site:au -# 182 "Eastern Daylight Time AEDT" site:au -# -# 17 "Central Daylight Time CDT" site:au [some are false hits] -# 46 "Central Daylight Time ACDT" site:au -# -# I tried several other variants (e.g., "Eastern Summer Time EST") but -# they all returned fewer than 10 unique hits. I also looked for pages -# mentioning both "western standard time" and an abbreviation, since -# there is no WST in the US to generate false hits, and found: -# -# 156 "western standard time" AWST site:au -# 226 "western standard time" WST site:au -# -# I then surveyed the top ten newspapers in Australia by circulation as -# listed in Wikipedia, using Google queries like "AEDT site:heraldsun.com.au" -# and obtaining estimated counts from the initial page of search results. -# All ten papers greatly preferred "AEDT" to "EDT". The papers -# surveyed were the Herald Sun, The Daily Telegraph, The Courier-Mail, -# The Sydney Morning Herald, The West Australian, The Age, The Advertiser, -# The Australian, The Financial Review, and The Herald (Newcastle). -# -# I also searched for historical usage, to see whether abbreviations -# like "AEDT" are new. A Trove search -# found only one newspaper (The Canberra Times) with a house style -# dating back to the 1970s, I expect because other newspapers weren't -# fully indexed. The Canberra Times strongly preferred abbreviations -# like "AEDT". The first occurrence of "AEDT" was a World Weather -# column (1971-11-17, page 24), and of "ACDT" was a Scoreboard column -# (1993-01-24, p 16). The style was the typical usage but was not -# strictly enforced; for example, "Welcome to the twilight zones ..." -# (1994-10-29, p 1) uses the abbreviations AEST/AEDT, CST/CDT, and -# WST, and goes on to say, "The confusion and frustration some feel -# about the lack of uniformity among Australia's six states and two -# territories has prompted one group to form its very own political -# party -- the Sydney-based Daylight Saving Extension Party." -# -# I also surveyed federal government sources. They did not agree: -# -# The Australian Government (2014-03-26) -# http://australia.gov.au/about-australia/our-country/time -# (This document was produced by the Department of Finance.) -# AEST ACST AWST AEDT ACDT -# -# Bureau of Meteorology (2012-11-08) -# http://www.bom.gov.au/climate/averages/tables/daysavtm.shtml -# EST CST WST EDT CDT -# -# Civil Aviation Safety Authority (undated) -# http://services.casa.gov.au/outnback/inc/pages/episode3/episode-3_time_zones.shtml -# EST CST WST (no abbreviations given for DST) -# -# Geoscience Australia (2011-11-24) -# http://www.ga.gov.au/geodesy/astro/sunrise.jsp -# AEST ACST AWST AEDT ACDT -# -# Parliamentary Library (2008-11-10) -# https://www.aph.gov.au/binaries/library/pubs/rp/2008-09/09rp14.pdf -# EST CST WST preferred for standard time; AEST AEDT ACST ACDT also used -# -# The Transport Safety Bureau has an extensive series of accident reports, -# and investigators seem to use whatever abbreviation they like. -# Googling site:atsb.gov.au found the following number of unique hits: -# 311 "ESuT", 195 "EDT", 26 "AEDT", 83 "CSuT", 46 "CDT". -# "_SuT" tended to appear in older reports, and "A_DT" tended to -# appear in reports of events with international implications. -# -# From the above it appears that there is a working consensus in -# Australia to use trailing "DT" for daylight saving time; although -# some sources use trailing "SST" or "ST" or "SuT" they are by far in -# the minority. The case for leading "A" is weaker, but since it -# seems to be preferred in the overall web and is preferred in all -# the leading newspaper websites and in many government departments, -# it has a stronger case than omitting the leading "A". The current -# version of the database therefore uses abbreviations like "AEST" and -# "AEDT" for Australian time zones. - -# From Paul Eggert (1995-12-19): -# Shanks & Pottenger report 2:00 for all autumn changes in Australia and NZ. -# Mark Prior writes that his newspaper -# reports that NSW's fall 1995 change will occur at 2:00, -# but Robert Elz says it's been 3:00 in Victoria since 1970 -# and perhaps the newspaper's '2:00' is referring to standard time. -# For now we'll continue to assume 2:00s for changes since 1960. - -# From Eric Ulevik (1998-01-05): -# -# Here are some URLs to Australian time legislation. These URLs are stable, -# and should probably be included in the data file. There are probably more -# relevant entries in this database. -# -# NSW (including LHI and Broken Hill): -# Standard Time Act 1987 (updated 1995-04-04) -# https://www.austlii.edu.au/au/legis/nsw/consol_act/sta1987137/index.html -# ACT -# Standard Time and Summer Time Act 1972 -# https://www.austlii.edu.au/au/legis/act/consol_act/stasta1972279/index.html -# SA -# Standard Time Act, 1898 -# https://www.austlii.edu.au/au/legis/sa/consol_act/sta1898137/index.html - -# From David Grosz (2005-06-13): -# It was announced last week that Daylight Saving would be extended by -# one week next year to allow for the 2006 Commonwealth Games. -# Daylight Saving is now to end for next year only on the first Sunday -# in April instead of the last Sunday in March. -# -# From Gwillim Law (2005-06-14): -# I did some Googling and found that all of those states (and territory) plan -# to extend DST together in 2006. -# ACT: http://www.cmd.act.gov.au/mediareleases/fileread.cfm?file=86.txt -# New South Wales: http://www.thecouriermail.news.com.au/common/story_page/0,5936,15538869%255E1702,00.html -# South Australia: http://www.news.com.au/story/0,10117,15555031-1246,00.html -# Tasmania: http://www.media.tas.gov.au/release.php?id=14772 -# Victoria: I wasn't able to find anything separate, but the other articles -# allude to it. -# But not Queensland -# http://www.news.com.au/story/0,10117,15564030-1248,00.html - -# Northern Territory - -# From George Shepherd via Simon Woodhead via Robert Elz (1991-03-06): -# # The NORTHERN TERRITORY.. [ Courtesy N.T. Dept of the Chief Minister ] -# # [ Nov 1990 ] -# # N.T. have never utilised any DST due to sub-tropical/tropical location. -# ... -# Zone Australia/North 9:30 - CST - -# From Bradley White (1991-03-04): -# A recent excerpt from an Australian newspaper... -# the Northern Territory do[es] not have daylight saving. - -# Western Australia - -# From George Shepherd via Simon Woodhead via Robert Elz (1991-03-06): -# # The state of WESTERN AUSTRALIA.. [ Courtesy W.A. dept Premier+Cabinet ] -# # [ Nov 1990 ] -# # W.A. suffers from a great deal of public and political opposition to -# # DST in principle. A bill is brought before parliament in most years, but -# # usually defeated either in the upper house, or in party caucus -# # before reaching parliament. -# ... -# Zone Australia/West 8:00 AW %sST -# ... -# Rule AW 1974 only - Oct lastSun 2:00 1:00 D -# Rule AW 1975 only - Mar Sun>=1 3:00 0 W -# Rule AW 1983 only - Oct lastSun 2:00 1:00 D -# Rule AW 1984 only - Mar Sun>=1 3:00 0 W - -# From Bradley White (1991-03-04): -# A recent excerpt from an Australian newspaper... -# Western Australia...do[es] not have daylight saving. - -# From John D. Newman via Bradley White (1991-11-02): -# Western Australia is still on "winter time". Some DH in Sydney -# rang me at home a few days ago at 6.00am. (He had just arrived at -# work at 9.00am.) -# W.A. is switching to Summer Time on Nov 17th just to confuse -# everybody again. - -# From Arthur David Olson (1992-03-08): -# The 1992 ending date used in the rules is a best guess; -# it matches what was used in the past. - -# The Australian Bureau of Meteorology FAQ -# http://www.bom.gov.au/faq/faqgen.htm -# (1999-09-27) writes that Giles Meteorological Station uses -# South Australian time even though it's located in Western Australia. - -# From Paul Eggert (2018-04-01): -# The Guardian Express of Perth, Australia reported today that the -# government decided to advance the clocks permanently on January 1, -# 2019, from UT +08 to UT +09. The article noted that an exemption -# would be made for people aged 61 and over, who "can apply in writing -# to have the extra hour of sunshine removed from their area." See: -# Daylight saving coming to WA in 2019. Guardian Express. 2018-04-01. -# https://www.communitynews.com.au/guardian-express/news/exclusive-daylight-savings-coming-wa-summer-2018/ - -# Queensland - -# From Paul Eggert (2018-02-26): -# I lack access to the following source for Queensland DST: -# Pearce C. History of daylight saving time in Queensland. -# Queensland Hist J. 2017 Aug;23(6):389-403 -# https://search.informit.com.au/documentSummary;dn=994682348436426;res=IELHSS - -# From George Shepherd via Simon Woodhead via Robert Elz (1991-03-06): -# # The state of QUEENSLAND.. [ Courtesy Qld. Dept Premier Econ&Trade Devel ] -# # [ Dec 1990 ] -# ... -# Zone Australia/Queensland 10:00 AQ %sST -# ... -# Rule AQ 1971 only - Oct lastSun 2:00 1:00 D -# Rule AQ 1972 only - Feb lastSun 3:00 0 E -# Rule AQ 1989 max - Oct lastSun 2:00 1:00 D -# Rule AQ 1990 max - Mar Sun>=1 3:00 0 E - -# From Bradley White (1989-12-24): -# "Australia/Queensland" now observes daylight time (i.e. from -# October 1989). - -# From Bradley White (1991-03-04): -# A recent excerpt from an Australian newspaper... -# ...Queensland...[has] agreed to end daylight saving -# at 3am tomorrow (March 3)... - -# From John Mackin (1991-03-06): -# I can certainly confirm for my part that Daylight Saving in NSW did in fact -# end on Sunday, 3 March. I don't know at what hour, though. (It surprised -# me.) - -# From Bradley White (1992-03-08): -# ...there was recently a referendum in Queensland which resulted -# in the experimental daylight saving system being abandoned. So, ... -# ... -# Rule QLD 1989 1991 - Oct lastSun 2:00 1:00 D -# Rule QLD 1990 1992 - Mar Sun>=1 3:00 0 S -# ... - -# From Arthur David Olson (1992-03-08): -# The chosen rules the union of the 1971/1972 change and the 1989-1992 changes. - -# From Christopher Hunt (2006-11-21), after an advance warning -# from Jesper Nørgaard Welen (2006-11-01): -# WA are trialing DST for three years. -# http://www.parliament.wa.gov.au/parliament/bills.nsf/9A1B183144403DA54825721200088DF1/$File/Bill175-1B.pdf - -# From Rives McDow (2002-04-09): -# The most interesting region I have found consists of three towns on the -# southern coast.... South Australia observes daylight saving time; Western -# Australia does not. The two states are one and a half hours apart. The -# residents decided to forget about this nonsense of changing the clock so -# much and set the local time 20 hours and 45 minutes from the -# international date line, or right in the middle of the time of South -# Australia and Western Australia.... -# -# From Paul Eggert (2002-04-09): -# This is confirmed by the section entitled -# "What's the deal with time zones???" in -# http://www.earthsci.unimelb.edu.au/~awatkins/null.html -# -# From Alex Livingston (2006-12-07): -# ... it was just on four years ago that I drove along the Eyre Highway, -# which passes through eastern Western Australia close to the southern -# coast of the continent. -# -# I paid particular attention to the time kept there. There can be no -# dispute that UTC+08:45 was considered "the time" from the border -# village just inside the border with South Australia to as far west -# as just east of Caiguna. There can also be no dispute that Eucla is -# the largest population centre in this zone.... -# -# Now that Western Australia is observing daylight saving, the -# question arose whether this part of the state would follow suit. I -# just called the border village and confirmed that indeed they have, -# meaning that they are now observing UTC+09:45. -# -# (2006-12-09): -# I personally doubt that either experimentation with daylight saving -# in WA or its introduction in SA had anything to do with the genesis -# of this time zone. My hunch is that it's been around since well -# before 1975. I remember seeing it noted on road maps decades ago. - -# From Paul Eggert (2006-12-15): -# For lack of better info, assume the tradition dates back to the -# introduction of standard time in 1895. - - -# southeast Australia -# -# From Paul Eggert (2007-07-23): -# Starting autumn 2008 Victoria, NSW, South Australia, Tasmania and the ACT -# end DST the first Sunday in April and start DST the first Sunday in October. -# http://www.theage.com.au/news/national/daylight-savings-to-span-six-months/2007/06/27/1182623966703.html - - -# South Australia - -# From Bradley White (1991-03-04): -# A recent excerpt from an Australian newspaper... -# ...South Australia...[has] agreed to end daylight saving -# at 3am tomorrow (March 3)... - -# From George Shepherd via Simon Woodhead via Robert Elz (1991-03-06): -# # The state of SOUTH AUSTRALIA....[ Courtesy of S.A. Dept of Labour ] -# # [ Nov 1990 ] -# ... -# Zone Australia/South 9:30 AS %sST -# ... -# Rule AS 1971 max - Oct lastSun 2:00 1:00 D -# Rule AS 1972 1985 - Mar Sun>=1 3:00 0 C -# Rule AS 1986 1990 - Mar Sun>=15 3:00 0 C -# Rule AS 1991 max - Mar Sun>=1 3:00 0 C - -# From Bradley White (1992-03-11): -# Recent correspondence with a friend in Adelaide -# contained the following exchange: "Due to the Adelaide Festival, -# South Australia delays setting back our clocks for a few weeks." - -# From Robert Elz (1992-03-13): -# I heard that apparently (or at least, it appears that) -# South Aus will have an extra 3 weeks daylight saving every even -# numbered year (from 1990). That's when the Adelaide Festival -# is on... - -# From Robert Elz (1992-03-16, 00:57:07 +1000): -# DST didn't end in Adelaide today (yesterday).... -# But whether it's "4th Sunday" or "2nd last Sunday" I have no idea whatever... -# (it's just as likely to be "the Sunday we pick for this year"...). - -# From Bradley White (1994-04-11): -# If Sun, 15 March, 1992 was at +1030 as kre asserts, but yet Sun, 20 March, -# 1994 was at +0930 as John Connolly's customer seems to assert, then I can -# only conclude that the actual rule is more complicated.... - -# From John Warburton (1994-10-07): -# The new Daylight Savings dates for South Australia ... -# was gazetted in the Government Hansard on Sep 26 1994.... -# start on last Sunday in October and end in last sunday in March. - -# From Paul Eggert (2007-07-23): -# See "southeast Australia" above for 2008 and later. - -# Tasmania - -# The rules for 1967 through 1991 were reported by George Shepherd -# via Simon Woodhead via Robert Elz (1991-03-06): -# # The state of TASMANIA.. [Courtesy Tasmanian Dept of Premier + Cabinet ] -# # [ Nov 1990 ] - -# From Bill Hart via Guy Harris (1991-10-10): -# Oh yes, the new daylight savings rules are uniquely tasmanian, we have -# 6 weeks a year now when we are out of sync with the rest of Australia -# (but nothing new about that). - -# From Alex Livingston (1999-10-04): -# I heard on the ABC (Australian Broadcasting Corporation) radio news on the -# (long) weekend that Tasmania, which usually goes its own way in this regard, -# has decided to join with most of NSW, the ACT, and most of Victoria -# (Australia) and start daylight saving on the last Sunday in August in 2000 -# instead of the first Sunday in October. - -# Sim Alam (2000-07-03) reported a legal citation for the 2000/2001 rules: -# http://www.thelaw.tas.gov.au/fragview/42++1968+GS3A@EN+2000070300 - -# From Paul Eggert (2007-07-23): -# See "southeast Australia" above for 2008 and later. - -# Victoria - -# The rules for 1971 through 1991 were reported by George Shepherd -# via Simon Woodhead via Robert Elz (1991-03-06): -# # The state of VICTORIA.. [ Courtesy of Vic. Dept of Premier + Cabinet ] -# # [ Nov 1990 ] - -# From Scott Harrington (2001-08-29): -# On KQED's "City Arts and Lectures" program last night I heard an -# interesting story about daylight savings time. Dr. John Heilbron was -# discussing his book "The Sun in the Church: Cathedrals as Solar -# Observatories"[1], and in particular the Shrine of Remembrance[2] located -# in Melbourne, Australia. -# -# Apparently the shrine's main purpose is a beam of sunlight which -# illuminates a special spot on the floor at the 11th hour of the 11th day -# of the 11th month (Remembrance Day) every year in memory of Australia's -# fallen WWI soldiers. And if you go there on Nov. 11, at 11am local time, -# you will indeed see the sunbeam illuminate the special spot at the -# expected time. -# -# However, that is only because of some special mirror contraption that had -# to be employed, since due to daylight savings time, the true solar time of -# the remembrance moment occurs one hour later (or earlier?). Perhaps -# someone with more information on this jury-rig can tell us more. -# -# [1] http://www.hup.harvard.edu/catalog/HEISUN.html -# [2] http://www.shrine.org.au - -# From Paul Eggert (2007-07-23): -# See "southeast Australia" above for 2008 and later. - -# New South Wales - -# From Arthur David Olson: -# New South Wales and subjurisdictions have their own ideas of a fun time. -# Based on law library research by John Mackin, -# who notes: -# In Australia, time is not legislated federally, but rather by the -# individual states. Thus, while such terms as "Eastern Standard Time" -# [I mean, of course, Australian EST, not any other kind] are in common -# use, _they have NO REAL MEANING_, as they are not defined in the -# legislation. This is very important to understand. -# I have researched New South Wales time only... - -# From Eric Ulevik (1999-05-26): -# DST will start in NSW on the last Sunday of August, rather than the usual -# October in 2000. See: Matthew Moore, -# Two months more daylight saving, Sydney Morning Herald (1999-05-26). -# http://www.smh.com.au/news/9905/26/pageone/pageone4.html - -# From Paul Eggert (1999-09-27): -# See the following official NSW source: -# Daylight Saving in New South Wales. -# http://dir.gis.nsw.gov.au/cgi-bin/genobject/document/other/daylightsaving/tigGmZ -# -# Narrabri Shire (NSW) council has announced it will ignore the extension of -# daylight saving next year. See: -# Narrabri Council to ignore daylight saving -# http://abc.net.au/news/regionals/neweng/monthly/regeng-22jul1999-1.htm -# (1999-07-22). For now, we'll wait to see if this really happens. -# -# Victoria will follow NSW. See: -# Vic to extend daylight saving (1999-07-28) -# http://abc.net.au/local/news/olympics/1999/07/item19990728112314_1.htm -# -# However, South Australia rejected the DST request. See: -# South Australia rejects Olympics daylight savings request (1999-07-19) -# http://abc.net.au/news/olympics/1999/07/item19990719151754_1.htm -# -# Queensland also will not observe DST for the Olympics. See: -# Qld says no to daylight savings for Olympics -# http://abc.net.au/news/olympics/1999/06/item19990601114608_1.htm -# (1999-06-01), which quotes Queensland Premier Peter Beattie as saying -# "Look you've got to remember in my family when this came up last time -# I voted for it, my wife voted against it and she said to me it's all very -# well for you, you don't have to worry about getting the children out of -# bed, getting them to school, getting them to sleep at night. -# I've been through all this argument domestically...my wife rules." -# -# Broken Hill will stick with South Australian time in 2000. See: -# Broken Hill to be behind the times (1999-07-21) -# http://abc.net.au/news/regionals/brokenh/monthly/regbrok-21jul1999-6.htm - -# IATA SSIM (1998-09) says that the spring 2000 change for Australian -# Capital Territory, New South Wales except Lord Howe Island and Broken -# Hill, and Victoria will be August 27, presumably due to the Sydney Olympics. - -# From Eric Ulevik, referring to Sydney's Sun Herald (2000-08-13), page 29: -# The Queensland Premier Peter Beattie is encouraging northern NSW -# towns to use Queensland time. - -# From Paul Eggert (2007-07-23): -# See "southeast Australia" above for 2008 and later. - -# Yancowinna - -# From John Mackin (1989-01-04): -# 'Broken Hill' means the County of Yancowinna. - -# From George Shepherd via Simon Woodhead via Robert Elz (1991-03-06): -# # YANCOWINNA.. [ Confirmation courtesy of Broken Hill Postmaster ] -# # [ Dec 1990 ] -# ... -# # Yancowinna uses Central Standard Time, despite [its] location on the -# # New South Wales side of the S.A. border. Most business and social dealings -# # are with CST zones, therefore CST is legislated by local government -# # although the switch to Summer Time occurs in line with N.S.W. There have -# # been years when this did not apply, but the historical data is not -# # presently available. -# Zone Australia/Yancowinna 9:30 AY %sST -# ... -# Rule AY 1971 1985 - Oct lastSun 2:00 1:00 D -# Rule AY 1972 only - Feb lastSun 3:00 0 C -# [followed by other Rules] - -# Lord Howe Island - -# From George Shepherd via Simon Woodhead via Robert Elz (1991-03-06): -# LHI... [ Courtesy of Pauline Van Winsen ] -# [ Dec 1990 ] -# Lord Howe Island is located off the New South Wales coast, and is half an -# hour ahead of NSW time. - -# From James Lonergan, Secretary, Lord Howe Island Board (2000-01-27): -# Lord Howe Island summer time in 2000/2001 will commence on the same -# date as the rest of NSW (i.e. 2000-08-27). For your information the -# Lord Howe Island Board (controlling authority for the Island) is -# seeking the community's views on various options for summer time -# arrangements on the Island, e.g. advance clocks by 1 full hour -# instead of only 30 minutes. [Dependent] on the wishes of residents -# the Board may approach the NSW government to change the existing -# arrangements. The starting date for summer time on the Island will -# however always coincide with the rest of NSW. - -# From James Lonergan, Secretary, Lord Howe Island Board (2000-10-25): -# Lord Howe Island advances clocks by 30 minutes during DST in NSW and retards -# clocks by 30 minutes when DST finishes. Since DST was most recently -# introduced in NSW, the "changeover" time on the Island has been 02:00 as -# shown on clocks on LHI. I guess this means that for 30 minutes at the start -# of DST, LHI is actually 1 hour ahead of the rest of NSW. - -# From Paul Eggert (2006-03-22): -# For Lord Howe dates we use Shanks & Pottenger through 1989, and -# Lonergan thereafter. For times we use Lonergan. - -# From Paul Eggert (2007-07-23): -# See "southeast Australia" above for 2008 and later. - -# From Steffen Thorsen (2009-04-28): -# According to the official press release, South Australia's extended daylight -# saving period will continue with the same rules as used during the 2008-2009 -# summer (southern hemisphere). -# -# From -# http://www.safework.sa.gov.au/uploaded_files/DaylightDatesSet.pdf -# The extended daylight saving period that South Australia has been trialling -# for over the last year is now set to be ongoing. -# Daylight saving will continue to start on the first Sunday in October each -# year and finish on the first Sunday in April the following year. -# Industrial Relations Minister, Paul Caica, says this provides South Australia -# with a consistent half hour time difference with NSW, Victoria, Tasmania and -# the ACT for all 52 weeks of the year... -# -# We have a wrap-up here: -# https://www.timeanddate.com/news/time/south-australia-extends-dst.html -############################################################################### - -# New Zealand - -# From Mark Davies (1990-10-03): -# the 1989/90 year was a trial of an extended "daylight saving" period. -# This trial was deemed successful and the extended period adopted for -# subsequent years (with the addition of a further week at the start). -# source - phone call to Ministry of Internal Affairs Head Office. - -# From George Shepherd via Simon Woodhead via Robert Elz (1991-03-06): -# # The Country of New Zealand (Australia's east island -) Gee they hate that! -# # or is Australia the west island of N.Z. -# # [ courtesy of Geoff Tribble.. Auckland N.Z. ] -# # [ Nov 1990 ] -# ... -# Rule NZ 1974 1988 - Oct lastSun 2:00 1:00 D -# Rule NZ 1989 max - Oct Sun>=1 2:00 1:00 D -# Rule NZ 1975 1989 - Mar Sun>=1 3:00 0 S -# Rule NZ 1990 max - Mar lastSun 3:00 0 S -# ... -# Zone NZ 12:00 NZ NZ%sT # New Zealand -# Zone NZ-CHAT 12:45 - NZ-CHAT # Chatham Island - -# From Arthur David Olson (1992-03-08): -# The chosen rules use the Davies October 8 values for the start of DST in 1989 -# rather than the October 1 value. - -# From Paul Eggert (1995-12-19); -# Shank & Pottenger report 2:00 for all autumn changes in Australia and NZ. -# Robert Uzgalis writes that the New Zealand Daylight -# Savings Time Order in Council dated 1990-06-18 specifies 2:00 standard -# time on both the first Sunday in October and the third Sunday in March. -# As with Australia, we'll assume the tradition is 2:00s, not 2:00. -# -# From Paul Eggert (2006-03-22): -# The Department of Internal Affairs (DIA) maintains a brief history, -# as does Carol Squires; see tz-link.html for the full references. -# Use these sources in preference to Shanks & Pottenger. -# -# For Chatham, IATA SSIM (1991/1999) gives the NZ rules but with -# transitions at 2:45 local standard time; this confirms that Chatham -# is always exactly 45 minutes ahead of Auckland. - -# From Colin Sharples (2007-04-30): -# DST will now start on the last Sunday in September, and end on the -# first Sunday in April. The changes take effect this year, meaning -# that DST will begin on 2007-09-30 2008-04-06. -# http://www.dia.govt.nz/diawebsite.nsf/wpg_URL/Services-Daylight-Saving-Daylight-saving-to-be-extended - -# From Paul Eggert (2014-07-14): -# Chatham Island time was formally standardized on 1957-01-01 by -# New Zealand's Standard Time Amendment Act 1956 (1956-10-26). -# https://www.austlii.edu.au/nz/legis/hist_act/staa19561956n100244.pdf -# According to Google Books snippet view, a speaker in the New Zealand -# parliamentary debates in 1956 said "Clause 78 makes provision for standard -# time in the Chatham Islands. The time there is 45 minutes in advance of New -# Zealand time. I understand that is the time they keep locally, anyhow." -# For now, assume this practice goes back to the introduction of standard time -# in New Zealand, as this would make Chatham Islands time almost exactly match -# LMT back when New Zealand was at UT +11:30; also, assume Chatham Islands did -# not observe New Zealand's prewar DST. - -############################################################################### - - -# Fiji - -# Howse writes (p 153) that in 1879 the British governor of Fiji -# enacted an ordinance standardizing the islands on Antipodean Time -# instead of the American system (which was one day behind). - -# From Rives McDow (1998-10-08): -# Fiji will introduce DST effective 0200 local time, 1998-11-01 -# until 0300 local time 1999-02-28. Each year the DST period will -# be from the first Sunday in November until the last Sunday in February. - -# From Paul Eggert (2000-01-08): -# IATA SSIM (1999-09) says DST ends 0100 local time. Go with McDow. - -# From the BBC World Service in -# http://news.bbc.co.uk/2/hi/asia-pacific/205226.stm (1998-10-31 16:03 UTC): -# The Fijian government says the main reasons for the time change is to -# improve productivity and reduce road accidents.... [T]he move is also -# intended to boost Fiji's ability to attract tourists to witness the dawning -# of the new millennium. - -# http://www.fiji.gov.fj/press/2000_09/2000_09_13-05.shtml (2000-09-13) -# reports that Fiji has discontinued DST. - - -# Kiribati - -# From Paul Eggert (1996-01-22): -# Today's _Wall Street Journal_ (page 1) reports that Kiribati -# "declared it the same day [throughout] the country as of Jan. 1, 1995" -# as part of the competition to be first into the 21st century. - -# From Kerry Shetline (2018-02-03): -# December 31 was the day that was skipped, so that the transition -# would be from Friday December 30, 1994 to Sunday January 1, 1995. -# From Paul Eggert (2018-02-04): -# One source for this is page 202 of: Bartky IR. One Time Fits All: -# The Campaigns for Global Uniformity (2007). - -# Kwajalein - -# In comp.risks 14.87 (26 August 1993), Peter Neumann writes: -# I wonder what happened in Kwajalein, where there was NO Friday, -# 1993-08-20. Thursday night at midnight Kwajalein switched sides with -# respect to the International Date Line, to rejoin its fellow islands, -# going from 11:59 p.m. Thursday to 12:00 m. Saturday in a blink. - - -# N Mariana Is, Guam - -# Howse writes (p 153) "The Spaniards, on the other hand, reached the -# Philippines and the Ladrones from America," and implies that the Ladrones -# (now called the Marianas) kept American date for quite some time. -# For now, we assume the Ladrones switched at the same time as the Philippines; -# see Asia/Manila. - -# US Public Law 106-564 (2000-12-23) made UT +10 the official standard time, -# under the name "Chamorro Standard Time". There is no official abbreviation, -# but Congressman Robert A. Underwood, author of the bill that became law, -# wrote in a press release (2000-12-27) that he will seek the use of "ChST". - - -# Micronesia - -# Alan Eugene Davis writes (1996-03-16), -# "I am certain, having lived there for the past decade, that 'Truk' -# (now properly known as Chuuk) ... is in the time zone GMT+10." -# -# Shanks & Pottenger write that Truk switched from UT +10 to +11 -# on 1978-10-01; ignore this for now. - -# From Paul Eggert (1999-10-29): -# The Federated States of Micronesia Visitors Board writes in -# The Federated States of Micronesia - Visitor Information (1999-01-26) -# http://www.fsmgov.org/info/clocks.html -# that Truk and Yap are UT +10, and Ponape and Kosrae are +11. -# We don't know when Kosrae switched from +12; assume January 1 for now. - - -# Midway - -# From Charles T O'Connor, KMTH DJ (1956), -# quoted in the KTMH section of the Radio Heritage Collection -# (2002-12-31): -# For the past two months we've been on what is known as Daylight -# Saving Time. This time has put us on air at 5am in the morning, -# your time down there in New Zealand. Starting September 2, 1956 -# we'll again go back to Standard Time. This'll mean that we'll go to -# air at 6am your time. -# -# From Paul Eggert (2003-03-23): -# We don't know the date of that quote, but we'll guess they -# started DST on June 3. Possibly DST was observed other years -# in Midway, but we have no record of it. - -# Norfolk - -# From Alexander Krivenyshev (2015-09-23): -# Norfolk Island will change ... from +1130 to +1100: -# https://www.comlaw.gov.au/Details/F2015L01483/Explanatory%20Statement/Text -# ... at 12.30 am (by legal time in New South Wales) on 4 October 2015. -# http://www.norfolkisland.gov.nf/nia/MediaRelease/Media%20Release%20Norfolk%20Island%20Standard%20Time%20Change.pdf - -# From Paul Eggert (2015-09-23): -# Transitions before 2015 are from timeanddate.com, which consulted -# the Norfolk Island Museum and the Australian Bureau of Meteorology's -# Norfolk Island station, and found no record of Norfolk observing DST -# other than in 1974/5. See: -# https://www.timeanddate.com/time/australia/norfolk-island.html - -# Pitcairn - -# From Rives McDow (1999-11-08): -# A Proclamation was signed by the Governor of Pitcairn on the 27th March 1998 -# with regard to Pitcairn Standard Time. The Proclamation is as follows. -# -# The local time for general purposes in the Islands shall be -# Co-ordinated Universal time minus 8 hours and shall be known -# as Pitcairn Standard Time. -# -# ... I have also seen Pitcairn listed as UTC minus 9 hours in several -# references, and can only assume that this was an error in interpretation -# somehow in light of this proclamation. - -# From Rives McDow (1999-11-09): -# The Proclamation regarding Pitcairn time came into effect on 27 April 1998 -# ... at midnight. - -# From Howie Phelps (1999-11-10), who talked to a Pitcairner via shortwave: -# Betty Christian told me yesterday that their local time is the same as -# Pacific Standard Time. They used to be ½ hour different from us here in -# Sacramento but it was changed a couple of years ago. - - -# (Western) Samoa and American Samoa - -# Howse writes (p 153) that after the 1879 standardization on Antipodean -# time by the British governor of Fiji, the King of Samoa decided to change -# "the date in his kingdom from the Antipodean to the American system, -# ordaining - by a masterpiece of diplomatic flattery - that -# the Fourth of July should be celebrated twice in that year." -# This happened in 1892, according to the Evening News (Sydney) of 1892-07-20. -# https://www.staff.science.uu.nl/~gent0113/idl/idl.htm - -# Although Shanks & Pottenger says they both switched to UT -11:30 -# in 1911, and to -11 in 1950. many earlier sources give -11 -# for American Samoa, e.g., the US National Bureau of Standards -# circular "Standard Time Throughout the World", 1932. -# Assume American Samoa switched to -11 in 1911, not 1950, -# and that after 1950 they agreed until (western) Samoa skipped a -# day in 2011. Assume also that the Samoas follow the US and New -# Zealand's "ST"/"DT" style of daylight-saving abbreviations. - - -# Tonga - -# From Paul Eggert (1996-01-22): -# Today's _Wall Street Journal_ (p 1) reports that "Tonga has been plotting -# to sneak ahead of [New Zealanders] by introducing daylight-saving time." -# Since Kiribati has moved the Date Line it's not clear what Tonga will do. - -# Don Mundell writes in the 1997-02-20 Tonga Chronicle -# How Tonga became 'The Land where Time Begins': -# http://www.tongatapu.net.to/tonga/homeland/timebegins.htm -# -# Until 1941 Tonga maintained a standard time 50 minutes ahead of NZST -# 12 hours and 20 minutes ahead of GMT. When New Zealand adjusted its -# standard time in 1940s, Tonga had the choice of subtracting from its -# local time to come on the same standard time as New Zealand or of -# advancing its time to maintain the differential of 13° -# (approximately 50 minutes ahead of New Zealand time). -# -# Because His Majesty King Tāufaʻāhau Tupou IV, then Crown Prince -# Tungī, preferred to ensure Tonga's title as the land where time -# begins, the Legislative Assembly approved the latter change. -# -# But some of the older, more conservative members from the outer -# islands objected. "If at midnight on Dec. 31, we move ahead 40 -# minutes, as your Royal Highness wishes, what becomes of the 40 -# minutes we have lost?" -# -# The Crown Prince, presented an unanswerable argument: "Remember that -# on the World Day of Prayer, you would be the first people on Earth -# to say your prayers in the morning." - -# From Paul Eggert (2006-03-22): -# Shanks & Pottenger say the transition was on 1968-10-01; go with Mundell. - -# From Eric Ulevik (1999-05-03): -# Tonga's director of tourism, who is also secretary of the National Millennium -# Committee, has a plan to get Tonga back in front. -# He has proposed a one-off move to tropical daylight saving for Tonga from -# October to March, which has won approval in principle from the Tongan -# Government. - -# From Steffen Thorsen (1999-09-09): -# * Tonga will introduce DST in November -# -# I was given this link by John Letts: -# http://news.bbc.co.uk/hi/english/world/asia-pacific/newsid_424000/424764.stm -# -# I have not been able to find exact dates for the transition in November -# yet. By reading this article it seems like Fiji will be 14 hours ahead -# of UTC as well, but as far as I know Fiji will only be 13 hours ahead -# (12 + 1 hour DST). - -# From Arthur David Olson (1999-09-20): -# According to : -# "Daylight Savings Time will take effect on Oct. 2 through April 15, 2000 -# and annually thereafter from the first Saturday in October through the -# third Saturday of April. Under the system approved by Privy Council on -# Sept. 10, clocks must be turned ahead one hour on the opening day and -# set back an hour on the closing date." -# Alas, no indication of the time of day. - -# From Rives McDow (1999-10-06): -# Tonga started its Daylight Saving on Saturday morning October 2nd at 0200am. -# Daylight Saving ends on April 16 at 0300am which is Sunday morning. - -# From Steffen Thorsen (2000-10-31): -# Back in March I found a notice on the website http://www.tongaonline.com -# that Tonga changed back to standard time one month early, on March 19 -# instead of the original reported date April 16. Unfortunately, the article -# is no longer available on the site, and I did not make a copy of the -# text, and I have forgotten to report it here. -# (Original URL was ) - -# From Rives McDow (2000-12-01): -# Tonga is observing DST as of 2000-11-04 and will stop on 2001-01-27. - -# From Sione Moala-Mafi (2001-09-20) via Rives McDow: -# At 2:00am on the first Sunday of November, the standard time in the Kingdom -# shall be moved forward by one hour to 3:00am. At 2:00am on the last Sunday -# of January the standard time in the Kingdom shall be moved backward by one -# hour to 1:00am. - -# From Pulu ʻAnau (2002-11-05): -# The law was for 3 years, supposedly to get renewed. It wasn't. - -# From Pulu ʻAnau (2016-10-27): -# http://mic.gov.to/news-today/press-releases/6375-daylight-saving-set-to-run-from-6-november-2016-to-15-january-2017 -# Cannot find anyone who knows the rules, has seen the duration or has seen -# the cabinet decision, but it appears we are following Fiji's rule set. -# -# From Tim Parenti (2016-10-26): -# Assume Tonga will observe DST from the first Sunday in November at 02:00 -# through the third Sunday in January at 03:00, like Fiji, for now. - -# From David Wade (2017-10-18): -# In August government was disolved by the King. The current prime minister -# continued in office in care taker mode. It is easy to see that few -# decisions will be made until elections 16th November. -# -# From Paul Eggert (2017-10-18): -# For now, guess that DST is discontinued. That's what the IATA is guessing. - - -# Wake - -# From Vernice Anderson, Personal Secretary to Philip Jessup, -# US Ambassador At Large (oral history interview, 1971-02-02): -# -# Saturday, the 14th [of October, 1950] - ... The time was all the -# more confusing at that point, because we had crossed the -# International Date Line, thus getting two Sundays. Furthermore, we -# discovered that Wake Island had two hours of daylight saving time -# making calculation of time in Washington difficult if not almost -# impossible. -# -# https://www.trumanlibrary.org/oralhist/andrsonv.htm - -# From Paul Eggert (2003-03-23): -# We have no other report of DST in Wake Island, so omit this info for now. - -############################################################################### - -# The International Date Line - -# From Gwillim Law (2000-01-03): -# -# The International Date Line is not defined by any international standard, -# convention, or treaty. Mapmakers are free to draw it as they please. -# Reputable mapmakers will simply ensure that every point of land appears on -# the correct side of the IDL, according to the date legally observed there. -# -# When Kiribati adopted a uniform date in 1995, thereby moving the Phoenix and -# Line Islands to the west side of the IDL (or, if you prefer, moving the IDL -# to the east side of the Phoenix and Line Islands), I suppose that most -# mapmakers redrew the IDL following the boundary of Kiribati. Even that line -# has a rather arbitrary nature. The straight-line boundaries between Pacific -# island nations that are shown on many maps are based on an international -# convention, but are not legally binding national borders.... The date is -# governed by the IDL; therefore, even on the high seas, there may be some -# places as late as fourteen hours later than UTC. And, since the IDL is not -# an international standard, there are some places on the high seas where the -# correct date is ambiguous. - -# From Wikipedia (2005-08-31): -# Before 1920, all ships kept local apparent time on the high seas by setting -# their clocks at night or at the morning sight so that, given the ship's -# speed and direction, it would be 12 o'clock when the Sun crossed the ship's -# meridian (12 o'clock = local apparent noon). During 1917, at the -# Anglo-French Conference on Time-keeping at Sea, it was recommended that all -# ships, both military and civilian, should adopt hourly standard time zones -# on the high seas. Whenever a ship was within the territorial waters of any -# nation it would use that nation's standard time. The captain was permitted -# to change his ship's clocks at a time of his choice following his ship's -# entry into another zone time - he often chose midnight. These zones were -# adopted by all major fleets between 1920 and 1925 but not by many -# independent merchant ships until World War II. - -# From Paul Eggert, using references suggested by Oscar van Vlijmen -# (2005-03-20): -# -# The American Practical Navigator (2002) -# http://pollux.nss.nima.mil/pubs/pubs_j_apn_sections.html?rid=187 -# talks only about the 180-degree meridian with respect to ships in -# international waters; it ignores the international date line. diff --git a/date_time/backward b/date_time/backward deleted file mode 100644 index 2141f0d579..0000000000 --- a/date_time/backward +++ /dev/null @@ -1,128 +0,0 @@ -# This file is in the public domain, so clarified as of -# 2009-05-17 by Arthur David Olson. - -# This file provides links between current names for time zones -# and their old names. Many names changed in late 1993. - -# Link TARGET LINK-NAME -Link Africa/Nairobi Africa/Asmera -Link Africa/Abidjan Africa/Timbuktu -Link America/Argentina/Catamarca America/Argentina/ComodRivadavia -Link America/Adak America/Atka -Link America/Argentina/Buenos_Aires America/Buenos_Aires -Link America/Argentina/Catamarca America/Catamarca -Link America/Atikokan America/Coral_Harbour -Link America/Argentina/Cordoba America/Cordoba -Link America/Tijuana America/Ensenada -Link America/Indiana/Indianapolis America/Fort_Wayne -Link America/Indiana/Indianapolis America/Indianapolis -Link America/Argentina/Jujuy America/Jujuy -Link America/Indiana/Knox America/Knox_IN -Link America/Kentucky/Louisville America/Louisville -Link America/Argentina/Mendoza America/Mendoza -Link America/Toronto America/Montreal -Link America/Rio_Branco America/Porto_Acre -Link America/Argentina/Cordoba America/Rosario -Link America/Tijuana America/Santa_Isabel -Link America/Denver America/Shiprock -Link America/Port_of_Spain America/Virgin -Link Pacific/Auckland Antarctica/South_Pole -Link Asia/Ashgabat Asia/Ashkhabad -Link Asia/Kolkata Asia/Calcutta -Link Asia/Shanghai Asia/Chongqing -Link Asia/Shanghai Asia/Chungking -Link Asia/Dhaka Asia/Dacca -Link Asia/Shanghai Asia/Harbin -Link Asia/Urumqi Asia/Kashgar -Link Asia/Kathmandu Asia/Katmandu -Link Asia/Macau Asia/Macao -Link Asia/Yangon Asia/Rangoon -Link Asia/Ho_Chi_Minh Asia/Saigon -Link Asia/Jerusalem Asia/Tel_Aviv -Link Asia/Thimphu Asia/Thimbu -Link Asia/Makassar Asia/Ujung_Pandang -Link Asia/Ulaanbaatar Asia/Ulan_Bator -Link Atlantic/Faroe Atlantic/Faeroe -Link Europe/Oslo Atlantic/Jan_Mayen -Link Australia/Sydney Australia/ACT -Link Australia/Sydney Australia/Canberra -Link Australia/Lord_Howe Australia/LHI -Link Australia/Sydney Australia/NSW -Link Australia/Darwin Australia/North -Link Australia/Brisbane Australia/Queensland -Link Australia/Adelaide Australia/South -Link Australia/Hobart Australia/Tasmania -Link Australia/Melbourne Australia/Victoria -Link Australia/Perth Australia/West -Link Australia/Broken_Hill Australia/Yancowinna -Link America/Rio_Branco Brazil/Acre -Link America/Noronha Brazil/DeNoronha -Link America/Sao_Paulo Brazil/East -Link America/Manaus Brazil/West -Link America/Halifax Canada/Atlantic -Link America/Winnipeg Canada/Central -# This line is commented out, as the name exceeded the 14-character limit -# and was an unused misnomer. -#Link America/Regina Canada/East-Saskatchewan -Link America/Toronto Canada/Eastern -Link America/Edmonton Canada/Mountain -Link America/St_Johns Canada/Newfoundland -Link America/Vancouver Canada/Pacific -Link America/Regina Canada/Saskatchewan -Link America/Whitehorse Canada/Yukon -Link America/Santiago Chile/Continental -Link Pacific/Easter Chile/EasterIsland -Link America/Havana Cuba -Link Africa/Cairo Egypt -Link Europe/Dublin Eire -Link Europe/London Europe/Belfast -Link Europe/Chisinau Europe/Tiraspol -Link Europe/London GB -Link Europe/London GB-Eire -Link Etc/GMT GMT+0 -Link Etc/GMT GMT-0 -Link Etc/GMT GMT0 -Link Etc/GMT Greenwich -Link Asia/Hong_Kong Hongkong -Link Atlantic/Reykjavik Iceland -Link Asia/Tehran Iran -Link Asia/Jerusalem Israel -Link America/Jamaica Jamaica -Link Asia/Tokyo Japan -Link Pacific/Kwajalein Kwajalein -Link Africa/Tripoli Libya -Link America/Tijuana Mexico/BajaNorte -Link America/Mazatlan Mexico/BajaSur -Link America/Mexico_City Mexico/General -Link Pacific/Auckland NZ -Link Pacific/Chatham NZ-CHAT -Link America/Denver Navajo -Link Asia/Shanghai PRC -Link Pacific/Honolulu Pacific/Johnston -Link Pacific/Pohnpei Pacific/Ponape -Link Pacific/Pago_Pago Pacific/Samoa -Link Pacific/Chuuk Pacific/Truk -Link Pacific/Chuuk Pacific/Yap -Link Europe/Warsaw Poland -Link Europe/Lisbon Portugal -Link Asia/Taipei ROC -Link Asia/Seoul ROK -Link Asia/Singapore Singapore -Link Europe/Istanbul Turkey -Link Etc/UCT UCT -Link America/Anchorage US/Alaska -Link America/Adak US/Aleutian -Link America/Phoenix US/Arizona -Link America/Chicago US/Central -Link America/Indiana/Indianapolis US/East-Indiana -Link America/New_York US/Eastern -Link Pacific/Honolulu US/Hawaii -Link America/Indiana/Knox US/Indiana-Starke -Link America/Detroit US/Michigan -Link America/Denver US/Mountain -Link America/Los_Angeles US/Pacific -Link Pacific/Pago_Pago US/Samoa -Link Etc/UTC UTC -Link Etc/UTC Universal -Link Europe/Moscow W-SU -Link Etc/UTC Zulu diff --git a/date_time/etcetera b/date_time/etcetera deleted file mode 100644 index f5fa4c94b4..0000000000 --- a/date_time/etcetera +++ /dev/null @@ -1,78 +0,0 @@ -# This file is in the public domain, so clarified as of -# 2009-05-17 by Arthur David Olson. - -# These entries are mostly present for historical reasons, so that -# people in areas not otherwise covered by the tz files could "zic -l" -# to a time zone that was right for their area. These days, the -# tz files cover almost all the inhabited world, and the only practical -# need now for the entries that are not on UTC are for ships at sea -# that cannot use POSIX TZ settings. - -# Starting with POSIX 1003.1-2001, the entries below are all -# unnecessary as settings for the TZ environment variable. E.g., -# instead of TZ='Etc/GMT+4' one can use the POSIX setting TZ='<-04>+4'. -# -# Do not use a POSIX TZ setting like TZ='GMT+4', which is four hours -# behind GMT but uses the completely misleading abbreviation "GMT". - -Zone Etc/GMT 0 - GMT -Zone Etc/UTC 0 - UTC -Zone Etc/UCT 0 - UCT - -# The following link uses older naming conventions, -# but it belongs here, not in the file 'backward', -# as functions like gmtime load the "GMT" file to handle leap seconds properly. -# We want this to work even on installations that omit the other older names. -Link Etc/GMT GMT - -Link Etc/UTC Etc/Universal -Link Etc/UTC Etc/Zulu - -Link Etc/GMT Etc/Greenwich -Link Etc/GMT Etc/GMT-0 -Link Etc/GMT Etc/GMT+0 -Link Etc/GMT Etc/GMT0 - -# Be consistent with POSIX TZ settings in the Zone names, -# even though this is the opposite of what many people expect. -# POSIX has positive signs west of Greenwich, but many people expect -# positive signs east of Greenwich. For example, TZ='Etc/GMT+4' uses -# the abbreviation "-04" and corresponds to 4 hours behind UT -# (i.e. west of Greenwich) even though many people would expect it to -# mean 4 hours ahead of UT (i.e. east of Greenwich). - -# Earlier incarnations of this package were not POSIX-compliant, -# and had lines such as -# Zone GMT-12 -12 - GMT-1200 -# We did not want things to change quietly if someone accustomed to the old -# way does a -# zic -l GMT-12 -# so we moved the names into the Etc subdirectory. -# Also, the time zone abbreviations are now compatible with %z. - -Zone Etc/GMT-14 14 - +14 -Zone Etc/GMT-13 13 - +13 -Zone Etc/GMT-12 12 - +12 -Zone Etc/GMT-11 11 - +11 -Zone Etc/GMT-10 10 - +10 -Zone Etc/GMT-9 9 - +09 -Zone Etc/GMT-8 8 - +08 -Zone Etc/GMT-7 7 - +07 -Zone Etc/GMT-6 6 - +06 -Zone Etc/GMT-5 5 - +05 -Zone Etc/GMT-4 4 - +04 -Zone Etc/GMT-3 3 - +03 -Zone Etc/GMT-2 2 - +02 -Zone Etc/GMT-1 1 - +01 -Zone Etc/GMT+1 -1 - -01 -Zone Etc/GMT+2 -2 - -02 -Zone Etc/GMT+3 -3 - -03 -Zone Etc/GMT+4 -4 - -04 -Zone Etc/GMT+5 -5 - -05 -Zone Etc/GMT+6 -6 - -06 -Zone Etc/GMT+7 -7 - -07 -Zone Etc/GMT+8 -8 - -08 -Zone Etc/GMT+9 -9 - -09 -Zone Etc/GMT+10 -10 - -10 -Zone Etc/GMT+11 -11 - -11 -Zone Etc/GMT+12 -12 - -12 diff --git a/date_time/europe b/date_time/europe deleted file mode 100644 index 6994ed807e..0000000000 --- a/date_time/europe +++ /dev/null @@ -1,3938 +0,0 @@ -# This file is in the public domain, so clarified as of -# 2009-05-17 by Arthur David Olson. - -# This file is by no means authoritative; if you think you know better, -# go ahead and edit the file (and please send any changes to -# tz@iana.org for general use in the future). For more, please see -# the file CONTRIBUTING in the tz distribution. - -# From Paul Eggert (2017-02-10): -# -# Unless otherwise specified, the source for data through 1990 is: -# Thomas G. Shanks and Rique Pottenger, The International Atlas (6th edition), -# San Diego: ACS Publications, Inc. (2003). -# Unfortunately this book contains many errors and cites no sources. -# -# Many years ago Gwillim Law wrote that a good source -# for time zone data was the International Air Transport -# Association's Standard Schedules Information Manual (IATA SSIM), -# published semiannually. Law sent in several helpful summaries -# of the IATA's data after 1990. Except where otherwise noted, -# IATA SSIM is the source for entries after 1990. -# -# A reliable and entertaining source about time zones is -# Derek Howse, Greenwich time and longitude, Philip Wilson Publishers (1997). -# -# Except where otherwise noted, Shanks & Pottenger is the source for -# entries through 1991, and IATA SSIM is the source for entries afterwards. -# -# Other sources occasionally used include: -# -# Edward W. Whitman, World Time Differences, -# Whitman Publishing Co, 2 Niagara Av, Ealing, London (undated), -# which I found in the UCLA library. -# -# William Willett, The Waste of Daylight, 19th edition -# -# [PDF] (1914-03) -# -# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94 -# . He writes: -# "It is requested that corrections and additions to these tables -# may be sent to Mr. John Milne, Royal Geographical Society, -# Savile Row, London." Nowadays please email them to tz@iana.org. -# -# Byalokoz EL. New Counting of Time in Russia since July 1, 1919. -# This Russian-language source was consulted by Vladimir Karpinsky; see -# https://mm.icann.org/pipermail/tz/2014-August/021320.html -# The full Russian citation is: -# Бялокоз, Евгений Людвигович. Новый счет времени в течении суток -# введенный декретом Совета народных комиссаров для всей России с 1-го -# июля 1919 г. / Изд. 2-е Междуведомственной комиссии. - Петроград: -# Десятая гос. тип., 1919. -# http://resolver.gpntb.ru/purl?docushare/dsweb/Get/Resource-2011/Byalokoz__E.L.__Novyy__schet__vremeni__v__techenie__sutok__izd__2(1).pdf -# -# Brazil's Divisão Serviço da Hora (DSHO), -# History of Summer Time -# -# (1998-09-21, in Portuguese) -# -# I invented the abbreviations marked '*' in the following table; -# the rest are variants of the "xMT" pattern for a city's mean time, -# or are from other sources. Corrections are welcome! -# std dst 2dst -# LMT Local Mean Time -# -4:00 AST ADT Atlantic -# 0:00 GMT BST BDST Greenwich, British Summer -# 0:00 GMT IST Greenwich, Irish Summer -# 0:00 WET WEST WEMT Western Europe -# 0:19:32.13 AMT* NST* Amsterdam, Netherlands Summer (1835-1937) -# 1:00 BST British Standard (1968-1971) -# 1:00 IST GMT Irish Standard (1968-) with winter DST -# 1:00 CET CEST CEMT Central Europe -# 1:00:14 SET Swedish (1879-1899) -# 1:36:34 RMT* LST* Riga, Latvian Summer (1880-1926)* -# 2:00 EET EEST Eastern Europe -# 3:00 MSK MSD MDST* Moscow - -# From Peter Ilieve (1994-12-04), re EEC/EC/EU members: -# The original six: Belgium, France, (West) Germany, Italy, -# Luxembourg, the Netherlands. -# Plus, from 1 Jan 73: Denmark, Ireland, United Kingdom. -# Plus, from 1 Jan 81: Greece. -# Plus, from 1 Jan 86: Spain, Portugal. -# Plus, from 1 Jan 95: Austria, Finland, Sweden. (Norway negotiated terms for -# entry but in a referendum on 28 Nov 94 the people voted No by 52.2% to 47.8% -# on a turnout of 88.6%. This was almost the same result as Norway's previous -# referendum in 1972, they are the only country to have said No twice. -# Referendums in the other three countries voted Yes.) -# ... -# Estonia ... uses EU dates but not at 01:00 GMT, they use midnight GMT. -# I don't think they know yet what they will do from 1996 onwards. -# ... -# There shouldn't be any [current members who are not using EU rules]. -# A Directive has the force of law, member states are obliged to enact -# national law to implement it. The only contentious issue was the -# different end date for the UK and Ireland, and this was always allowed -# in the Directive. - - -############################################################################### - -# Britain (United Kingdom) and Ireland (Eire) - -# From Peter Ilieve (1994-07-06): -# -# On 17 Jan 1994 the Independent, a UK quality newspaper, had a piece about -# historical vistas along the Thames in west London. There was a photo -# and a sketch map showing some of the sightlines involved. One paragraph -# of the text said: -# -# 'An old stone obelisk marking a forgotten terrestrial meridian stands -# beside the river at Kew. In the 18th century, before time and longitude -# was standardised by the Royal Observatory in Greenwich, scholars observed -# this stone and the movement of stars from Kew Observatory nearby. They -# made their calculations and set the time for the Horse Guards and Parliament, -# but now the stone is obscured by scrubwood and can only be seen by walking -# along the towpath within a few yards of it.' -# -# I have a one inch to one mile map of London and my estimate of the stone's -# position is 51° 28' 30" N, 0° 18' 45" W. The longitude should -# be within about ±2". The Ordnance Survey grid reference is TQ172761. -# -# [This yields GMTOFF = -0:01:15 for London LMT in the 18th century.] - -# From Paul Eggert (1993-11-18): -# -# Howse writes that Britain was the first country to use standard time. -# The railways cared most about the inconsistencies of local mean time, -# and it was they who forced a uniform time on the country. -# The original idea was credited to Dr. William Hyde Wollaston (1766-1828) -# and was popularized by Abraham Follett Osler (1808-1903). -# The first railway to adopt London time was the Great Western Railway -# in November 1840; other railways followed suit, and by 1847 most -# (though not all) railways used London time. On 1847-09-22 the -# Railway Clearing House, an industry standards body, recommended that GMT be -# adopted at all stations as soon as the General Post Office permitted it. -# The transition occurred on 12-01 for the L&NW, the Caledonian, -# and presumably other railways; the January 1848 Bradshaw's lists many -# railways as using GMT. By 1855 the vast majority of public -# clocks in Britain were set to GMT (though some, like the great clock -# on Tom Tower at Christ Church, Oxford, were fitted with two minute hands, -# one for local time and one for GMT). The last major holdout was the legal -# system, which stubbornly stuck to local time for many years, leading -# to oddities like polls opening at 08:13 and closing at 16:13. -# The legal system finally switched to GMT when the Statutes (Definition -# of Time) Act took effect; it received the Royal Assent on 1880-08-02. -# -# In the tables below, we condense this complicated story into a single -# transition date for London, namely 1847-12-01. We don't know as much -# about Dublin, so we use 1880-08-02, the legal transition time. - -# From Paul Eggert (2014-07-19): -# The ancients had no need for daylight saving, as they kept time -# informally or via hours whose length depended on the time of year. -# Daylight saving time in its modern sense was invented by the -# New Zealand entomologist George Vernon Hudson (1867-1946), -# whose day job as a postal clerk led him to value -# after-hours daylight in which to pursue his research. -# In 1895 he presented a paper to the Wellington Philosophical Society -# that proposed a two-hour daylight-saving shift. See: -# Hudson GV. On seasonal time-adjustment in countries south of lat. 30°. -# Transactions and Proceedings of the New Zealand Institute. 1895;28:734 -# http://rsnz.natlib.govt.nz/volume/rsnz_28/rsnz_28_00_006110.html -# Although some interest was expressed in New Zealand, his proposal -# did not find its way into law and eventually it was almost forgotten. -# -# In England, DST was independently reinvented by William Willett (1857-1915), -# a London builder and member of the Royal Astronomical Society -# who circulated a pamphlet "The Waste of Daylight" (1907) -# that proposed advancing clocks 20 minutes on each of four Sundays in April, -# and retarding them by the same amount on four Sundays in September. -# A bill was drafted in 1909 and introduced in Parliament several times, -# but it met with ridicule and opposition, especially from farming interests. -# Later editions of the pamphlet proposed one-hour summer time, and -# it was eventually adopted as a wartime measure in 1916. -# See: Summer Time Arrives Early, The Times (2000-05-18). -# A monument to Willett was unveiled on 1927-05-21, in an open space in -# a 45-acre wood near Chislehurst, Kent that was purchased by popular -# subscription and open to the public. On the south face of the monolith, -# designed by G. W. Miller, is the William Willett Memorial Sundial, -# which is permanently set to Summer Time. - -# From Winston Churchill (1934-04-28): -# It is one of the paradoxes of history that we should owe the boon of -# summer time, which gives every year to the people of this country -# between 160 and 170 hours more daylight leisure, to a war which -# plunged Europe into darkness for four years, and shook the -# foundations of civilization throughout the world. -# -- "A Silent Toast to William Willett", Pictorial Weekly; -# republished in Finest Hour (Spring 2002) 1(114):26 -# https://www.winstonchurchill.org/publications/finest-hour/finest-hour-114/a-silent-toast-to-william-willett-by-winston-s-churchill - -# From Paul Eggert (2015-08-08): -# The OED Supplement says that the English originally said "Daylight Saving" -# when they were debating the adoption of DST in 1908; but by 1916 this -# term appears only in quotes taken from DST's opponents, whereas the -# proponents (who eventually won the argument) are quoted as using "Summer". -# The term "Summer Time" was introduced by Herbert Samuel, Home Secretary; see: -# Viscount Samuel. Leisure in a Democracy. Cambridge University Press -# ISBN 978-1-107-49471-8 (1949, reissued 2015), p 8. - -# From Arthur David Olson (1989-01-19): -# A source at the British Information Office in New York avers that it's -# known as "British" Summer Time in all parts of the United Kingdom. - -# Date: 4 Jan 89 08:57:25 GMT (Wed) -# From: Jonathan Leffler -# [British Summer Time] is fixed annually by Act of Parliament. -# If you can predict what Parliament will do, you should be in -# politics making a fortune, not computing. - -# From Chris Carrier (1996-06-14): -# I remember reading in various wartime issues of the London Times the -# acronym BDST for British Double Summer Time. Look for the published -# time of sunrise and sunset in The Times, when BDST was in effect, and -# if you find a zone reference it will say, "All times B.D.S.T." - -# From Joseph S. Myers (1999-09-02): -# ... some military cables (WO 219/4100 - this is a copy from the -# main SHAEF archives held in the US National Archives, SHAEF/5252/8/516) -# agree that the usage is BDST (this appears in a message dated 17 Feb 1945). - -# From Joseph S. Myers (2000-10-03): -# On 18th April 1941, Sir Stephen Tallents of the BBC wrote to Sir -# Alexander Maxwell of the Home Office asking whether there was any -# official designation; the reply of the 21st was that there wasn't -# but he couldn't think of anything better than the "Double British -# Summer Time" that the BBC had been using informally. -# https://www.polyomino.org.uk/british-time/bbc-19410418.png -# https://www.polyomino.org.uk/british-time/ho-19410421.png - -# From Sir Alexander Maxwell in the above-mentioned letter (1941-04-21): -# [N]o official designation has as far as I know been adopted for the time -# which is to be introduced in May.... -# I cannot think of anything better than "Double British Summer Time" -# which could not be said to run counter to any official description. - -# From Paul Eggert (2000-10-02): -# Howse writes (p 157) 'DBST' too, but 'BDST' seems to have been common -# and follows the more usual convention of putting the location name first, -# so we use 'BDST'. - -# Peter Ilieve (1998-04-19) described at length -# the history of summer time legislation in the United Kingdom. -# Since 1998 Joseph S. Myers has been updating -# and extending this list, which can be found in -# https://www.polyomino.org.uk/british-time/ - -# From Joseph S. Myers (1998-01-06): -# -# The legal time in the UK outside of summer time is definitely GMT, not UTC; -# see Lord Tanlaw's speech -# https://www.publications.parliament.uk/pa/ld199798/ldhansrd/vo970611/text/70611-10.htm#70611-10_head0 -# (Lords Hansard 11 June 1997 columns 964 to 976). - -# From Paul Eggert (2006-03-22): -# -# For lack of other data, follow Shanks & Pottenger for Eire in 1940-1948. -# -# Given Ilieve and Myers's data, the following claims by Shanks & Pottenger -# are incorrect: -# * Wales did not switch from GMT to daylight saving time until -# 1921 Apr 3, when they began to conform with the rest of Great Britain. -# Actually, Wales was identical after 1880. -# * Eire had two transitions on 1916 Oct 1. -# It actually just had one transition. -# * Northern Ireland used single daylight saving time throughout WW II. -# Actually, it conformed to Britain. -# * GB-Eire changed standard time to 1 hour ahead of GMT on 1968-02-18. -# Actually, that date saw the usual switch to summer time. -# Standard time was not changed until 1968-10-27 (the clocks didn't change). -# -# Here is another incorrect claim by Shanks & Pottenger: -# * Jersey, Guernsey, and the Isle of Man did not switch from GMT -# to daylight saving time until 1921 Apr 3, when they began to -# conform with Great Britain. -# S.R.&O. 1916, No. 382 and HO 45/10811/312364 (quoted above) say otherwise. -# -# The following claim by Shanks & Pottenger is possible though doubtful; -# we'll ignore it for now. -# * Dublin's 1971-10-31 switch was at 02:00, even though London's was 03:00. - -# From Paul Eggert (2017-12-04): -# -# Dunsink Observatory (8 km NW of Dublin's center) was to Dublin as -# Greenwich was to London. For example: -# -# "Timeball on the ballast office is down. Dunsink time." -# -- James Joyce, Ulysses -# -# The abbreviation DMT stood for "Dublin Mean Time" or "Dunsink Mean Time"; -# this being Ireland, opinions differed. -# -# Whitman says Dublin/Dunsink Mean Time was UT-00:25:21, which agrees -# with measurements of recent visitors to the Meridian Room of Dunsink -# Observatory; see Malone D. Dunsink and timekeeping. 2016-01-24. -# . Malone -# writes that the Nautical Almanac listed UT-00:25:22 until 1896, when -# it moved to UT-00:25:21.1 (I confirmed that the 1893 edition used -# the former and the 1896 edition used the latter). Evidently the -# news of this change propagated slowly, as Milne 1899 still lists -# UT-00:25:22 and cites the International Telegraph Bureau. As it is -# not clear that there was any practical significance to the change -# from UT-00:25:22 to UT-00:25:21.1 in civil timekeeping, omit this -# transition for now and just use the latter value, omitting its -# fraction since our format cannot represent fractions. - -# "Countess Markievicz ... claimed that the [1916] abolition of Dublin Mean Time -# was among various actions undertaken by the 'English' government that -# would 'put the whole country into the SF (Sinn Féin) camp'. She claimed -# Irish 'public feeling (was) outraged by forcing of English time on us'." -# -- Parsons M. Dublin lost its time zone - and 25 minutes - after 1916 Rising. -# Irish Times 2014-10-27. -# https://www.irishtimes.com/news/politics/dublin-lost-its-time-zone-and-25-minutes-after-1916-rising-1.1977411 - -# From Joseph S. Myers (2005-01-26): -# Irish laws are available online at . -# These include various relating to legal time, for example: -# -# ZZA13Y1923.html ZZA12Y1924.html ZZA8Y1925.html ZZSIV20PG1267.html -# -# ZZSI71Y1947.html ZZSI128Y1948.html ZZSI23Y1949.html ZZSI41Y1950.html -# ZZSI27Y1951.html ZZSI73Y1952.html -# -# ZZSI11Y1961.html ZZSI232Y1961.html ZZSI182Y1962.html -# ZZSI167Y1963.html ZZSI257Y1964.html ZZSI198Y1967.html -# ZZA23Y1968.html ZZA17Y1971.html -# -# ZZSI67Y1981.html ZZSI212Y1982.html ZZSI45Y1986.html -# ZZSI264Y1988.html ZZSI52Y1990.html ZZSI371Y1992.html -# ZZSI395Y1994.html ZZSI484Y1997.html ZZSI506Y2001.html -# -# [These are all relative to the root, e.g., the first is -# .] -# -# (These are those I found, but there could be more. In any case these -# should allow various updates to the comments in the europe file to cover -# the laws applicable in Ireland.) -# -# (Note that the time in the Republic of Ireland since 1968 has been defined -# in terms of standard time being GMT+1 with a period of winter time when it -# is GMT, rather than standard time being GMT with a period of summer time -# being GMT+1.) - -# From Paul Eggert (1999-03-28): -# Clive Feather (, 1997-03-31) -# reports that Folkestone (Cheriton) Shuttle Terminal uses Concession Time -# (CT), equivalent to French civil time. -# Julian Hill (, 1998-09-30) reports that -# trains between Dollands Moor (the freight facility next door) -# and Frethun run in CT. -# My admittedly uninformed guess is that the terminal has two authorities, -# the French concession operators and the British civil authorities, -# and that the time depends on who you're talking to. -# If, say, the British police were called to the station for some reason, -# I would expect the official police report to use GMT/BST and not CET/CEST. -# This is a borderline case, but for now let's stick to GMT/BST. - -# From an anonymous contributor (1996-06-02): -# The law governing time in Ireland is under Statutory Instrument SI 395/94, -# which gives force to European Union 7th Council Directive No. 94/21/EC. -# Under this directive, the Minister for Justice in Ireland makes appropriate -# regulations. I spoke this morning with the Secretary of the Department of -# Justice (tel +353 1 678 9711) who confirmed to me that the correct name is -# "Irish Summer Time", abbreviated to "IST". -# -# From Paul Eggert (2017-12-07): -# The 1996 anonymous contributor's goal was to determine the correct -# abbreviation for summer time in Dublin and so the contributor -# focused on the "IST", not on the "Irish Summer Time". Though the -# "IST" was correct, the "Irish Summer Time" appears to have been an -# error, as Ireland's Standard Time (Amendment) Act, 1971 states that -# standard time in Ireland remains at UT +01 and is observed in -# summer, and that Greenwich mean time is observed in winter. (Thanks -# to Derick Rethans for pointing out the error.) That is, when -# Ireland amended the 1968 act that established UT +01 as Irish -# Standard Time, it left standard time unchanged and established GMT -# as a negative daylight saving time in winter. So, in this database -# IST stands for Irish Summer Time for timestamps before 1968, and for -# Irish Standard Time after that. See: -# http://www.irishstatutebook.ie/eli/1971/act/17/enacted/en/print - -# Michael Deckers (2017-06-01) gave the following URLs for Ireland's -# Summer Time Act, 1925 and Summer Time Orders, 1926 and 1947: -# http://www.irishstatutebook.ie/eli/1925/act/8/enacted/en/print -# http://www.irishstatutebook.ie/eli/1926/sro/919/made/en/print -# http://www.irishstatutebook.ie/eli/1947/sro/71/made/en/print - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -# Summer Time Act, 1916 -Rule GB-Eire 1916 only - May 21 2:00s 1:00 BST -Rule GB-Eire 1916 only - Oct 1 2:00s 0 GMT -# S.R.&O. 1917, No. 358 -Rule GB-Eire 1917 only - Apr 8 2:00s 1:00 BST -Rule GB-Eire 1917 only - Sep 17 2:00s 0 GMT -# S.R.&O. 1918, No. 274 -Rule GB-Eire 1918 only - Mar 24 2:00s 1:00 BST -Rule GB-Eire 1918 only - Sep 30 2:00s 0 GMT -# S.R.&O. 1919, No. 297 -Rule GB-Eire 1919 only - Mar 30 2:00s 1:00 BST -Rule GB-Eire 1919 only - Sep 29 2:00s 0 GMT -# S.R.&O. 1920, No. 458 -Rule GB-Eire 1920 only - Mar 28 2:00s 1:00 BST -# S.R.&O. 1920, No. 1844 -Rule GB-Eire 1920 only - Oct 25 2:00s 0 GMT -# S.R.&O. 1921, No. 363 -Rule GB-Eire 1921 only - Apr 3 2:00s 1:00 BST -Rule GB-Eire 1921 only - Oct 3 2:00s 0 GMT -# S.R.&O. 1922, No. 264 -Rule GB-Eire 1922 only - Mar 26 2:00s 1:00 BST -Rule GB-Eire 1922 only - Oct 8 2:00s 0 GMT -# The Summer Time Act, 1922 -Rule GB-Eire 1923 only - Apr Sun>=16 2:00s 1:00 BST -Rule GB-Eire 1923 1924 - Sep Sun>=16 2:00s 0 GMT -Rule GB-Eire 1924 only - Apr Sun>=9 2:00s 1:00 BST -Rule GB-Eire 1925 1926 - Apr Sun>=16 2:00s 1:00 BST -# The Summer Time Act, 1925 -Rule GB-Eire 1925 1938 - Oct Sun>=2 2:00s 0 GMT -Rule GB-Eire 1927 only - Apr Sun>=9 2:00s 1:00 BST -Rule GB-Eire 1928 1929 - Apr Sun>=16 2:00s 1:00 BST -Rule GB-Eire 1930 only - Apr Sun>=9 2:00s 1:00 BST -Rule GB-Eire 1931 1932 - Apr Sun>=16 2:00s 1:00 BST -Rule GB-Eire 1933 only - Apr Sun>=9 2:00s 1:00 BST -Rule GB-Eire 1934 only - Apr Sun>=16 2:00s 1:00 BST -Rule GB-Eire 1935 only - Apr Sun>=9 2:00s 1:00 BST -Rule GB-Eire 1936 1937 - Apr Sun>=16 2:00s 1:00 BST -Rule GB-Eire 1938 only - Apr Sun>=9 2:00s 1:00 BST -Rule GB-Eire 1939 only - Apr Sun>=16 2:00s 1:00 BST -# S.R.&O. 1939, No. 1379 -Rule GB-Eire 1939 only - Nov Sun>=16 2:00s 0 GMT -# S.R.&O. 1940, No. 172 and No. 1883 -Rule GB-Eire 1940 only - Feb Sun>=23 2:00s 1:00 BST -# S.R.&O. 1941, No. 476 -Rule GB-Eire 1941 only - May Sun>=2 1:00s 2:00 BDST -Rule GB-Eire 1941 1943 - Aug Sun>=9 1:00s 1:00 BST -# S.R.&O. 1942, No. 506 -Rule GB-Eire 1942 1944 - Apr Sun>=2 1:00s 2:00 BDST -# S.R.&O. 1944, No. 932 -Rule GB-Eire 1944 only - Sep Sun>=16 1:00s 1:00 BST -# S.R.&O. 1945, No. 312 -Rule GB-Eire 1945 only - Apr Mon>=2 1:00s 2:00 BDST -Rule GB-Eire 1945 only - Jul Sun>=9 1:00s 1:00 BST -# S.R.&O. 1945, No. 1208 -Rule GB-Eire 1945 1946 - Oct Sun>=2 2:00s 0 GMT -Rule GB-Eire 1946 only - Apr Sun>=9 2:00s 1:00 BST -# The Summer Time Act, 1947 -Rule GB-Eire 1947 only - Mar 16 2:00s 1:00 BST -Rule GB-Eire 1947 only - Apr 13 1:00s 2:00 BDST -Rule GB-Eire 1947 only - Aug 10 1:00s 1:00 BST -Rule GB-Eire 1947 only - Nov 2 2:00s 0 GMT -# Summer Time Order, 1948 (S.I. 1948/495) -Rule GB-Eire 1948 only - Mar 14 2:00s 1:00 BST -Rule GB-Eire 1948 only - Oct 31 2:00s 0 GMT -# Summer Time Order, 1949 (S.I. 1949/373) -Rule GB-Eire 1949 only - Apr 3 2:00s 1:00 BST -Rule GB-Eire 1949 only - Oct 30 2:00s 0 GMT -# Summer Time Order, 1950 (S.I. 1950/518) -# Summer Time Order, 1951 (S.I. 1951/430) -# Summer Time Order, 1952 (S.I. 1952/451) -Rule GB-Eire 1950 1952 - Apr Sun>=14 2:00s 1:00 BST -Rule GB-Eire 1950 1952 - Oct Sun>=21 2:00s 0 GMT -# revert to the rules of the Summer Time Act, 1925 -Rule GB-Eire 1953 only - Apr Sun>=16 2:00s 1:00 BST -Rule GB-Eire 1953 1960 - Oct Sun>=2 2:00s 0 GMT -Rule GB-Eire 1954 only - Apr Sun>=9 2:00s 1:00 BST -Rule GB-Eire 1955 1956 - Apr Sun>=16 2:00s 1:00 BST -Rule GB-Eire 1957 only - Apr Sun>=9 2:00s 1:00 BST -Rule GB-Eire 1958 1959 - Apr Sun>=16 2:00s 1:00 BST -Rule GB-Eire 1960 only - Apr Sun>=9 2:00s 1:00 BST -# Summer Time Order, 1961 (S.I. 1961/71) -# Summer Time (1962) Order, 1961 (S.I. 1961/2465) -# Summer Time Order, 1963 (S.I. 1963/81) -Rule GB-Eire 1961 1963 - Mar lastSun 2:00s 1:00 BST -Rule GB-Eire 1961 1968 - Oct Sun>=23 2:00s 0 GMT -# Summer Time (1964) Order, 1963 (S.I. 1963/2101) -# Summer Time Order, 1964 (S.I. 1964/1201) -# Summer Time Order, 1967 (S.I. 1967/1148) -Rule GB-Eire 1964 1967 - Mar Sun>=19 2:00s 1:00 BST -# Summer Time Order, 1968 (S.I. 1968/117) -Rule GB-Eire 1968 only - Feb 18 2:00s 1:00 BST -# The British Standard Time Act, 1968 -# (no summer time) -# The Summer Time Act, 1972 -Rule GB-Eire 1972 1980 - Mar Sun>=16 2:00s 1:00 BST -Rule GB-Eire 1972 1980 - Oct Sun>=23 2:00s 0 GMT -# Summer Time Order, 1980 (S.I. 1980/1089) -# Summer Time Order, 1982 (S.I. 1982/1673) -# Summer Time Order, 1986 (S.I. 1986/223) -# Summer Time Order, 1988 (S.I. 1988/931) -Rule GB-Eire 1981 1995 - Mar lastSun 1:00u 1:00 BST -Rule GB-Eire 1981 1989 - Oct Sun>=23 1:00u 0 GMT -# Summer Time Order, 1989 (S.I. 1989/985) -# Summer Time Order, 1992 (S.I. 1992/1729) -# Summer Time Order 1994 (S.I. 1994/2798) -Rule GB-Eire 1990 1995 - Oct Sun>=22 1:00u 0 GMT -# Summer Time Order 1997 (S.I. 1997/2982) -# See EU for rules starting in 1996. -# -# Use Europe/London for Jersey, Guernsey, and the Isle of Man. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/London -0:01:15 - LMT 1847 Dec 1 0:00s - 0:00 GB-Eire %s 1968 Oct 27 - 1:00 - BST 1971 Oct 31 2:00u - 0:00 GB-Eire %s 1996 - 0:00 EU GMT/BST -Link Europe/London Europe/Jersey -Link Europe/London Europe/Guernsey -Link Europe/London Europe/Isle_of_Man - -# From Paul Eggert (2018-02-15): -# In January 2018 we discovered that the negative SAVE values in the -# Eire rules cause problems with tests for ICU: -# https://mm.icann.org/pipermail/tz/2018-January/025825.html -# and with tests for OpenJDK: -# https://mm.icann.org/pipermail/tz/2018-January/025822.html -# -# To work around this problem, the build procedure can translate the -# following data into two forms, one with negative SAVE values and the -# other form with a traditional approximation for Irish time stamps -# after 1971-10-31 02:00 UTC; although this approximation has tm_isdst -# flags that are reversed, its UTC offsets are correct and this often -# suffices. This source file currently uses only nonnegative SAVE -# values, but this is intended to change and downstream code should -# not rely on it. -# -# The following is like GB-Eire and EU, except with standard time in -# summer and negative daylight saving time in winter. It is for when -# negative SAVE values are used. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Eire 1971 only - Oct 31 2:00u -1:00 - -Rule Eire 1972 1980 - Mar Sun>=16 2:00u 0 - -Rule Eire 1972 1980 - Oct Sun>=23 2:00u -1:00 - -Rule Eire 1981 max - Mar lastSun 1:00u 0 - -Rule Eire 1981 1989 - Oct Sun>=23 1:00u -1:00 - -Rule Eire 1990 1995 - Oct Sun>=22 1:00u -1:00 - -Rule Eire 1996 max - Oct lastSun 1:00u -1:00 - - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Dublin -0:25:00 - LMT 1880 Aug 2 - -0:25:21 - DMT 1916 May 21 2:00s - -0:25:21 1:00 IST 1916 Oct 1 2:00s - 0:00 GB-Eire %s 1921 Dec 6 # independence - 0:00 GB-Eire GMT/IST 1940 Feb 25 2:00s - 0:00 1:00 IST 1946 Oct 6 2:00s - 0:00 - GMT 1947 Mar 16 2:00s - 0:00 1:00 IST 1947 Nov 2 2:00s - 0:00 - GMT 1948 Apr 18 2:00s - 0:00 GB-Eire GMT/IST 1968 Oct 27 -# The next line is for when negative SAVE values are used. - 1:00 Eire IST/GMT -# These three lines are for when SAVE values are always nonnegative. -# 1:00 - IST 1971 Oct 31 2:00u -# 0:00 GB-Eire GMT/IST 1996 -# 0:00 EU GMT/IST - - -############################################################################### - -# Europe - -# The following rules are for the European Union and for its -# predecessor organization, the European Communities. -# For brevity they are called "EU rules" elsewhere in this file. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule EU 1977 1980 - Apr Sun>=1 1:00u 1:00 S -Rule EU 1977 only - Sep lastSun 1:00u 0 - -Rule EU 1978 only - Oct 1 1:00u 0 - -Rule EU 1979 1995 - Sep lastSun 1:00u 0 - -Rule EU 1981 max - Mar lastSun 1:00u 1:00 S -Rule EU 1996 max - Oct lastSun 1:00u 0 - -# The most recent directive covers the years starting in 2002. See: -# Directive 2000/84/EC of the European Parliament and of the Council -# of 19 January 2001 on summer-time arrangements. -# http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=CELEX:32000L0084:EN:NOT - -# W-Eur differs from EU only in that W-Eur uses standard time. -Rule W-Eur 1977 1980 - Apr Sun>=1 1:00s 1:00 S -Rule W-Eur 1977 only - Sep lastSun 1:00s 0 - -Rule W-Eur 1978 only - Oct 1 1:00s 0 - -Rule W-Eur 1979 1995 - Sep lastSun 1:00s 0 - -Rule W-Eur 1981 max - Mar lastSun 1:00s 1:00 S -Rule W-Eur 1996 max - Oct lastSun 1:00s 0 - - -# Older C-Eur rules are for convenience in the tables. -# From 1977 on, C-Eur differs from EU only in that C-Eur uses standard time. -Rule C-Eur 1916 only - Apr 30 23:00 1:00 S -Rule C-Eur 1916 only - Oct 1 1:00 0 - -Rule C-Eur 1917 1918 - Apr Mon>=15 2:00s 1:00 S -Rule C-Eur 1917 1918 - Sep Mon>=15 2:00s 0 - -Rule C-Eur 1940 only - Apr 1 2:00s 1:00 S -Rule C-Eur 1942 only - Nov 2 2:00s 0 - -Rule C-Eur 1943 only - Mar 29 2:00s 1:00 S -Rule C-Eur 1943 only - Oct 4 2:00s 0 - -Rule C-Eur 1944 1945 - Apr Mon>=1 2:00s 1:00 S -# Whitman gives 1944 Oct 7; go with Shanks & Pottenger. -Rule C-Eur 1944 only - Oct 2 2:00s 0 - -# From Jesper Nørgaard Welen (2008-07-13): -# -# I found what is probably a typo of 2:00 which should perhaps be 2:00s -# in the C-Eur rule from tz database version 2008d (this part was -# corrected in version 2008d). The circumstantial evidence is simply the -# tz database itself, as seen below: -# -# Zone Europe/Paris 0:09:21 - LMT 1891 Mar 15 0:01 -# 0:00 France WE%sT 1945 Sep 16 3:00 -# -# Zone Europe/Monaco 0:29:32 - LMT 1891 Mar 15 -# 0:00 France WE%sT 1945 Sep 16 3:00 -# -# Zone Europe/Belgrade 1:22:00 - LMT 1884 -# 1:00 1:00 CEST 1945 Sep 16 2:00s -# -# Rule France 1945 only - Sep 16 3:00 0 - -# Rule Belgium 1945 only - Sep 16 2:00s 0 - -# Rule Neth 1945 only - Sep 16 2:00s 0 - -# -# The rule line to be changed is: -# -# Rule C-Eur 1945 only - Sep 16 2:00 0 - -# -# It seems that Paris, Monaco, Rule France, Rule Belgium all agree on -# 2:00 standard time, e.g. 3:00 local time. However there are no -# countries that use C-Eur rules in September 1945, so the only items -# affected are apparently these fictitious zones that translate acronyms -# CET and MET: -# -# Zone CET 1:00 C-Eur CE%sT -# Zone MET 1:00 C-Eur ME%sT -# -# It this is right then the corrected version would look like: -# -# Rule C-Eur 1945 only - Sep 16 2:00s 0 - -# -# A small step for mankind though 8-) -Rule C-Eur 1945 only - Sep 16 2:00s 0 - -Rule C-Eur 1977 1980 - Apr Sun>=1 2:00s 1:00 S -Rule C-Eur 1977 only - Sep lastSun 2:00s 0 - -Rule C-Eur 1978 only - Oct 1 2:00s 0 - -Rule C-Eur 1979 1995 - Sep lastSun 2:00s 0 - -Rule C-Eur 1981 max - Mar lastSun 2:00s 1:00 S -Rule C-Eur 1996 max - Oct lastSun 2:00s 0 - - -# E-Eur differs from EU only in that E-Eur switches at midnight local time. -Rule E-Eur 1977 1980 - Apr Sun>=1 0:00 1:00 S -Rule E-Eur 1977 only - Sep lastSun 0:00 0 - -Rule E-Eur 1978 only - Oct 1 0:00 0 - -Rule E-Eur 1979 1995 - Sep lastSun 0:00 0 - -Rule E-Eur 1981 max - Mar lastSun 0:00 1:00 S -Rule E-Eur 1996 max - Oct lastSun 0:00 0 - - - -# Daylight saving time for Russia and the Soviet Union -# -# The 1917-1921 decree URLs are from Alexander Belopolsky (2016-08-23). - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Russia 1917 only - Jul 1 23:00 1:00 MST # Moscow Summer Time -# -# Decree No. 142 (1917-12-22) http://istmat.info/node/28137 -Rule Russia 1917 only - Dec 28 0:00 0 MMT # Moscow Mean Time -# -# Decree No. 497 (1918-05-30) http://istmat.info/node/30001 -Rule Russia 1918 only - May 31 22:00 2:00 MDST # Moscow Double Summer Time -Rule Russia 1918 only - Sep 16 1:00 1:00 MST -# -# Decree No. 258 (1919-05-29) http://istmat.info/node/37949 -Rule Russia 1919 only - May 31 23:00 2:00 MDST -# -Rule Russia 1919 only - Jul 1 0:00u 1:00 MSD -Rule Russia 1919 only - Aug 16 0:00 0 MSK -# -# Decree No. 63 (1921-02-03) http://istmat.info/node/45840 -Rule Russia 1921 only - Feb 14 23:00 1:00 MSD -# -# Decree No. 121 (1921-03-07) http://istmat.info/node/45949 -Rule Russia 1921 only - Mar 20 23:00 2:00 +05 -# -Rule Russia 1921 only - Sep 1 0:00 1:00 MSD -Rule Russia 1921 only - Oct 1 0:00 0 - -# Act No. 925 of the Council of Ministers of the USSR (1980-10-24): -Rule Russia 1981 1984 - Apr 1 0:00 1:00 S -Rule Russia 1981 1983 - Oct 1 0:00 0 - -# Act No. 967 of the Council of Ministers of the USSR (1984-09-13), repeated in -# Act No. 227 of the Council of Ministers of the USSR (1989-03-14): -Rule Russia 1984 1995 - Sep lastSun 2:00s 0 - -Rule Russia 1985 2010 - Mar lastSun 2:00s 1:00 S -# -Rule Russia 1996 2010 - Oct lastSun 2:00s 0 - -# As described below, Russia's 2014 change affects Zone data, not Rule data. - -# From Stepan Golosunov (2016-03-07): -# Wikipedia and other sources refer to the Act of the Council of -# Ministers of the USSR from 1988-01-04 No. 5 and the Act of the -# Council of Ministers of the USSR from 1989-03-14 No. 227. -# -# I did not find full texts of these acts. For the 1989 one we have -# title at https://base.garant.ru/70754136/ : -# "About change in calculation of time on the territories of -# Lithuanian SSR, Latvian SSR and Estonian SSR, Astrakhan, -# Kaliningrad, Kirov, Kuybyshev, Ulyanovsk and Uralsk oblasts". -# And http://astrozet.net/files/Zones/DOC/RU/1980-925.txt appears to -# contain quotes from both acts: Since last Sunday of March 1988 rules -# of the second time belt are installed in Volgograd and Saratov -# oblasts. Since last Sunday of March 1989: -# a) Lithuanian SSR, Latvian SSR, Estonian SSR, Kaliningrad oblast: -# second time belt rules without extra hour (Moscow-1); -# b) Astrakhan, Kirov, Kuybyshev, Ulyanovsk oblasts: second time belt -# rules (Moscow time) -# c) Uralsk oblast: third time belt rules (Moscow+1). - -# From Stepan Golosunov (2016-03-27): -# Unamended version of the act of the -# Government of the Russian Federation No. 23 from 08.01.1992 -# http://pravo.gov.ru/proxy/ips/?docbody=&nd=102014034&rdk=0 -# says that every year clocks were to be moved forward on last Sunday -# of March at 2 hours and moved backwards on last Sunday of September -# at 3 hours. It was amended in 1996 to replace September with October. - -# From Alexander Krivenyshev (2011-06-14): -# According to Kremlin press service, Russian President Dmitry Medvedev -# signed a federal law "On calculation of time" on June 9, 2011. -# According to the law Russia is abolishing daylight saving time. -# -# Medvedev signed a law "On the Calculation of Time" (in russian): -# http://bmockbe.ru/events/?ID=7583 -# -# Medvedev signed a law on the calculation of the time (in russian): -# https://www.regnum.ru/news/polit/1413906.html - -# From Arthur David Olson (2011-06-15): -# Take "abolishing daylight saving time" to mean that time is now considered -# to be standard. - -# These are for backward compatibility with older versions. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone WET 0:00 EU WE%sT -Zone CET 1:00 C-Eur CE%sT -Zone MET 1:00 C-Eur ME%sT -Zone EET 2:00 EU EE%sT - -# Previous editions of this database used abbreviations like MET DST -# for Central European Summer Time, but this didn't agree with common usage. - -# From Markus Kuhn (1996-07-12): -# The official German names ... are -# -# Mitteleuropäische Zeit (MEZ) = UTC+01:00 -# Mitteleuropäische Sommerzeit (MESZ) = UTC+02:00 -# -# as defined in the German Time Act (Gesetz über die Zeitbestimmung (ZeitG), -# 1978-07-25, Bundesgesetzblatt, Jahrgang 1978, Teil I, S. 1110-1111).... -# I wrote ... to the German Federal Physical-Technical Institution -# -# Physikalisch-Technische Bundesanstalt (PTB) -# Laboratorium 4.41 "Zeiteinheit" -# Postfach 3345 -# D-38023 Braunschweig -# phone: +49 531 592-0 -# -# ... I received today an answer letter from Dr. Peter Hetzel, head of the PTB -# department for time and frequency transmission. He explained that the -# PTB translates MEZ and MESZ into English as -# -# Central European Time (CET) = UTC+01:00 -# Central European Summer Time (CEST) = UTC+02:00 - - -# Albania -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Albania 1940 only - Jun 16 0:00 1:00 S -Rule Albania 1942 only - Nov 2 3:00 0 - -Rule Albania 1943 only - Mar 29 2:00 1:00 S -Rule Albania 1943 only - Apr 10 3:00 0 - -Rule Albania 1974 only - May 4 0:00 1:00 S -Rule Albania 1974 only - Oct 2 0:00 0 - -Rule Albania 1975 only - May 1 0:00 1:00 S -Rule Albania 1975 only - Oct 2 0:00 0 - -Rule Albania 1976 only - May 2 0:00 1:00 S -Rule Albania 1976 only - Oct 3 0:00 0 - -Rule Albania 1977 only - May 8 0:00 1:00 S -Rule Albania 1977 only - Oct 2 0:00 0 - -Rule Albania 1978 only - May 6 0:00 1:00 S -Rule Albania 1978 only - Oct 1 0:00 0 - -Rule Albania 1979 only - May 5 0:00 1:00 S -Rule Albania 1979 only - Sep 30 0:00 0 - -Rule Albania 1980 only - May 3 0:00 1:00 S -Rule Albania 1980 only - Oct 4 0:00 0 - -Rule Albania 1981 only - Apr 26 0:00 1:00 S -Rule Albania 1981 only - Sep 27 0:00 0 - -Rule Albania 1982 only - May 2 0:00 1:00 S -Rule Albania 1982 only - Oct 3 0:00 0 - -Rule Albania 1983 only - Apr 18 0:00 1:00 S -Rule Albania 1983 only - Oct 1 0:00 0 - -Rule Albania 1984 only - Apr 1 0:00 1:00 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Tirane 1:19:20 - LMT 1914 - 1:00 - CET 1940 Jun 16 - 1:00 Albania CE%sT 1984 Jul - 1:00 EU CE%sT - -# Andorra -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Andorra 0:06:04 - LMT 1901 - 0:00 - WET 1946 Sep 30 - 1:00 - CET 1985 Mar 31 2:00 - 1:00 EU CE%sT - -# Austria - -# Milne says Vienna time was 1:05:21. - -# From Paul Eggert (2006-03-22): Shanks & Pottenger give 1918-06-16 and -# 1945-11-18, but the Austrian Federal Office of Metrology and -# Surveying (BEV) gives 1918-09-16 and for Vienna gives the "alleged" -# date of 1945-04-12 with no time. For the 1980-04-06 transition -# Shanks & Pottenger give 02:00, the BEV 00:00. Go with the BEV, -# and guess 02:00 for 1945-04-12. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Austria 1920 only - Apr 5 2:00s 1:00 S -Rule Austria 1920 only - Sep 13 2:00s 0 - -Rule Austria 1946 only - Apr 14 2:00s 1:00 S -Rule Austria 1946 1948 - Oct Sun>=1 2:00s 0 - -Rule Austria 1947 only - Apr 6 2:00s 1:00 S -Rule Austria 1948 only - Apr 18 2:00s 1:00 S -Rule Austria 1980 only - Apr 6 0:00 1:00 S -Rule Austria 1980 only - Sep 28 0:00 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Vienna 1:05:21 - LMT 1893 Apr - 1:00 C-Eur CE%sT 1920 - 1:00 Austria CE%sT 1940 Apr 1 2:00s - 1:00 C-Eur CE%sT 1945 Apr 2 2:00s - 1:00 1:00 CEST 1945 Apr 12 2:00s - 1:00 - CET 1946 - 1:00 Austria CE%sT 1981 - 1:00 EU CE%sT - -# Belarus -# -# From Stepan Golosunov (2016-07-02): -# http://www.lawbelarus.com/repub/sub30/texf9611.htm -# (Act of the Cabinet of Ministers of the Republic of Belarus from -# 1992-03-25 No. 157) ... says clocks were to be moved forward at 2:00 -# on last Sunday of March and backward at 3:00 on last Sunday of September -# (the same as previous USSR and contemporary Russian regulations). -# -# From Yauhen Kharuzhy (2011-09-16): -# By latest Belarus government act Europe/Minsk timezone was changed to -# GMT+3 without DST (was GMT+2 with DST). -# -# Sources (Russian language): -# http://www.belta.by/ru/all_news/society/V-Belarusi-otmenjaetsja-perexod-na-sezonnoe-vremja_i_572952.html -# http://naviny.by/rubrics/society/2011/09/16/ic_articles_116_175144/ -# https://news.tut.by/society/250578.html -# -# From Alexander Bokovoy (2014-10-09): -# Belarussian government decided against changing to winter time.... -# http://eng.belta.by/all_news/society/Belarus-decides-against-adjusting-time-in-Russias-wake_i_76335.html -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Minsk 1:50:16 - LMT 1880 - 1:50 - MMT 1924 May 2 # Minsk Mean Time - 2:00 - EET 1930 Jun 21 - 3:00 - MSK 1941 Jun 28 - 1:00 C-Eur CE%sT 1944 Jul 3 - 3:00 Russia MSK/MSD 1990 - 3:00 - MSK 1991 Mar 31 2:00s - 2:00 Russia EE%sT 2011 Mar 27 2:00s - 3:00 - +03 - -# Belgium -# -# From Paul Eggert (1997-07-02): -# Entries from 1918 through 1991 are taken from: -# Annuaire de L'Observatoire Royal de Belgique, -# Avenue Circulaire, 3, B-1180 BRUXELLES, CLVIIe année, 1991 -# (Imprimerie HAYEZ, s.p.r.l., Rue Fin, 4, 1080 BRUXELLES, MCMXC), -# pp 8-9. -# LMT before 1892 was 0:17:30, according to the official journal of Belgium: -# Moniteur Belge, Samedi 30 Avril 1892, N.121. -# Thanks to Pascal Delmoitie for these references. -# The 1918 rules are listed for completeness; they apply to unoccupied Belgium. -# Assume Brussels switched to WET in 1918 when the armistice took effect. -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Belgium 1918 only - Mar 9 0:00s 1:00 S -Rule Belgium 1918 1919 - Oct Sat>=1 23:00s 0 - -Rule Belgium 1919 only - Mar 1 23:00s 1:00 S -Rule Belgium 1920 only - Feb 14 23:00s 1:00 S -Rule Belgium 1920 only - Oct 23 23:00s 0 - -Rule Belgium 1921 only - Mar 14 23:00s 1:00 S -Rule Belgium 1921 only - Oct 25 23:00s 0 - -Rule Belgium 1922 only - Mar 25 23:00s 1:00 S -Rule Belgium 1922 1927 - Oct Sat>=1 23:00s 0 - -Rule Belgium 1923 only - Apr 21 23:00s 1:00 S -Rule Belgium 1924 only - Mar 29 23:00s 1:00 S -Rule Belgium 1925 only - Apr 4 23:00s 1:00 S -# DSH writes that a royal decree of 1926-02-22 specified the Sun following 3rd -# Sat in Apr (except if it's Easter, in which case it's one Sunday earlier), -# to Sun following 1st Sat in Oct, and that a royal decree of 1928-09-15 -# changed the transition times to 02:00 GMT. -Rule Belgium 1926 only - Apr 17 23:00s 1:00 S -Rule Belgium 1927 only - Apr 9 23:00s 1:00 S -Rule Belgium 1928 only - Apr 14 23:00s 1:00 S -Rule Belgium 1928 1938 - Oct Sun>=2 2:00s 0 - -Rule Belgium 1929 only - Apr 21 2:00s 1:00 S -Rule Belgium 1930 only - Apr 13 2:00s 1:00 S -Rule Belgium 1931 only - Apr 19 2:00s 1:00 S -Rule Belgium 1932 only - Apr 3 2:00s 1:00 S -Rule Belgium 1933 only - Mar 26 2:00s 1:00 S -Rule Belgium 1934 only - Apr 8 2:00s 1:00 S -Rule Belgium 1935 only - Mar 31 2:00s 1:00 S -Rule Belgium 1936 only - Apr 19 2:00s 1:00 S -Rule Belgium 1937 only - Apr 4 2:00s 1:00 S -Rule Belgium 1938 only - Mar 27 2:00s 1:00 S -Rule Belgium 1939 only - Apr 16 2:00s 1:00 S -Rule Belgium 1939 only - Nov 19 2:00s 0 - -Rule Belgium 1940 only - Feb 25 2:00s 1:00 S -Rule Belgium 1944 only - Sep 17 2:00s 0 - -Rule Belgium 1945 only - Apr 2 2:00s 1:00 S -Rule Belgium 1945 only - Sep 16 2:00s 0 - -Rule Belgium 1946 only - May 19 2:00s 1:00 S -Rule Belgium 1946 only - Oct 7 2:00s 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Brussels 0:17:30 - LMT 1880 - 0:17:30 - BMT 1892 May 1 12:00 # Brussels MT - 0:00 - WET 1914 Nov 8 - 1:00 - CET 1916 May 1 0:00 - 1:00 C-Eur CE%sT 1918 Nov 11 11:00u - 0:00 Belgium WE%sT 1940 May 20 2:00s - 1:00 C-Eur CE%sT 1944 Sep 3 - 1:00 Belgium CE%sT 1977 - 1:00 EU CE%sT - -# Bosnia and Herzegovina -# See Europe/Belgrade. - -# Bulgaria -# -# From Plamen Simenov via Steffen Thorsen (1999-09-09): -# A document of Government of Bulgaria (No. 94/1997) says: -# EET -> EETDST is in 03:00 Local time in last Sunday of March ... -# EETDST -> EET is in 04:00 Local time in last Sunday of October -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Bulg 1979 only - Mar 31 23:00 1:00 S -Rule Bulg 1979 only - Oct 1 1:00 0 - -Rule Bulg 1980 1982 - Apr Sat>=1 23:00 1:00 S -Rule Bulg 1980 only - Sep 29 1:00 0 - -Rule Bulg 1981 only - Sep 27 2:00 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Sofia 1:33:16 - LMT 1880 - 1:56:56 - IMT 1894 Nov 30 # Istanbul MT? - 2:00 - EET 1942 Nov 2 3:00 - 1:00 C-Eur CE%sT 1945 - 1:00 - CET 1945 Apr 2 3:00 - 2:00 - EET 1979 Mar 31 23:00 - 2:00 Bulg EE%sT 1982 Sep 26 3:00 - 2:00 C-Eur EE%sT 1991 - 2:00 E-Eur EE%sT 1997 - 2:00 EU EE%sT - -# Croatia -# See Europe/Belgrade. - -# Cyprus -# Please see the 'asia' file for Asia/Nicosia. - -# Czech Republic / Czechia -# -# From Paul Eggert (2018-04-15): -# The source for Czech data is: Kdy začíná a končí letní čas. 2018-04-15. -# https://kalendar.beda.cz/kdy-zacina-a-konci-letni-cas -# We know of no English-language name for historical Czech winter time; -# abbreviate it as "GMT", as it happened to be GMT. -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Czech 1945 only - Apr Mon>=1 2:00s 1:00 S -Rule Czech 1945 only - Oct 1 2:00s 0 - -Rule Czech 1946 only - May 6 2:00s 1:00 S -Rule Czech 1946 1949 - Oct Sun>=1 2:00s 0 - -Rule Czech 1947 1948 - Apr Sun>=15 2:00s 1:00 S -Rule Czech 1949 only - Apr 9 2:00s 1:00 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Prague 0:57:44 - LMT 1850 - 0:57:44 - PMT 1891 Oct # Prague Mean Time - 1:00 C-Eur CE%sT 1945 May 9 - 1:00 Czech CE%sT 1946 Dec 1 3:00 -# Vanguard section, for zic and other parsers that support negative DST. - 1:00 -1:00 GMT 1947 Feb 23 2:00 -# Rearguard section, for parsers that do not support negative DST. -# 0:00 - GMT 1947 Feb 23 2:00 -# End of rearguard section. - 1:00 Czech CE%sT 1979 - 1:00 EU CE%sT -# Use Europe/Prague also for Slovakia. - -# Denmark, Faroe Islands, and Greenland - -# From Jesper Nørgaard Welen (2005-04-26): -# http://www.hum.aau.dk/~poe/tid/tine/DanskTid.htm says that the law -# [introducing standard time] was in effect from 1894-01-01.... -# The page http://www.retsinfo.dk/_GETDOCI_/ACCN/A18930008330-REGL -# confirms this, and states that the law was put forth 1893-03-29. -# -# The EU [actually, EEC and Euratom] treaty with effect from 1973: -# http://www.retsinfo.dk/_GETDOCI_/ACCN/A19722110030-REGL -# -# This provoked a new law from 1974 to make possible summer time changes -# in subsequent decrees with the law -# http://www.retsinfo.dk/_GETDOCI_/ACCN/A19740022330-REGL -# -# It seems however that no decree was set forward until 1980. I have -# not found any decree, but in another related law, the effecting DST -# changes are stated explicitly to be from 1980-04-06 at 02:00 to -# 1980-09-28 at 02:00. If this is true, this differs slightly from -# the EU rule in that DST runs to 02:00, not 03:00. We don't know -# when Denmark began using the EU rule correctly, but we have only -# confirmation of the 1980-time, so I presume it was correct in 1981: -# The law is about the management of the extra hour, concerning -# working hours reported and effect on obligatory-rest rules (which -# was suspended on that night): -# http://www.retsinfo.dk/_GETDOCI_/ACCN/C19801120554-REGL - -# From Jesper Nørgaard Welen (2005-06-11): -# The Herning Folkeblad (1980-09-26) reported that the night between -# Saturday and Sunday the clock is set back from three to two. - -# From Paul Eggert (2005-06-11): -# Hence the "02:00" of the 1980 law refers to standard time, not -# wall-clock time, and so the EU rules were in effect in 1980. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Denmark 1916 only - May 14 23:00 1:00 S -Rule Denmark 1916 only - Sep 30 23:00 0 - -Rule Denmark 1940 only - May 15 0:00 1:00 S -Rule Denmark 1945 only - Apr 2 2:00s 1:00 S -Rule Denmark 1945 only - Aug 15 2:00s 0 - -Rule Denmark 1946 only - May 1 2:00s 1:00 S -Rule Denmark 1946 only - Sep 1 2:00s 0 - -Rule Denmark 1947 only - May 4 2:00s 1:00 S -Rule Denmark 1947 only - Aug 10 2:00s 0 - -Rule Denmark 1948 only - May 9 2:00s 1:00 S -Rule Denmark 1948 only - Aug 8 2:00s 0 - -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Copenhagen 0:50:20 - LMT 1890 - 0:50:20 - CMT 1894 Jan 1 # Copenhagen MT - 1:00 Denmark CE%sT 1942 Nov 2 2:00s - 1:00 C-Eur CE%sT 1945 Apr 2 2:00 - 1:00 Denmark CE%sT 1980 - 1:00 EU CE%sT -Zone Atlantic/Faroe -0:27:04 - LMT 1908 Jan 11 # Tórshavn - 0:00 - WET 1981 - 0:00 EU WE%sT -# -# From Paul Eggert (2004-10-31): -# During World War II, Germany maintained secret manned weather stations in -# East Greenland and Franz Josef Land, but we don't know their time zones. -# My source for this is Wilhelm Dege's book mentioned under Svalbard. -# -# From Paul Eggert (2017-12-10): -# Greenland joined the European Communities as part of Denmark, -# obtained home rule on 1979-05-01, and left the European Communities -# on 1985-02-01. It therefore should have been using EU -# rules at least through 1984. Shanks & Pottenger say Scoresbysund and Godthåb -# used C-Eur rules after 1980, but IATA SSIM (1991/1996) says they use EU -# rules since at least 1991. Assume EU rules since 1980. - -# From Gwillim Law (2001-06-06), citing -# (2001-03-15), -# and with translations corrected by Steffen Thorsen: -# -# Greenland has four local times, and the relation to UTC -# is according to the following time line: -# -# The military zone near Thule UTC-4 -# Standard Greenland time UTC-3 -# Scoresbysund UTC-1 -# Danmarkshavn UTC -# -# In the military area near Thule and in Danmarkshavn DST will not be -# introduced. - -# From Rives McDow (2001-11-01): -# -# I correspond regularly with the Dansk Polarcenter, and wrote them at -# the time to clarify the situation in Thule. Unfortunately, I have -# not heard back from them regarding my recent letter. [But I have -# info from earlier correspondence.] -# -# According to the center, a very small local time zone around Thule -# Air Base keeps the time according to UTC-4, implementing daylight -# savings using North America rules, changing the time at 02:00 local time.... -# -# The east coast of Greenland north of the community of Scoresbysund -# uses UTC in the same way as in Iceland, year round, with no dst. -# There are just a few stations on this coast, including the -# Danmarkshavn ICAO weather station mentioned in your September 29th -# email. The other stations are two sledge patrol stations in -# Mestersvig and Daneborg, the air force base at Station Nord, and the -# DPC research station at Zackenberg. -# -# Scoresbysund and two small villages nearby keep time UTC-1 and use -# the same daylight savings time period as in West Greenland (Godthåb). -# -# The rest of Greenland, including Godthåb (this area, although it -# includes central Greenland, is known as west Greenland), keeps time -# UTC-3, with daylight savings methods according to European rules. -# -# It is common procedure to use UTC 0 in the wilderness of East and -# North Greenland, because it is mainly Icelandic aircraft operators -# maintaining traffic in these areas. However, the official status of -# this area is that it sticks with Godthåb time. This area might be -# considered a dual time zone in some respects because of this. - -# From Rives McDow (2001-11-19): -# I heard back from someone stationed at Thule; the time change took place -# there at 2:00 AM. - -# From Paul Eggert (2006-03-22): -# From 1997 on the CIA map shows Danmarkshavn on GMT; -# the 1995 map as like Godthåb. -# For lack of better info, assume they were like Godthåb before 1996. -# startkart.no says Thule does not observe DST, but this is clearly an error, -# so go with Shanks & Pottenger for Thule transitions until this year. -# For 2007 on assume Thule will stay in sync with US DST rules. - -# From J William Piggott (2016-02-20): -# "Greenland north of the community of Scoresbysund" is officially named -# "National Park" by Executive Order: -# http://naalakkersuisut.gl/~/media/Nanoq/Files/Attached%20Files/Engelske-tekster/Legislation/Executive%20Order%20National%20Park.rtf -# It is their only National Park. -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Thule 1991 1992 - Mar lastSun 2:00 1:00 D -Rule Thule 1991 1992 - Sep lastSun 2:00 0 S -Rule Thule 1993 2006 - Apr Sun>=1 2:00 1:00 D -Rule Thule 1993 2006 - Oct lastSun 2:00 0 S -Rule Thule 2007 max - Mar Sun>=8 2:00 1:00 D -Rule Thule 2007 max - Nov Sun>=1 2:00 0 S -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Danmarkshavn -1:14:40 - LMT 1916 Jul 28 - -3:00 - -03 1980 Apr 6 2:00 - -3:00 EU -03/-02 1996 - 0:00 - GMT -Zone America/Scoresbysund -1:27:52 - LMT 1916 Jul 28 # Ittoqqortoormiit - -2:00 - -02 1980 Apr 6 2:00 - -2:00 C-Eur -02/-01 1981 Mar 29 - -1:00 EU -01/+00 -Zone America/Godthab -3:26:56 - LMT 1916 Jul 28 # Nuuk - -3:00 - -03 1980 Apr 6 2:00 - -3:00 EU -03/-02 -Zone America/Thule -4:35:08 - LMT 1916 Jul 28 # Pituffik air base - -4:00 Thule A%sT - -# Estonia -# -# From Paul Eggert (2016-03-18): -# The 1989 transition is from USSR act No. 227 (1989-03-14). -# -# From Peter Ilieve (1994-10-15): -# A relative in Tallinn confirms the accuracy of the data for 1989 onwards -# [through 1994] and gives the legal authority for it, -# a regulation of the Government of Estonia, No. 111 of 1989.... -# -# From Peter Ilieve (1996-10-28): -# [IATA SSIM (1992/1996) claims that the Baltic republics switch at 01:00s, -# but a relative confirms that Estonia still switches at 02:00s, writing:] -# "I do not [know] exactly but there are some little different -# (confusing) rules for International Air and Railway Transport Schedules -# conversion in Sunday connected with end of summer time in Estonia.... -# A discussion is running about the summer time efficiency and effect on -# human physiology. It seems that Estonia maybe will not change to -# summer time next spring." - -# From Peter Ilieve (1998-11-04), heavily edited: -# The 1998-09-22 Estonian time law -# http://trip.rk.ee/cgi-bin/thw?${BASE}=akt&${OOHTML}=rtd&TA=1998&TO=1&AN=1390 -# refers to the Eighth Directive and cites the association agreement between -# the EU and Estonia, ratified by the Estonian law (RT II 1995, 22-27, 120). -# -# I also asked [my relative] whether they use any standard abbreviation -# for their standard and summer times. He says no, they use "suveaeg" -# (summer time) and "talveaeg" (winter time). - -# From The Baltic Times (1999-09-09) -# via Steffen Thorsen: -# This year will mark the last time Estonia shifts to summer time, -# a council of the ruling coalition announced Sept. 6.... -# But what this could mean for Estonia's chances of joining the European -# Union are still unclear. In 1994, the EU declared summer time compulsory -# for all member states until 2001. Brussels has yet to decide what to do -# after that. - -# From Mart Oruaas (2000-01-29): -# Regulation No. 301 (1999-10-12) obsoletes previous regulation -# No. 206 (1998-09-22) and thus sticks Estonia to +02:00 GMT for all -# the year round. The regulation is effective 1999-11-01. - -# From Toomas Soome (2002-02-21): -# The Estonian government has changed once again timezone politics. -# Now we are using again EU rules. -# -# From Urmet Jänes (2002-03-28): -# The legislative reference is Government decree No. 84 on 2002-02-21. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Tallinn 1:39:00 - LMT 1880 - 1:39:00 - TMT 1918 Feb # Tallinn Mean Time - 1:00 C-Eur CE%sT 1919 Jul - 1:39:00 - TMT 1921 May - 2:00 - EET 1940 Aug 6 - 3:00 - MSK 1941 Sep 15 - 1:00 C-Eur CE%sT 1944 Sep 22 - 3:00 Russia MSK/MSD 1989 Mar 26 2:00s - 2:00 1:00 EEST 1989 Sep 24 2:00s - 2:00 C-Eur EE%sT 1998 Sep 22 - 2:00 EU EE%sT 1999 Oct 31 4:00 - 2:00 - EET 2002 Feb 21 - 2:00 EU EE%sT - -# Finland - -# From Hannu Strang (1994-09-25 06:03:37 UTC): -# Well, here in Helsinki we're just changing from summer time to regular one, -# and it's supposed to change at 4am... - -# From Janne Snabb (2010-07-15): -# -# I noticed that the Finland data is not accurate for years 1981 and 1982. -# During these two first trial years the DST adjustment was made one hour -# earlier than in forthcoming years. Starting 1983 the adjustment was made -# according to the central European standards. -# -# This is documented in Heikki Oja: Aikakirja 2007, published by The Almanac -# Office of University of Helsinki, ISBN 952-10-3221-9, available online (in -# Finnish) at -# https://almanakka.helsinki.fi/aikakirja/Aikakirja2007kokonaan.pdf -# -# Page 105 (56 in PDF version) has a handy table of all past daylight savings -# transitions. It is easy enough to interpret without Finnish skills. -# -# This is also confirmed by Finnish Broadcasting Company's archive at: -# http://www.yle.fi/elavaarkisto/?s=s&g=1&ag=5&t=&a=3401 -# -# The news clip from 1981 says that "the time between 2 and 3 o'clock does not -# exist tonight." - -# From Konstantin Hyppönen (2014-06-13): -# [Heikki Oja's book Aikakirja 2013] -# https://almanakka.helsinki.fi/images/aikakirja/Aikakirja2013kokonaan.pdf -# pages 104-105, including a scan from a newspaper published on Apr 2 1942 -# say that ... [o]n Apr 2 1942, 24 o'clock (which means Apr 3 1942, -# 00:00), clocks were moved one hour forward. The newspaper -# mentions "on the night from Thursday to Friday".... -# On Oct 4 1942, clocks were moved at 1:00 one hour backwards. -# -# From Paul Eggert (2014-06-14): -# Go with Oja over Shanks. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Finland 1942 only - Apr 2 24:00 1:00 S -Rule Finland 1942 only - Oct 4 1:00 0 - -Rule Finland 1981 1982 - Mar lastSun 2:00 1:00 S -Rule Finland 1981 1982 - Sep lastSun 3:00 0 - - -# Milne says Helsinki (Helsingfors) time was 1:39:49.2 (official document); -# round to nearest. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Helsinki 1:39:49 - LMT 1878 May 31 - 1:39:49 - HMT 1921 May # Helsinki Mean Time - 2:00 Finland EE%sT 1983 - 2:00 EU EE%sT - -# Åland Is -Link Europe/Helsinki Europe/Mariehamn - - -# France - -# From Ciro Discepolo (2000-12-20): -# -# Henri Le Corre, Régimes horaires pour le monde entier, Éditions -# Traditionnelles - Paris 2 books, 1993 -# -# Gabriel, Traité de l'heure dans le monde, Guy Trédaniel, -# Paris, 1991 -# -# Françoise Gauquelin, Problèmes de l'heure résolus en astrologie, -# Guy Trédaniel, Paris 1987 - - -# -# Shank & Pottenger seem to use '24:00' ambiguously; resolve it with Whitman. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule France 1916 only - Jun 14 23:00s 1:00 S -Rule France 1916 1919 - Oct Sun>=1 23:00s 0 - -Rule France 1917 only - Mar 24 23:00s 1:00 S -Rule France 1918 only - Mar 9 23:00s 1:00 S -Rule France 1919 only - Mar 1 23:00s 1:00 S -Rule France 1920 only - Feb 14 23:00s 1:00 S -Rule France 1920 only - Oct 23 23:00s 0 - -Rule France 1921 only - Mar 14 23:00s 1:00 S -Rule France 1921 only - Oct 25 23:00s 0 - -Rule France 1922 only - Mar 25 23:00s 1:00 S -# DSH writes that a law of 1923-05-24 specified 3rd Sat in Apr at 23:00 to 1st -# Sat in Oct at 24:00; and that in 1930, because of Easter, the transitions -# were Apr 12 and Oct 5. Go with Shanks & Pottenger. -Rule France 1922 1938 - Oct Sat>=1 23:00s 0 - -Rule France 1923 only - May 26 23:00s 1:00 S -Rule France 1924 only - Mar 29 23:00s 1:00 S -Rule France 1925 only - Apr 4 23:00s 1:00 S -Rule France 1926 only - Apr 17 23:00s 1:00 S -Rule France 1927 only - Apr 9 23:00s 1:00 S -Rule France 1928 only - Apr 14 23:00s 1:00 S -Rule France 1929 only - Apr 20 23:00s 1:00 S -Rule France 1930 only - Apr 12 23:00s 1:00 S -Rule France 1931 only - Apr 18 23:00s 1:00 S -Rule France 1932 only - Apr 2 23:00s 1:00 S -Rule France 1933 only - Mar 25 23:00s 1:00 S -Rule France 1934 only - Apr 7 23:00s 1:00 S -Rule France 1935 only - Mar 30 23:00s 1:00 S -Rule France 1936 only - Apr 18 23:00s 1:00 S -Rule France 1937 only - Apr 3 23:00s 1:00 S -Rule France 1938 only - Mar 26 23:00s 1:00 S -Rule France 1939 only - Apr 15 23:00s 1:00 S -Rule France 1939 only - Nov 18 23:00s 0 - -Rule France 1940 only - Feb 25 2:00 1:00 S -# The French rules for 1941-1944 were not used in Paris, but Shanks & Pottenger -# write that they were used in Monaco and in many French locations. -# Le Corre writes that the upper limit of the free zone was Arnéguy, Orthez, -# Mont-de-Marsan, Bazas, Langon, Lamothe-Montravel, Marœuil, La -# Rochefoucauld, Champagne-Mouton, La Roche-Posay, La Haye-Descartes, -# Loches, Montrichard, Vierzon, Bourges, Moulins, Digoin, -# Paray-le-Monial, Montceau-les-Mines, Chalon-sur-Saône, Arbois, -# Dole, Morez, St-Claude, and Collonges (Haute-Savoie). -Rule France 1941 only - May 5 0:00 2:00 M # Midsummer -# Shanks & Pottenger say this transition occurred at Oct 6 1:00, -# but go with Denis Excoffier (1997-12-12), -# who quotes the Ephémérides astronomiques for 1998 from Bureau des Longitudes -# as saying 5/10/41 22hUT. -Rule France 1941 only - Oct 6 0:00 1:00 S -Rule France 1942 only - Mar 9 0:00 2:00 M -Rule France 1942 only - Nov 2 3:00 1:00 S -Rule France 1943 only - Mar 29 2:00 2:00 M -Rule France 1943 only - Oct 4 3:00 1:00 S -Rule France 1944 only - Apr 3 2:00 2:00 M -Rule France 1944 only - Oct 8 1:00 1:00 S -Rule France 1945 only - Apr 2 2:00 2:00 M -Rule France 1945 only - Sep 16 3:00 0 - -# Shanks & Pottenger give Mar 28 2:00 and Sep 26 3:00; -# go with Excoffier's 28/3/76 0hUT and 25/9/76 23hUT. -Rule France 1976 only - Mar 28 1:00 1:00 S -Rule France 1976 only - Sep 26 1:00 0 - -# Shanks & Pottenger give 0:09:20 for Paris Mean Time, and Whitman 0:09:05, -# but Howse quotes the actual French legislation as saying 0:09:21. -# Go with Howse. Howse writes that the time in France was officially based -# on PMT-0:09:21 until 1978-08-09, when the time base finally switched to UTC. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Paris 0:09:21 - LMT 1891 Mar 15 0:01 - 0:09:21 - PMT 1911 Mar 11 0:01 # Paris MT -# Shanks & Pottenger give 1940 Jun 14 0:00; go with Excoffier and Le Corre. - 0:00 France WE%sT 1940 Jun 14 23:00 -# Le Corre says Paris stuck with occupied-France time after the liberation; -# go with Shanks & Pottenger. - 1:00 C-Eur CE%sT 1944 Aug 25 - 0:00 France WE%sT 1945 Sep 16 3:00 - 1:00 France CE%sT 1977 - 1:00 EU CE%sT - -# Germany - -# From Markus Kuhn (1998-09-29): -# The German time zone web site by the Physikalisch-Technische -# Bundesanstalt contains DST information back to 1916. -# [See tz-link.html for the URL.] - -# From Jörg Schilling (2002-10-23): -# In 1945, Berlin was switched to Moscow Summer time (GMT+4) by -# https://www.dhm.de/lemo/html/biografien/BersarinNikolai/ -# General [Nikolai] Bersarin. - -# From Paul Eggert (2003-03-08): -# http://www.parlament-berlin.de/pds-fraktion.nsf/727459127c8b66ee8525662300459099/defc77cb784f180ac1256c2b0030274b/$FILE/bersarint.pdf -# says that Bersarin issued an order to use Moscow time on May 20. -# However, Moscow did not observe daylight saving in 1945, so -# this was equivalent to UT +03, not +04. - - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Germany 1946 only - Apr 14 2:00s 1:00 S -Rule Germany 1946 only - Oct 7 2:00s 0 - -Rule Germany 1947 1949 - Oct Sun>=1 2:00s 0 - -# http://www.ptb.de/de/org/4/44/441/salt.htm says the following transition -# occurred at 3:00 MEZ, not the 2:00 MEZ given in Shanks & Pottenger. -# Go with the PTB. -Rule Germany 1947 only - Apr 6 3:00s 1:00 S -Rule Germany 1947 only - May 11 2:00s 2:00 M -Rule Germany 1947 only - Jun 29 3:00 1:00 S -Rule Germany 1948 only - Apr 18 2:00s 1:00 S -Rule Germany 1949 only - Apr 10 2:00s 1:00 S - -Rule SovietZone 1945 only - May 24 2:00 2:00 M # Midsummer -Rule SovietZone 1945 only - Sep 24 3:00 1:00 S -Rule SovietZone 1945 only - Nov 18 2:00s 0 - - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Berlin 0:53:28 - LMT 1893 Apr - 1:00 C-Eur CE%sT 1945 May 24 2:00 - 1:00 SovietZone CE%sT 1946 - 1:00 Germany CE%sT 1980 - 1:00 EU CE%sT - -# From Tobias Conradi (2011-09-12): -# Büsingen , surrounded by the Swiss canton -# Schaffhausen, did not start observing DST in 1980 as the rest of DE -# (West Germany at that time) and DD (East Germany at that time) did. -# DD merged into DE, the area is currently covered by code DE in ISO 3166-1, -# which in turn is covered by the zone Europe/Berlin. -# -# Source for the time in Büsingen 1980: -# http://www.srf.ch/player/video?id=c012c029-03b7-4c2b-9164-aa5902cd58d3 - -# From Arthur David Olson (2012-03-03): -# Büsingen and Zurich have shared clocks since 1970. - -Link Europe/Zurich Europe/Busingen - -# Georgia -# Please see the "asia" file for Asia/Tbilisi. -# Herodotus (Histories, IV.45) says Georgia north of the Phasis (now Rioni) -# is in Europe. Our reference location Tbilisi is in the Asian part. - -# Gibraltar -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Gibraltar -0:21:24 - LMT 1880 Aug 2 0:00s - 0:00 GB-Eire %s 1957 Apr 14 2:00 - 1:00 - CET 1982 - 1:00 EU CE%sT - -# Greece -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -# Whitman gives 1932 Jul 5 - Nov 1; go with Shanks & Pottenger. -Rule Greece 1932 only - Jul 7 0:00 1:00 S -Rule Greece 1932 only - Sep 1 0:00 0 - -# Whitman gives 1941 Apr 25 - ?; go with Shanks & Pottenger. -Rule Greece 1941 only - Apr 7 0:00 1:00 S -# Whitman gives 1942 Feb 2 - ?; go with Shanks & Pottenger. -Rule Greece 1942 only - Nov 2 3:00 0 - -Rule Greece 1943 only - Mar 30 0:00 1:00 S -Rule Greece 1943 only - Oct 4 0:00 0 - -# Whitman gives 1944 Oct 3 - Oct 31; go with Shanks & Pottenger. -Rule Greece 1952 only - Jul 1 0:00 1:00 S -Rule Greece 1952 only - Nov 2 0:00 0 - -Rule Greece 1975 only - Apr 12 0:00s 1:00 S -Rule Greece 1975 only - Nov 26 0:00s 0 - -Rule Greece 1976 only - Apr 11 2:00s 1:00 S -Rule Greece 1976 only - Oct 10 2:00s 0 - -Rule Greece 1977 1978 - Apr Sun>=1 2:00s 1:00 S -Rule Greece 1977 only - Sep 26 2:00s 0 - -Rule Greece 1978 only - Sep 24 4:00 0 - -Rule Greece 1979 only - Apr 1 9:00 1:00 S -Rule Greece 1979 only - Sep 29 2:00 0 - -Rule Greece 1980 only - Apr 1 0:00 1:00 S -Rule Greece 1980 only - Sep 28 0:00 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Athens 1:34:52 - LMT 1895 Sep 14 - 1:34:52 - AMT 1916 Jul 28 0:01 # Athens MT - 2:00 Greece EE%sT 1941 Apr 30 - 1:00 Greece CE%sT 1944 Apr 4 - 2:00 Greece EE%sT 1981 - # Shanks & Pottenger say it switched to C-Eur in 1981; - # go with EU rules instead, since Greece joined Jan 1. - 2:00 EU EE%sT - -# Hungary -# From Paul Eggert (2014-07-15): -# Dates for 1916-1945 are taken from: -# Oross A. Jelen a múlt jövője: a nyári időszámítás Magyarországon 1916-1945. -# National Archives of Hungary (2012-10-29). -# http://mnl.gov.hu/a_het_dokumentuma/a_nyari_idoszamitas_magyarorszagon_19161945.html -# This source does not always give times, which are taken from Shanks -# & Pottenger (which disagree about the dates). -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Hungary 1918 only - Apr 1 3:00 1:00 S -Rule Hungary 1918 only - Sep 16 3:00 0 - -Rule Hungary 1919 only - Apr 15 3:00 1:00 S -Rule Hungary 1919 only - Nov 24 3:00 0 - -Rule Hungary 1945 only - May 1 23:00 1:00 S -Rule Hungary 1945 only - Nov 1 0:00 0 - -Rule Hungary 1946 only - Mar 31 2:00s 1:00 S -Rule Hungary 1946 1949 - Oct Sun>=1 2:00s 0 - -Rule Hungary 1947 1949 - Apr Sun>=4 2:00s 1:00 S -Rule Hungary 1950 only - Apr 17 2:00s 1:00 S -Rule Hungary 1950 only - Oct 23 2:00s 0 - -Rule Hungary 1954 1955 - May 23 0:00 1:00 S -Rule Hungary 1954 1955 - Oct 3 0:00 0 - -Rule Hungary 1956 only - Jun Sun>=1 0:00 1:00 S -Rule Hungary 1956 only - Sep lastSun 0:00 0 - -Rule Hungary 1957 only - Jun Sun>=1 1:00 1:00 S -Rule Hungary 1957 only - Sep lastSun 3:00 0 - -Rule Hungary 1980 only - Apr 6 1:00 1:00 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Budapest 1:16:20 - LMT 1890 Oct - 1:00 C-Eur CE%sT 1918 - 1:00 Hungary CE%sT 1941 Apr 8 - 1:00 C-Eur CE%sT 1945 - 1:00 Hungary CE%sT 1980 Sep 28 2:00s - 1:00 EU CE%sT - -# Iceland -# -# From Adam David (1993-11-06): -# The name of the timezone in Iceland for system / mail / news purposes is GMT. -# -# (1993-12-05): -# This material is paraphrased from the 1988 edition of the University of -# Iceland Almanak. -# -# From January 1st, 1908 the whole of Iceland was standardised at 1 hour -# behind GMT. Previously, local mean solar time was used in different parts -# of Iceland, the almanak had been based on Reykjavik mean solar time which -# was 1 hour and 28 minutes behind GMT. -# -# "first day of winter" referred to [below] means the first day of the 26 weeks -# of winter, according to the old icelandic calendar that dates back to the -# time the norsemen first settled Iceland. The first day of winter is always -# Saturday, but is not dependent on the Julian or Gregorian calendars. -# -# (1993-12-10): -# I have a reference from the Oxford Icelandic-English dictionary for the -# beginning of winter, which ties it to the ecclesiastical calendar (and thus -# to the julian/gregorian calendar) over the period in question. -# the winter begins on the Saturday next before St. Luke's day -# (old style), or on St. Luke's day, if a Saturday. -# St. Luke's day ought to be traceable from ecclesiastical sources. "old style" -# might be a reference to the Julian calendar as opposed to Gregorian, or it -# might mean something else (???). -# -# From Paul Eggert (2014-11-22): -# The information below is taken from the 1988 Almanak; see -# http://www.almanak.hi.is/klukkan.html -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Iceland 1917 1919 - Feb 19 23:00 1:00 - -Rule Iceland 1917 only - Oct 21 1:00 0 - -Rule Iceland 1918 1919 - Nov 16 1:00 0 - -Rule Iceland 1921 only - Mar 19 23:00 1:00 - -Rule Iceland 1921 only - Jun 23 1:00 0 - -Rule Iceland 1939 only - Apr 29 23:00 1:00 - -Rule Iceland 1939 only - Oct 29 2:00 0 - -Rule Iceland 1940 only - Feb 25 2:00 1:00 - -Rule Iceland 1940 1941 - Nov Sun>=2 1:00s 0 - -Rule Iceland 1941 1942 - Mar Sun>=2 1:00s 1:00 - -# 1943-1946 - first Sunday in March until first Sunday in winter -Rule Iceland 1943 1946 - Mar Sun>=1 1:00s 1:00 - -Rule Iceland 1942 1948 - Oct Sun>=22 1:00s 0 - -# 1947-1967 - first Sunday in April until first Sunday in winter -Rule Iceland 1947 1967 - Apr Sun>=1 1:00s 1:00 - -# 1949 and 1967 Oct transitions delayed by 1 week -Rule Iceland 1949 only - Oct 30 1:00s 0 - -Rule Iceland 1950 1966 - Oct Sun>=22 1:00s 0 - -Rule Iceland 1967 only - Oct 29 1:00s 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Atlantic/Reykjavik -1:28 - LMT 1908 - -1:00 Iceland -01/+00 1968 Apr 7 1:00s - 0:00 - GMT - -# Italy -# -# From Paul Eggert (2001-03-06): -# Sicily and Sardinia each had their own time zones from 1866 to 1893, -# called Palermo Time (+00:53:28) and Cagliari Time (+00:36:32). -# During World War II, German-controlled Italy used German time. -# But these events all occurred before the 1970 cutoff, -# so record only the time in Rome. -# -# From Michael Deckers (2016-10-24): -# http://www.ac-ilsestante.it/MERIDIANE/ora_legale quotes a law of 1893-08-10 -# ... [translated as] "The preceding dispositions will enter into -# force at the instant at which, according to the time specified in -# the 1st article, the 1st of November 1893 will begin...." -# -# From Pierpaolo Bernardi (2016-10-20): -# The authoritative source for time in Italy is the national metrological -# institute, which has a summary page of historical DST data at -# http://www.inrim.it/res/tf/ora_legale_i.shtml -# (2016-10-24): -# http://www.renzobaldini.it/le-ore-legali-in-italia/ -# has still different data for 1944. It divides Italy in two, as -# there were effectively two governments at the time, north of Gothic -# Line German controlled territory, official government RSI, and south -# of the Gothic Line, controlled by allied armies. -# -# From Brian Inglis (2016-10-23): -# Viceregal LEGISLATIVE DECREE. 14 September 1944, no. 219. -# Restoration of Standard Time. (044U0219) (OJ 62 of 30.9.1944) ... -# Given the R. law decreed on 1944-03-29, no. 92, by which standard time is -# advanced to sixty minutes later starting at hour two on 1944-04-02; ... -# Starting at hour three on the date 1944-09-17 standard time will be resumed. -# -# From Paul Eggert (2016-10-27): -# Go with INRiM for DST rules, except as corrected by Inglis for 1944 -# for the Kingdom of Italy. This is consistent with Renzo Baldini. -# Model Rome's occupation by using C-Eur rules from 1943-09-10 -# to 1944-06-04; although Rome was an open city during this period, it -# was effectively controlled by Germany. -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Italy 1916 only - Jun 3 24:00 1:00 S -Rule Italy 1916 1917 - Sep 30 24:00 0 - -Rule Italy 1917 only - Mar 31 24:00 1:00 S -Rule Italy 1918 only - Mar 9 24:00 1:00 S -Rule Italy 1918 only - Oct 6 24:00 0 - -Rule Italy 1919 only - Mar 1 24:00 1:00 S -Rule Italy 1919 only - Oct 4 24:00 0 - -Rule Italy 1920 only - Mar 20 24:00 1:00 S -Rule Italy 1920 only - Sep 18 24:00 0 - -Rule Italy 1940 only - Jun 14 24:00 1:00 S -Rule Italy 1942 only - Nov 2 2:00s 0 - -Rule Italy 1943 only - Mar 29 2:00s 1:00 S -Rule Italy 1943 only - Oct 4 2:00s 0 - -Rule Italy 1944 only - Apr 2 2:00s 1:00 S -Rule Italy 1944 only - Sep 17 2:00s 0 - -Rule Italy 1945 only - Apr 2 2:00 1:00 S -Rule Italy 1945 only - Sep 15 1:00 0 - -Rule Italy 1946 only - Mar 17 2:00s 1:00 S -Rule Italy 1946 only - Oct 6 2:00s 0 - -Rule Italy 1947 only - Mar 16 0:00s 1:00 S -Rule Italy 1947 only - Oct 5 0:00s 0 - -Rule Italy 1948 only - Feb 29 2:00s 1:00 S -Rule Italy 1948 only - Oct 3 2:00s 0 - -Rule Italy 1966 1968 - May Sun>=22 0:00s 1:00 S -Rule Italy 1966 only - Sep 24 24:00 0 - -Rule Italy 1967 1969 - Sep Sun>=22 0:00s 0 - -Rule Italy 1969 only - Jun 1 0:00s 1:00 S -Rule Italy 1970 only - May 31 0:00s 1:00 S -Rule Italy 1970 only - Sep lastSun 0:00s 0 - -Rule Italy 1971 1972 - May Sun>=22 0:00s 1:00 S -Rule Italy 1971 only - Sep lastSun 0:00s 0 - -Rule Italy 1972 only - Oct 1 0:00s 0 - -Rule Italy 1973 only - Jun 3 0:00s 1:00 S -Rule Italy 1973 1974 - Sep lastSun 0:00s 0 - -Rule Italy 1974 only - May 26 0:00s 1:00 S -Rule Italy 1975 only - Jun 1 0:00s 1:00 S -Rule Italy 1975 1977 - Sep lastSun 0:00s 0 - -Rule Italy 1976 only - May 30 0:00s 1:00 S -Rule Italy 1977 1979 - May Sun>=22 0:00s 1:00 S -Rule Italy 1978 only - Oct 1 0:00s 0 - -Rule Italy 1979 only - Sep 30 0:00s 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Rome 0:49:56 - LMT 1866 Sep 22 - 0:49:56 - RMT 1893 Oct 31 23:49:56 # Rome Mean - 1:00 Italy CE%sT 1943 Sep 10 - 1:00 C-Eur CE%sT 1944 Jun 4 - 1:00 Italy CE%sT 1980 - 1:00 EU CE%sT - -Link Europe/Rome Europe/Vatican -Link Europe/Rome Europe/San_Marino - -# Latvia - -# From Liene Kanepe (1998-09-17): - -# I asked about this matter Scientific Secretary of the Institute of Astronomy -# of The University of Latvia Dr. paed Mr. Ilgonis Vilks. I also searched the -# correct data in juridical acts and I found some juridical documents about -# changes in the counting of time in Latvia from 1981.... -# -# Act No. 35 of the Council of Ministers of Latvian SSR of 1981-01-22 ... -# according to the Act No. 925 of the Council of Ministers of USSR of 1980-10-24 -# ...: all year round the time of 2nd time zone + 1 hour, in addition turning -# the hands of the clock 1 hour forward on 1 April at 00:00 (GMT 31 March 21:00) -# and 1 hour backward on the 1 October at 00:00 (GMT 30 September 20:00). -# -# Act No. 592 of the Council of Ministers of Latvian SSR of 1984-09-24 ... -# according to the Act No. 967 of the Council of Ministers of USSR of 1984-09-13 -# ...: all year round the time of 2nd time zone + 1 hour, in addition turning -# the hands of the clock 1 hour forward on the last Sunday of March at 02:00 -# (GMT 23:00 on the previous day) and 1 hour backward on the last Sunday of -# September at 03:00 (GMT 23:00 on the previous day). -# -# Act No. 81 of the Council of Ministers of Latvian SSR of 1989-03-22 ... -# according to the Act No. 227 of the Council of Ministers of USSR of 1989-03-14 -# ...: since the last Sunday of March 1989 in Lithuanian SSR, Latvian SSR, -# Estonian SSR and Kaliningrad region of Russian Federation all year round the -# time of 2nd time zone (Moscow time minus one hour). On the territory of Latvia -# transition to summer time is performed on the last Sunday of March at 02:00 -# (GMT 00:00), turning the hands of the clock 1 hour forward. The end of -# daylight saving time is performed on the last Sunday of September at 03:00 -# (GMT 00:00), turning the hands of the clock 1 hour backward. Exception is -# 1989-03-26, when we must not turn the hands of the clock.... -# -# The Regulations of the Cabinet of Ministers of the Republic of Latvia of -# 1997-01-21 on transition to Summer time ... established the same order of -# daylight savings time settings as in the States of the European Union. - -# From Andrei Ivanov (2000-03-06): -# This year Latvia will not switch to Daylight Savings Time (as specified in -# The Regulations of the Cabinet of Ministers of the Rep. of Latvia of -# 29-Feb-2000 (No. 79) , -# in Latvian for subscribers only). - -# From RFE/RL Newsline -# http://www.rferl.org/newsline/2001/01/3-CEE/cee-030101.html -# (2001-01-03), noted after a heads-up by Rives McDow: -# The Latvian government on 2 January decided that the country will -# institute daylight-saving time this spring, LETA reported. -# Last February the three Baltic states decided not to turn back their -# clocks one hour in the spring.... -# Minister of Economy Aigars Kalvītis noted that Latvia had too few -# daylight hours and thus decided to comply with a draft European -# Commission directive that provides for instituting daylight-saving -# time in EU countries between 2002 and 2006. The Latvian government -# urged Lithuania and Estonia to adopt a similar time policy, but it -# appears that they will not do so.... - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Latvia 1989 1996 - Mar lastSun 2:00s 1:00 S -Rule Latvia 1989 1996 - Sep lastSun 2:00s 0 - - -# Milne 1899 says Riga was 1:36:28 (Polytechnique House time). -# Byalokoz 1919 says Latvia was 1:36:34. -# Go with Byalokoz. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Riga 1:36:34 - LMT 1880 - 1:36:34 - RMT 1918 Apr 15 2:00 # Riga MT - 1:36:34 1:00 LST 1918 Sep 16 3:00 # Latvian ST - 1:36:34 - RMT 1919 Apr 1 2:00 - 1:36:34 1:00 LST 1919 May 22 3:00 - 1:36:34 - RMT 1926 May 11 - 2:00 - EET 1940 Aug 5 - 3:00 - MSK 1941 Jul - 1:00 C-Eur CE%sT 1944 Oct 13 - 3:00 Russia MSK/MSD 1989 Mar lastSun 2:00s - 2:00 1:00 EEST 1989 Sep lastSun 2:00s - 2:00 Latvia EE%sT 1997 Jan 21 - 2:00 EU EE%sT 2000 Feb 29 - 2:00 - EET 2001 Jan 2 - 2:00 EU EE%sT - -# Liechtenstein - -# From Paul Eggert (2013-09-09): -# Shanks & Pottenger say Vaduz is like Zurich. - -# From Alois Treindl (2013-09-18): -# http://www.eliechtensteinensia.li/LIJ/1978/1938-1978/1941.pdf -# ... confirms on p. 6 that Liechtenstein followed Switzerland in 1941 and 1942. -# I ... translate only the last two paragraphs: -# ... during second world war, in the years 1941 and 1942, Liechtenstein -# introduced daylight saving time, adapting to Switzerland. From 1943 on -# central European time was in force throughout the year. -# From a report of the duke's government to the high council, -# regarding the introduction of a time law, of 31 May 1977. - -Link Europe/Zurich Europe/Vaduz - - -# Lithuania - -# From Paul Eggert (2016-03-18): -# The 1989 transition is from USSR act No. 227 (1989-03-14). - -# From Paul Eggert (1996-11-22): -# IATA SSIM (1992/1996) says Lithuania uses W-Eur rules, but since it is -# known to be wrong about Estonia and Latvia, assume it's wrong here too. - -# From Marius Gedminas (1998-08-07): -# I would like to inform that in this year Lithuanian time zone -# (Europe/Vilnius) was changed. - -# From ELTA No. 972 (2582) (1999-09-29) , -# via Steffen Thorsen: -# Lithuania has shifted back to the second time zone (GMT plus two hours) -# to be valid here starting from October 31, -# as decided by the national government on Wednesday.... -# The Lithuanian government also announced plans to consider a -# motion to give up shifting to summer time in spring, as it was -# already done by Estonia. - -# From the Fact File, Lithuanian State Department of Tourism -# (2000-03-27): -# Local time is GMT+2 hours ..., no daylight saving. - -# From a user via Klaus Marten (2003-02-07): -# As a candidate for membership of the European Union, Lithuania will -# observe Summer Time in 2003, changing its clocks at the times laid -# down in EU Directive 2000/84 of 19.I.01 (i.e. at the same times as its -# neighbour Latvia). The text of the Lithuanian government Order of -# 7.XI.02 to this effect can be found at -# http://www.lrvk.lt/nut/11/n1749.htm - - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Vilnius 1:41:16 - LMT 1880 - 1:24:00 - WMT 1917 # Warsaw Mean Time - 1:35:36 - KMT 1919 Oct 10 # Kaunas Mean Time - 1:00 - CET 1920 Jul 12 - 2:00 - EET 1920 Oct 9 - 1:00 - CET 1940 Aug 3 - 3:00 - MSK 1941 Jun 24 - 1:00 C-Eur CE%sT 1944 Aug - 3:00 Russia MSK/MSD 1989 Mar 26 2:00s - 2:00 Russia EE%sT 1991 Sep 29 2:00s - 2:00 C-Eur EE%sT 1998 - 2:00 - EET 1998 Mar 29 1:00u - 1:00 EU CE%sT 1999 Oct 31 1:00u - 2:00 - EET 2003 Jan 1 - 2:00 EU EE%sT - -# Luxembourg -# Whitman disagrees with most of these dates in minor ways; -# go with Shanks & Pottenger. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Lux 1916 only - May 14 23:00 1:00 S -Rule Lux 1916 only - Oct 1 1:00 0 - -Rule Lux 1917 only - Apr 28 23:00 1:00 S -Rule Lux 1917 only - Sep 17 1:00 0 - -Rule Lux 1918 only - Apr Mon>=15 2:00s 1:00 S -Rule Lux 1918 only - Sep Mon>=15 2:00s 0 - -Rule Lux 1919 only - Mar 1 23:00 1:00 S -Rule Lux 1919 only - Oct 5 3:00 0 - -Rule Lux 1920 only - Feb 14 23:00 1:00 S -Rule Lux 1920 only - Oct 24 2:00 0 - -Rule Lux 1921 only - Mar 14 23:00 1:00 S -Rule Lux 1921 only - Oct 26 2:00 0 - -Rule Lux 1922 only - Mar 25 23:00 1:00 S -Rule Lux 1922 only - Oct Sun>=2 1:00 0 - -Rule Lux 1923 only - Apr 21 23:00 1:00 S -Rule Lux 1923 only - Oct Sun>=2 2:00 0 - -Rule Lux 1924 only - Mar 29 23:00 1:00 S -Rule Lux 1924 1928 - Oct Sun>=2 1:00 0 - -Rule Lux 1925 only - Apr 5 23:00 1:00 S -Rule Lux 1926 only - Apr 17 23:00 1:00 S -Rule Lux 1927 only - Apr 9 23:00 1:00 S -Rule Lux 1928 only - Apr 14 23:00 1:00 S -Rule Lux 1929 only - Apr 20 23:00 1:00 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Luxembourg 0:24:36 - LMT 1904 Jun - 1:00 Lux CE%sT 1918 Nov 25 - 0:00 Lux WE%sT 1929 Oct 6 2:00s - 0:00 Belgium WE%sT 1940 May 14 3:00 - 1:00 C-Eur WE%sT 1944 Sep 18 3:00 - 1:00 Belgium CE%sT 1977 - 1:00 EU CE%sT - -# Macedonia -# See Europe/Belgrade. - -# Malta -# -# From Paul Eggert (2016-10-21): -# Assume 1900-1972 was like Rome, overriding Shanks. -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Malta 1973 only - Mar 31 0:00s 1:00 S -Rule Malta 1973 only - Sep 29 0:00s 0 - -Rule Malta 1974 only - Apr 21 0:00s 1:00 S -Rule Malta 1974 only - Sep 16 0:00s 0 - -Rule Malta 1975 1979 - Apr Sun>=15 2:00 1:00 S -Rule Malta 1975 1980 - Sep Sun>=15 2:00 0 - -Rule Malta 1980 only - Mar 31 2:00 1:00 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Malta 0:58:04 - LMT 1893 Nov 2 0:00s # Valletta - 1:00 Italy CE%sT 1973 Mar 31 - 1:00 Malta CE%sT 1981 - 1:00 EU CE%sT - -# Moldova - -# From Stepan Golosunov (2016-03-07): -# the act of the government of the Republic of Moldova Nr. 132 from 1990-05-04 -# http://lex.justice.md/viewdoc.php?action=view&view=doc&id=298782&lang=2 -# ... says that since 1990-05-06 on the territory of the Moldavian SSR -# time would be calculated as the standard time of the second time belt -# plus one hour of the "summer" time. To implement that clocks would be -# adjusted one hour backwards at 1990-05-06 2:00. After that "summer" -# time would be cancelled last Sunday of September at 3:00 and -# reintroduced last Sunday of March at 2:00. - -# From Paul Eggert (2006-03-22): -# A previous version of this database followed Shanks & Pottenger, who write -# that Tiraspol switched to Moscow time on 1992-01-19 at 02:00. -# However, this is most likely an error, as Moldova declared independence -# on 1991-08-27 (the 1992-01-19 date is that of a Russian decree). -# In early 1992 there was large-scale interethnic violence in the area -# and it's possible that some Russophones continued to observe Moscow time. -# But [two people] separately reported via -# Jesper Nørgaard that as of 2001-01-24 Tiraspol was like Chisinau. -# The Tiraspol entry has therefore been removed for now. -# -# From Alexander Krivenyshev (2011-10-17): -# Pridnestrovian Moldavian Republic (PMR, also known as -# "Pridnestrovie") has abolished seasonal clock change (no transition -# to the Winter Time). -# -# News (in Russian): -# http://www.kyivpost.ua/russia/news/pridnestrove-otkazalos-ot-perehoda-na-zimnee-vremya-30954.html -# http://www.allmoldova.com/moldova-news/1249064116.html -# -# The substance of this change (reinstatement of the Tiraspol entry) -# is from a patch from Petr Machata (2011-10-17) -# -# From Tim Parenti (2011-10-19) -# In addition, being situated at +4651+2938 would give Tiraspol -# a pre-1880 LMT offset of 1:58:32. -# -# (which agrees with the earlier entry that had been removed) -# -# From Alexander Krivenyshev (2011-10-26) -# NO need to divide Moldova into two timezones at this point. -# As of today, Transnistria (Pridnestrovie)- Tiraspol reversed its own -# decision to abolish DST this winter. -# Following Moldova and neighboring Ukraine- Transnistria (Pridnestrovie)- -# Tiraspol will go back to winter time on October 30, 2011. -# News from Moldova (in russian): -# https://ru.publika.md/link_317061.html - -# From Roman Tudos (2015-07-02): -# http://lex.justice.md/index.php?action=view&view=doc&lang=1&id=355077 -# From Paul Eggert (2015-07-01): -# The abovementioned official link to IGO1445-868/2014 states that -# 2014-10-26's fallback transition occurred at 03:00 local time. Also, -# https://www.trm.md/en/social/la-30-martie-vom-trece-la-ora-de-vara -# says the 2014-03-30 spring-forward transition was at 02:00 local time. -# Guess that since 1997 Moldova has switched one hour before the EU. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Moldova 1997 max - Mar lastSun 2:00 1:00 S -Rule Moldova 1997 max - Oct lastSun 3:00 0 - - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Chisinau 1:55:20 - LMT 1880 - 1:55 - CMT 1918 Feb 15 # Chisinau MT - 1:44:24 - BMT 1931 Jul 24 # Bucharest MT - 2:00 Romania EE%sT 1940 Aug 15 - 2:00 1:00 EEST 1941 Jul 17 - 1:00 C-Eur CE%sT 1944 Aug 24 - 3:00 Russia MSK/MSD 1990 May 6 2:00 - 2:00 Russia EE%sT 1992 - 2:00 E-Eur EE%sT 1997 -# See Romania commentary for the guessed 1997 transition to EU rules. - 2:00 Moldova EE%sT - -# Monaco -# Shanks & Pottenger give 0:09:20 for Paris Mean Time; go with Howse's -# more precise 0:09:21. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Monaco 0:29:32 - LMT 1891 Mar 15 - 0:09:21 - PMT 1911 Mar 11 # Paris Mean Time - 0:00 France WE%sT 1945 Sep 16 3:00 - 1:00 France CE%sT 1977 - 1:00 EU CE%sT - -# Montenegro -# See Europe/Belgrade. - -# Netherlands - -# Howse writes that the Netherlands' railways used GMT between 1892 and 1940, -# but for other purposes the Netherlands used Amsterdam mean time. - -# However, Robert H. van Gent writes (2001-04-01): -# Howse's statement is only correct up to 1909. From 1909-05-01 (00:00:00 -# Amsterdam mean time) onwards, the whole of the Netherlands (including -# the Dutch railways) was required by law to observe Amsterdam mean time -# (19 minutes 32.13 seconds ahead of GMT). This had already been the -# common practice (except for the railways) for many decades but it was -# not until 1909 when the Dutch government finally defined this by law. -# On 1937-07-01 this was changed to 20 minutes (exactly) ahead of GMT and -# was generally known as Dutch Time ("Nederlandse Tijd"). -# -# (2001-04-08): -# 1892-05-01 was the date when the Dutch railways were by law required to -# observe GMT while the remainder of the Netherlands adhered to the common -# practice of following Amsterdam mean time. -# -# (2001-04-09): -# In 1835 the authorities of the province of North Holland requested the -# municipal authorities of the towns and cities in the province to observe -# Amsterdam mean time but I do not know in how many cases this request was -# actually followed. -# -# From 1852 onwards the Dutch telegraph offices were by law required to -# observe Amsterdam mean time. As the time signals from the observatory of -# Leiden were also distributed by the telegraph system, I assume that most -# places linked up with the telegraph (and railway) system automatically -# adopted Amsterdam mean time. -# -# Although the early Dutch railway companies initially observed a variety -# of times, most of them had adopted Amsterdam mean time by 1858 but it -# was not until 1866 when they were all required by law to observe -# Amsterdam mean time. - -# The data entries before 1945 are taken from -# https://www.staff.science.uu.nl/~gent0113/wettijd/wettijd.htm - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Neth 1916 only - May 1 0:00 1:00 NST # Netherlands Summer Time -Rule Neth 1916 only - Oct 1 0:00 0 AMT # Amsterdam Mean Time -Rule Neth 1917 only - Apr 16 2:00s 1:00 NST -Rule Neth 1917 only - Sep 17 2:00s 0 AMT -Rule Neth 1918 1921 - Apr Mon>=1 2:00s 1:00 NST -Rule Neth 1918 1921 - Sep lastMon 2:00s 0 AMT -Rule Neth 1922 only - Mar lastSun 2:00s 1:00 NST -Rule Neth 1922 1936 - Oct Sun>=2 2:00s 0 AMT -Rule Neth 1923 only - Jun Fri>=1 2:00s 1:00 NST -Rule Neth 1924 only - Mar lastSun 2:00s 1:00 NST -Rule Neth 1925 only - Jun Fri>=1 2:00s 1:00 NST -# From 1926 through 1939 DST began 05-15, except that it was delayed by a week -# in years when 05-15 fell in the Pentecost weekend. -Rule Neth 1926 1931 - May 15 2:00s 1:00 NST -Rule Neth 1932 only - May 22 2:00s 1:00 NST -Rule Neth 1933 1936 - May 15 2:00s 1:00 NST -Rule Neth 1937 only - May 22 2:00s 1:00 NST -Rule Neth 1937 only - Jul 1 0:00 1:00 S -Rule Neth 1937 1939 - Oct Sun>=2 2:00s 0 - -Rule Neth 1938 1939 - May 15 2:00s 1:00 S -Rule Neth 1945 only - Apr 2 2:00s 1:00 S -Rule Neth 1945 only - Sep 16 2:00s 0 - -# -# Amsterdam Mean Time was +00:19:32.13, but the .13 is omitted -# below because the current format requires GMTOFF to be an integer. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Amsterdam 0:19:32 - LMT 1835 - 0:19:32 Neth %s 1937 Jul 1 - 0:20 Neth +0020/+0120 1940 May 16 0:00 - 1:00 C-Eur CE%sT 1945 Apr 2 2:00 - 1:00 Neth CE%sT 1977 - 1:00 EU CE%sT - -# Norway -# http://met.no/met/met_lex/q_u/sommertid.html (2004-01) agrees with Shanks & -# Pottenger. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Norway 1916 only - May 22 1:00 1:00 S -Rule Norway 1916 only - Sep 30 0:00 0 - -Rule Norway 1945 only - Apr 2 2:00s 1:00 S -Rule Norway 1945 only - Oct 1 2:00s 0 - -Rule Norway 1959 1964 - Mar Sun>=15 2:00s 1:00 S -Rule Norway 1959 1965 - Sep Sun>=15 2:00s 0 - -Rule Norway 1965 only - Apr 25 2:00s 1:00 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Oslo 0:43:00 - LMT 1895 Jan 1 - 1:00 Norway CE%sT 1940 Aug 10 23:00 - 1:00 C-Eur CE%sT 1945 Apr 2 2:00 - 1:00 Norway CE%sT 1980 - 1:00 EU CE%sT - -# Svalbard & Jan Mayen - -# From Steffen Thorsen (2001-05-01): -# Although I could not find it explicitly, it seems that Jan Mayen and -# Svalbard have been using the same time as Norway at least since the -# time they were declared as parts of Norway. Svalbard was declared -# as a part of Norway by law of 1925-07-17 no 11, section 4 and Jan -# Mayen by law of 1930-02-27 no 2, section 2. (From -# and -# ). The law/regulation -# for normal/standard time in Norway is from 1894-06-29 no 1 (came -# into operation on 1895-01-01) and Svalbard/Jan Mayen seem to be a -# part of this law since 1925/1930. (From -# ) I have not been -# able to find if Jan Mayen used a different time zone (e.g. -0100) -# before 1930. Jan Mayen has only been "inhabited" since 1921 by -# Norwegian meteorologists and maybe used the same time as Norway ever -# since 1921. Svalbard (Arctic/Longyearbyen) has been inhabited since -# before 1895, and therefore probably changed the local time somewhere -# between 1895 and 1925 (inclusive). - -# From Paul Eggert (2013-09-04): -# -# Actually, Jan Mayen was never occupied by Germany during World War II, -# so it must have diverged from Oslo time during the war, as Oslo was -# keeping Berlin time. -# -# says that the meteorologists -# burned down their station in 1940 and left the island, but returned in -# 1941 with a small Norwegian garrison and continued operations despite -# frequent air attacks from Germans. In 1943 the Americans established a -# radiolocating station on the island, called "Atlantic City". Possibly -# the UT offset changed during the war, but I think it unlikely that -# Jan Mayen used German daylight-saving rules. -# -# Svalbard is more complicated, as it was raided in August 1941 by an -# Allied party that evacuated the civilian population to England (says -# ). The Svalbard FAQ -# says that the Germans were -# expelled on 1942-05-14. However, small parties of Germans did return, -# and according to Wilhelm Dege's book "War North of 80" (1954) -# http://www.ucalgary.ca/UofC/departments/UP/1-55238/1-55238-110-2.html -# the German armed forces at the Svalbard weather station code-named -# Haudegen did not surrender to the Allies until September 1945. -# -# All these events predate our cutoff date of 1970, so use Europe/Oslo -# for these regions. -Link Europe/Oslo Arctic/Longyearbyen - -# Poland - -# The 1919 dates and times can be found in Tygodnik Urzędowy nr 1 (1919-03-20), -# pp 1-2. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Poland 1918 1919 - Sep 16 2:00s 0 - -Rule Poland 1919 only - Apr 15 2:00s 1:00 S -Rule Poland 1944 only - Apr 3 2:00s 1:00 S -# Whitman gives 1944 Nov 30; go with Shanks & Pottenger. -Rule Poland 1944 only - Oct 4 2:00 0 - -# For 1944-1948 Whitman gives the previous day; go with Shanks & Pottenger. -Rule Poland 1945 only - Apr 29 0:00 1:00 S -Rule Poland 1945 only - Nov 1 0:00 0 - -# For 1946 on the source is Kazimierz Borkowski, -# Toruń Center for Astronomy, Dept. of Radio Astronomy, Nicolaus Copernicus U., -# https://www.astro.uni.torun.pl/~kb/Artykuly/U-PA/Czas2.htm#tth_tAb1 -# Thanks to Przemysław Augustyniak (2005-05-28) for this reference. -# He also gives these further references: -# Mon Pol nr 13, poz 162 (1995) -# Druk nr 2180 (2003) -Rule Poland 1946 only - Apr 14 0:00s 1:00 S -Rule Poland 1946 only - Oct 7 2:00s 0 - -Rule Poland 1947 only - May 4 2:00s 1:00 S -Rule Poland 1947 1949 - Oct Sun>=1 2:00s 0 - -Rule Poland 1948 only - Apr 18 2:00s 1:00 S -Rule Poland 1949 only - Apr 10 2:00s 1:00 S -Rule Poland 1957 only - Jun 2 1:00s 1:00 S -Rule Poland 1957 1958 - Sep lastSun 1:00s 0 - -Rule Poland 1958 only - Mar 30 1:00s 1:00 S -Rule Poland 1959 only - May 31 1:00s 1:00 S -Rule Poland 1959 1961 - Oct Sun>=1 1:00s 0 - -Rule Poland 1960 only - Apr 3 1:00s 1:00 S -Rule Poland 1961 1964 - May lastSun 1:00s 1:00 S -Rule Poland 1962 1964 - Sep lastSun 1:00s 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Warsaw 1:24:00 - LMT 1880 - 1:24:00 - WMT 1915 Aug 5 # Warsaw Mean Time - 1:00 C-Eur CE%sT 1918 Sep 16 3:00 - 2:00 Poland EE%sT 1922 Jun - 1:00 Poland CE%sT 1940 Jun 23 2:00 - 1:00 C-Eur CE%sT 1944 Oct - 1:00 Poland CE%sT 1977 - 1:00 W-Eur CE%sT 1988 - 1:00 EU CE%sT - -# Portugal - -# From Paul Eggert (2014-08-11), after a heads-up from Stephen Colebourne: -# According to a Portuguese decree (1911-05-26) -# https://dre.pt/application/dir/pdf1sdip/1911/05/12500/23132313.pdf -# Lisbon was at -0:36:44.68, but switched to GMT on 1912-01-01 at 00:00. -# Round the old offset to -0:36:45. This agrees with Willett.... -# -# From Michael Deckers (2018-02-15): -# article 5 [of the 1911 decree; Deckers's translation] ...: -# These dispositions shall enter into force at the instant at which, -# according to the 2nd article, the civil day January 1, 1912 begins, -# all clocks therefore having to be advanced or set back correspondingly ... - -# From Rui Pedro Salgueiro (1992-11-12): -# Portugal has recently (September, 27) changed timezone -# (from WET to MET or CET) to harmonize with EEC. -# -# Martin Bruckmann (1996-02-29) reports via Peter Ilieve -# that Portugal is reverting to 0:00 by not moving its clocks this spring. -# The new Prime Minister was fed up with getting up in the dark in the winter. -# -# From Paul Eggert (1996-11-12): -# IATA SSIM (1991-09) reports several 1991-09 and 1992-09 transitions -# at 02:00u, not 01:00u. Assume that these are typos. -# IATA SSIM (1991/1992) reports that the Azores were at -1:00. -# IATA SSIM (1993-02) says +0:00; later issues (through 1996-09) say -1:00. -# Guess that the Azores changed to EU rules in 1992 (since that's when Portugal -# harmonized with EU rules), and that they stayed +0:00 that winter. -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -# DSH writes that despite Decree 1,469 (1915), the change to the clocks was not -# done every year, depending on what Spain did, because of railroad schedules. -# Go with Shanks & Pottenger. -Rule Port 1916 only - Jun 17 23:00 1:00 S -# Whitman gives 1916 Oct 31; go with Shanks & Pottenger. -Rule Port 1916 only - Nov 1 1:00 0 - -Rule Port 1917 only - Feb 28 23:00s 1:00 S -Rule Port 1917 1921 - Oct 14 23:00s 0 - -Rule Port 1918 only - Mar 1 23:00s 1:00 S -Rule Port 1919 only - Feb 28 23:00s 1:00 S -Rule Port 1920 only - Feb 29 23:00s 1:00 S -Rule Port 1921 only - Feb 28 23:00s 1:00 S -Rule Port 1924 only - Apr 16 23:00s 1:00 S -Rule Port 1924 only - Oct 14 23:00s 0 - -Rule Port 1926 only - Apr 17 23:00s 1:00 S -Rule Port 1926 1929 - Oct Sat>=1 23:00s 0 - -Rule Port 1927 only - Apr 9 23:00s 1:00 S -Rule Port 1928 only - Apr 14 23:00s 1:00 S -Rule Port 1929 only - Apr 20 23:00s 1:00 S -Rule Port 1931 only - Apr 18 23:00s 1:00 S -# Whitman gives 1931 Oct 8; go with Shanks & Pottenger. -Rule Port 1931 1932 - Oct Sat>=1 23:00s 0 - -Rule Port 1932 only - Apr 2 23:00s 1:00 S -Rule Port 1934 only - Apr 7 23:00s 1:00 S -# Whitman gives 1934 Oct 5; go with Shanks & Pottenger. -Rule Port 1934 1938 - Oct Sat>=1 23:00s 0 - -# Shanks & Pottenger give 1935 Apr 30; go with Whitman. -Rule Port 1935 only - Mar 30 23:00s 1:00 S -Rule Port 1936 only - Apr 18 23:00s 1:00 S -# Whitman gives 1937 Apr 2; go with Shanks & Pottenger. -Rule Port 1937 only - Apr 3 23:00s 1:00 S -Rule Port 1938 only - Mar 26 23:00s 1:00 S -Rule Port 1939 only - Apr 15 23:00s 1:00 S -# Whitman gives 1939 Oct 7; go with Shanks & Pottenger. -Rule Port 1939 only - Nov 18 23:00s 0 - -Rule Port 1940 only - Feb 24 23:00s 1:00 S -# Shanks & Pottenger give 1940 Oct 7; go with Whitman. -Rule Port 1940 1941 - Oct 5 23:00s 0 - -Rule Port 1941 only - Apr 5 23:00s 1:00 S -Rule Port 1942 1945 - Mar Sat>=8 23:00s 1:00 S -Rule Port 1942 only - Apr 25 22:00s 2:00 M # Midsummer -Rule Port 1942 only - Aug 15 22:00s 1:00 S -Rule Port 1942 1945 - Oct Sat>=24 23:00s 0 - -Rule Port 1943 only - Apr 17 22:00s 2:00 M -Rule Port 1943 1945 - Aug Sat>=25 22:00s 1:00 S -Rule Port 1944 1945 - Apr Sat>=21 22:00s 2:00 M -Rule Port 1946 only - Apr Sat>=1 23:00s 1:00 S -Rule Port 1946 only - Oct Sat>=1 23:00s 0 - -Rule Port 1947 1949 - Apr Sun>=1 2:00s 1:00 S -Rule Port 1947 1949 - Oct Sun>=1 2:00s 0 - -# Shanks & Pottenger say DST was observed in 1950; go with Whitman. -# Whitman gives Oct lastSun for 1952 on; go with Shanks & Pottenger. -Rule Port 1951 1965 - Apr Sun>=1 2:00s 1:00 S -Rule Port 1951 1965 - Oct Sun>=1 2:00s 0 - -Rule Port 1977 only - Mar 27 0:00s 1:00 S -Rule Port 1977 only - Sep 25 0:00s 0 - -Rule Port 1978 1979 - Apr Sun>=1 0:00s 1:00 S -Rule Port 1978 only - Oct 1 0:00s 0 - -Rule Port 1979 1982 - Sep lastSun 1:00s 0 - -Rule Port 1980 only - Mar lastSun 0:00s 1:00 S -Rule Port 1981 1982 - Mar lastSun 1:00s 1:00 S -Rule Port 1983 only - Mar lastSun 2:00s 1:00 S -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Lisbon -0:36:45 - LMT 1884 - -0:36:45 - LMT 1912 Jan 1 0:00u # Lisbon MT - 0:00 Port WE%sT 1966 Apr 3 2:00 - 1:00 - CET 1976 Sep 26 1:00 - 0:00 Port WE%sT 1983 Sep 25 1:00s - 0:00 W-Eur WE%sT 1992 Sep 27 1:00s - 1:00 EU CE%sT 1996 Mar 31 1:00u - 0:00 EU WE%sT -# This Zone can be simplified once we assume zic %z. -Zone Atlantic/Azores -1:42:40 - LMT 1884 # Ponta Delgada - -1:54:32 - HMT 1912 Jan 1 2:00u # Horta MT - -2:00 Port -02/-01 1942 Apr 25 22:00s - -2:00 Port +00 1942 Aug 15 22:00s - -2:00 Port -02/-01 1943 Apr 17 22:00s - -2:00 Port +00 1943 Aug 28 22:00s - -2:00 Port -02/-01 1944 Apr 22 22:00s - -2:00 Port +00 1944 Aug 26 22:00s - -2:00 Port -02/-01 1945 Apr 21 22:00s - -2:00 Port +00 1945 Aug 25 22:00s - -2:00 Port -02/-01 1966 Apr 3 2:00 - -1:00 Port -01/+00 1983 Sep 25 1:00s - -1:00 W-Eur -01/+00 1992 Sep 27 1:00s - 0:00 EU WE%sT 1993 Mar 28 1:00u - -1:00 EU -01/+00 -# This Zone can be simplified once we assume zic %z. -Zone Atlantic/Madeira -1:07:36 - LMT 1884 # Funchal - -1:07:36 - FMT 1912 Jan 1 1:00u # Funchal MT - -1:00 Port -01/+00 1942 Apr 25 22:00s - -1:00 Port +01 1942 Aug 15 22:00s - -1:00 Port -01/+00 1943 Apr 17 22:00s - -1:00 Port +01 1943 Aug 28 22:00s - -1:00 Port -01/+00 1944 Apr 22 22:00s - -1:00 Port +01 1944 Aug 26 22:00s - -1:00 Port -01/+00 1945 Apr 21 22:00s - -1:00 Port +01 1945 Aug 25 22:00s - -1:00 Port -01/+00 1966 Apr 3 2:00 - 0:00 Port WE%sT 1983 Sep 25 1:00s - 0:00 EU WE%sT - -# Romania -# -# From Paul Eggert (1999-10-07): -# Nine O'clock -# (1998-10-23) reports that the switch occurred at -# 04:00 local time in fall 1998. For lack of better info, -# assume that Romania and Moldova switched to EU rules in 1997, -# the same year as Bulgaria. -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Romania 1932 only - May 21 0:00s 1:00 S -Rule Romania 1932 1939 - Oct Sun>=1 0:00s 0 - -Rule Romania 1933 1939 - Apr Sun>=2 0:00s 1:00 S -Rule Romania 1979 only - May 27 0:00 1:00 S -Rule Romania 1979 only - Sep lastSun 0:00 0 - -Rule Romania 1980 only - Apr 5 23:00 1:00 S -Rule Romania 1980 only - Sep lastSun 1:00 0 - -Rule Romania 1991 1993 - Mar lastSun 0:00s 1:00 S -Rule Romania 1991 1993 - Sep lastSun 0:00s 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Bucharest 1:44:24 - LMT 1891 Oct - 1:44:24 - BMT 1931 Jul 24 # Bucharest MT - 2:00 Romania EE%sT 1981 Mar 29 2:00s - 2:00 C-Eur EE%sT 1991 - 2:00 Romania EE%sT 1994 - 2:00 E-Eur EE%sT 1997 - 2:00 EU EE%sT - - -# Russia - -# From Alexander Krivenyshev (2011-09-15): -# Based on last Russian Government Decree No. 725 on August 31, 2011 -# (Government document -# http://www.government.ru/gov/results/16355/print/ -# in Russian) -# there are few corrections have to be made for some Russian time zones... -# All updated Russian Time Zones were placed in table and translated to English -# by WorldTimeZone.com at the link below: -# http://www.worldtimezone.com/dst_news/dst_news_russia36.htm - -# From Sanjeev Gupta (2011-09-27): -# Scans of [Decree No. 23 of January 8, 1992] are available at: -# http://government.consultant.ru/page.aspx?1223966 -# They are in Cyrillic letters (presumably Russian). - -# From Arthur David Olson (2012-05-09): -# Regarding the instant when clocks in time-zone-shifting parts of Russia -# changed in September 2011: -# -# One source is -# http://government.ru/gov/results/16355/ -# which, according to translate.google.com, begins "Decree of August 31, -# 2011 No. 725" and contains no other dates or "effective date" information. -# -# Another source is -# https://rg.ru/2011/09/06/chas-zona-dok.html -# which, according to translate.google.com, begins "Resolution of the -# Government of the Russian Federation on August 31, 2011 N 725" and also -# contains "Date first official publication: September 6, 2011 Posted on: -# in the 'RG' - Federal Issue No. 5573 September 6, 2011" but which -# does not contain any "effective date" information. -# -# Another source is -# https://en.wikipedia.org/wiki/Oymyakonsky_District#cite_note-RuTime-7 -# which, in note 8, contains "Resolution No. 725 of August 31, 2011... -# Effective as of after 7 days following the day of the official publication" -# but which does not contain any reference to September 6, 2011. -# -# The Wikipedia article refers to -# http://base.consultant.ru/cons/cgi/online.cgi?req=doc;base=LAW;n=118896 -# which seems to copy the text of the government.ru page. -# -# Tobias Conradi combines Wikipedia's -# "as of after 7 days following the day of the official publication" -# with www.rg.ru's "Date of first official publication: September 6, 2011" to -# get September 13, 2011 as the cutover date (unusually, a Tuesday, as Tobias -# Conradi notes). -# -# None of the sources indicates a time of day for changing clocks. -# -# Go with 2011-09-13 0:00s. - -# From Alexander Krivenyshev (2014-07-01): -# According to the Russian news (ITAR-TASS News Agency) -# http://en.itar-tass.com/russia/738562 -# the State Duma has approved ... the draft bill on returning to -# winter time standard and return Russia 11 time zones. The new -# regulations will come into effect on October 26, 2014 at 02:00 ... -# http://asozd2.duma.gov.ru/main.nsf/%28Spravka%29?OpenAgent&RN=431985-6&02 -# Here is a link where we put together table (based on approved Bill N -# 431985-6) with proposed 11 Russian time zones and corresponding -# areas/cities/administrative centers in the Russian Federation (in English): -# http://www.worldtimezone.com/dst_news/dst_news_russia65.html -# -# From Alexander Krivenyshev (2014-07-22): -# Putin signed the Federal Law 431985-6 ... (in Russian) -# http://itar-tass.com/obschestvo/1333711 -# http://www.pravo.gov.ru:8080/page.aspx?111660 -# http://www.kremlin.ru/acts/46279 -# From October 26, 2014 the new Russian time zone map will look like this: -# http://www.worldtimezone.com/dst_news/dst_news_russia-map-2014-07.html - -# From Paul Eggert (2006-03-22): -# Moscow time zone abbreviations after 1919-07-01, and Moscow rules after 1991, -# are from Andrey A. Chernov. The rest is from Shanks & Pottenger, -# except we follow Chernov's report that 1992 DST transitions were Sat -# 23:00, not Sun 02:00s. -# -# From Stanislaw A. Kuzikowski (1994-06-29): -# But now it is some months since Novosibirsk is 3 hours ahead of Moscow! -# I do not know why they have decided to make this change; -# as far as I remember it was done exactly during winter->summer switching -# so we (Novosibirsk) simply did not switch. -# -# From Andrey A. Chernov (1996-10-04): -# 'MSK' and 'MSD' were born and used initially on Moscow computers with -# UNIX-like OSes by several developer groups (e.g. Demos group, Kiae group).... -# The next step was the UUCP network, the Relcom predecessor -# (used mainly for mail), and MSK/MSD was actively used there. -# -# From Chris Carrier (1996-10-30): -# According to a friend of mine who rode the Trans-Siberian Railroad from -# Moscow to Irkutsk in 1995, public air and rail transport in Russia ... -# still follows Moscow time, no matter where in Russia it is located. -# -# For Grozny, Chechnya, we have the following story from -# John Daniszewski, "Scavengers in the Rubble", Los Angeles Times (2001-02-07): -# News - often false - is spread by word of mouth. A rumor that it was -# time to move the clocks back put this whole city out of sync with -# the rest of Russia for two weeks - even soldiers stationed here began -# enforcing curfew at the wrong time. -# -# From Gwillim Law (2001-06-05): -# There's considerable evidence that Sakhalin Island used to be in -# UTC+11, and has changed to UTC+10, in this decade. I start with the -# SSIM, which listed Yuzhno-Sakhalinsk in zone RU10 along with Magadan -# until February 1997, and then in RU9 with Khabarovsk and Vladivostok -# since September 1997.... Although the Kuril Islands are -# administratively part of Sakhalin oblast', they appear to have -# remained on UTC+11 along with Magadan. - -# From Tim Parenti (2014-07-06): -# The comments detailing the coverage of each Russian zone are meant to assist -# with maintenance only and represent our best guesses as to which regions -# are covered by each zone. They are not meant to be taken as an authoritative -# listing. The region codes listed come from -# https://en.wikipedia.org/w/?title=Federal_subjects_of_Russia&oldid=611810498 -# and are used for convenience only; no guarantees are made regarding their -# future stability. ISO 3166-2:RU codes are also listed for first-level -# divisions where available. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] - - -# From Tim Parenti (2014-07-03): -# Europe/Kaliningrad covers... -# 39 RU-KGD Kaliningrad Oblast - -# From Paul Eggert (2016-03-18): -# The 1989 transition is from USSR act No. 227 (1989-03-14). - -# From Stepan Golosunov (2016-03-07): -# http://www.rgo.ru/ru/kaliningradskoe-oblastnoe-otdelenie/ob-otdelenii/publikacii/kak-nam-zhilos-bez-letnego-vremeni -# confirms that the 1989 change to Moscow-1 was implemented. -# (The article, though, is misattributed to 1990 while saying that -# summer->winter transition would be done on the 24 of September. But -# 1990-09-24 was Monday, while 1989-09-24 was Sunday as expected.) -# ... -# http://www.kaliningradka.ru/site_pc/cherez/index.php?ELEMENT_ID=40091 -# says that Kaliningrad switched to Moscow-1 on 1989-03-26, avoided -# at the last moment switch to Moscow-1 on 1991-03-31, switched to -# Moscow on 1991-11-03, switched to Moscow-1 on 1992-01-19. - -Zone Europe/Kaliningrad 1:22:00 - LMT 1893 Apr - 1:00 C-Eur CE%sT 1945 - 2:00 Poland CE%sT 1946 - 3:00 Russia MSK/MSD 1989 Mar 26 2:00s - 2:00 Russia EE%sT 2011 Mar 27 2:00s - 3:00 - +03 2014 Oct 26 2:00s - 2:00 - EET - - -# From Paul Eggert (2016-02-21), per Tim Parenti (2014-07-03) and -# Oscar van Vlijmen (2001-08-25): -# Europe/Moscow covers... -# 01 RU-AD Adygea, Republic of -# 05 RU-DA Dagestan, Republic of -# 06 RU-IN Ingushetia, Republic of -# 07 RU-KB Kabardino-Balkar Republic -# 08 RU-KL Kalmykia, Republic of -# 09 RU-KC Karachay-Cherkess Republic -# 10 RU-KR Karelia, Republic of -# 11 RU-KO Komi Republic -# 12 RU-ME Mari El Republic -# 13 RU-MO Mordovia, Republic of -# 15 RU-SE North Ossetia-Alania, Republic of -# 16 RU-TA Tatarstan, Republic of -# 20 RU-CE Chechen Republic -# 21 RU-CU Chuvash Republic -# 23 RU-KDA Krasnodar Krai -# 26 RU-STA Stavropol Krai -# 29 RU-ARK Arkhangelsk Oblast -# 31 RU-BEL Belgorod Oblast -# 32 RU-BRY Bryansk Oblast -# 33 RU-VLA Vladimir Oblast -# 35 RU-VLG Vologda Oblast -# 36 RU-VOR Voronezh Oblast -# 37 RU-IVA Ivanovo Oblast -# 40 RU-KLU Kaluga Oblast -# 44 RU-KOS Kostroma Oblast -# 46 RU-KRS Kursk Oblast -# 47 RU-LEN Leningrad Oblast -# 48 RU-LIP Lipetsk Oblast -# 50 RU-MOS Moscow Oblast -# 51 RU-MUR Murmansk Oblast -# 52 RU-NIZ Nizhny Novgorod Oblast -# 53 RU-NGR Novgorod Oblast -# 57 RU-ORL Oryol Oblast -# 58 RU-PNZ Penza Oblast -# 60 RU-PSK Pskov Oblast -# 61 RU-ROS Rostov Oblast -# 62 RU-RYA Ryazan Oblast -# 67 RU-SMO Smolensk Oblast -# 68 RU-TAM Tambov Oblast -# 69 RU-TVE Tver Oblast -# 71 RU-TUL Tula Oblast -# 76 RU-YAR Yaroslavl Oblast -# 77 RU-MOW Moscow -# 78 RU-SPE Saint Petersburg -# 83 RU-NEN Nenets Autonomous Okrug - -# From Paul Eggert (2016-08-23): -# The Soviets switched to UT-based time in 1919. Decree No. 59 -# (1919-02-08) http://istmat.info/node/35567 established UT-based time -# zones, and Decree No. 147 (1919-03-29) http://istmat.info/node/35854 -# specified a transition date of 1919-07-01, apparently at 00:00 UT. -# No doubt only the Soviet-controlled regions switched on that date; -# later transitions to UT-based time in other parts of Russia are -# taken from what appear to be guesses by Shanks. -# (Thanks to Alexander Belopolsky for pointers to the decrees.) - -# From Stepan Golosunov (2016-03-07): -# 11. Regions-violators, 1981-1982. -# Wikipedia refers to -# http://maps.monetonos.ru/maps/raznoe/Old_Maps/Old_Maps/Articles/022/3_1981.html -# http://besp.narod.ru/nauka_1981_3.htm -# -# The second link provides two articles scanned from the Nauka i Zhizn -# magazine No. 3, 1981 and a scan of the short article attributed to -# the Trud newspaper from February 1982. The first link provides the -# same Nauka i Zhizn articles converted to the text form (but misses -# time belt changes map). -# -# The second Nauka i Zhizn article says that in addition to -# introduction of summer time on 1981-04-01 there are some time belt -# border changes on 1981-10-01, mostly affecting Nenets Autonomous -# Okrug, Krasnoyarsk Krai, Yakutia, Magadan Oblast and Chukotka -# according to the provided map (colored one). In addition to that -# "time violators" (regions which were not using rules of the time -# belts in which they were located) would not be moving off the DST on -# 1981-10-01 to restore the decree time usage. (Komi ASSR was -# supposed to repeat that move in October 1982 to account for the 2 -# hour difference.) Map depicting "time violators" before 1981-10-01 -# is also provided. -# -# The article from Trud says that 1981-10-01 changes caused problems -# and some territories would be moved to pre-1981-10-01 time by not -# moving to summer time on 1982-04-01. Namely: Dagestan, -# Kabardino-Balkar, Kalmyk, Komi, Mari, Mordovian, North Ossetian, -# Tatar, Chechen-Ingush and Chuvash ASSR, Krasnodar and Stavropol -# krais, Arkhangelsk, Vladimir, Vologda, Voronezh, Gorky, Ivanovo, -# Kostroma, Lipetsk, Penza, Rostov, Ryazan, Tambov, Tyumen and -# Yaroslavl oblasts, Nenets and Evenk autonomous okrugs, Khatangsky -# district of Taymyr Autonomous Okrug. As a result Evenk Autonomous -# Okrug and Khatangsky district of Taymyr Autonomous Okrug would end -# up on Moscow+4, Tyumen Oblast on Moscow+2 and the rest on Moscow -# time. -# -# http://astrozet.net/files/Zones/DOC/RU/1980-925.txt -# attributes the 1982 changes to the Act of the Council of Ministers -# of the USSR No. 126 from 18.02.1982. 1980-925.txt also adds -# Udmurtia to the list of affected territories and lists Khatangsky -# district separately from Taymyr Autonomous Okrug. Probably erroneously. -# -# The affected territories are currently listed under Europe/Moscow, -# Asia/Yekaterinburg and Asia/Krasnoyarsk. -# -# 12. Udmurtia -# The fact that Udmurtia is depicted as a violator in the Nauka i -# Zhizn article hints at Izhevsk being on different time from -# Kuybyshev before 1981-10-01. Udmurtia is not mentioned in the 1989 act. -# http://astrozet.net/files/Zones/DOC/RU/1980-925.txt -# implies Udmurtia was on Moscow time after 1982-04-01. -# Wikipedia implies Udmurtia being on Moscow+1 until 1991. -# -# ... -# -# All Russian zones are supposed to have by default a -1 change at -# 1991-03-31 2:00 (cancellation of the decree time in the USSR) and a +1 -# change at 1992-01-19 2:00 (restoration of the decree time in Russia). -# -# There were some exceptions, though. -# Wikipedia says newspapers listed Astrakhan, Saratov, Kirov, Volgograd, -# Izhevsk, Grozny, Kazan and Samara as such exceptions for the 1992 -# change. (Different newspapers providing different lists. And some -# lists found in the internet are quite wild.) -# -# And apparently some exceptions were reverted in the last moment. -# http://www.kaliningradka.ru/site_pc/cherez/index.php?ELEMENT_ID=40091 -# says that Kaliningrad decided not to be an exception 2 days before the -# 1991-03-31 switch and one person at -# https://izhevsk.ru/forum_light_message/50/682597-m8369040.html -# says he remembers that Samara opted out of the 1992-01-19 exception -# 2 days before the switch. -# -# -# From Paul Eggert (2016-03-18): -# Given the above, we appear to be missing some Zone entries for the -# chaotic early 1980s in Russia. It's not clear what these entries -# should be. For now, sweep this under the rug and just document the -# time in Moscow. - -# From Vladimir Karpinsky (2014-07-08): -# LMT in Moscow (before Jul 3, 1916) is 2:30:17, that was defined by Moscow -# Observatory (coordinates: 55° 45' 29.70", 37° 34' 05.30").... -# LMT in Moscow since Jul 3, 1916 is 2:31:01 as a result of new standard. -# (The info is from the book by Byalokoz ... p. 18.) -# The time in St. Petersburg as capital of Russia was defined by -# Pulkov observatory, near St. Petersburg. In 1916 LMT Moscow -# was synchronized with LMT St. Petersburg (+30 minutes), (Pulkov observatory -# coordinates: 59° 46' 18.70", 30° 19' 40.70") so 30° 19' 40.70" > -# 2h01m18.7s = 2:01:19. LMT Moscow = LMT St.Petersburg + 30m 2:01:19 + 0:30 = -# 2:31:19 ... -# -# From Paul Eggert (2014-07-08): -# Milne does not list Moscow, but suggests that its time might be listed in -# Résumés mensuels et annuels des observations météorologiques (1895). -# Presumably this is OCLC 85825704, a journal published with parallel text in -# Russian and French. This source has not been located; go with Karpinsky. - -Zone Europe/Moscow 2:30:17 - LMT 1880 - 2:30:17 - MMT 1916 Jul 3 # Moscow Mean Time - 2:31:19 Russia %s 1919 Jul 1 0:00u - 3:00 Russia %s 1921 Oct - 3:00 Russia MSK/MSD 1922 Oct - 2:00 - EET 1930 Jun 21 - 3:00 Russia MSK/MSD 1991 Mar 31 2:00s - 2:00 Russia EE%sT 1992 Jan 19 2:00s - 3:00 Russia MSK/MSD 2011 Mar 27 2:00s - 4:00 - MSK 2014 Oct 26 2:00s - 3:00 - MSK - - -# From Paul Eggert (2016-12-06): -# Europe/Simferopol covers Crimea. - -Zone Europe/Simferopol 2:16:24 - LMT 1880 - 2:16 - SMT 1924 May 2 # Simferopol Mean T - 2:00 - EET 1930 Jun 21 - 3:00 - MSK 1941 Nov - 1:00 C-Eur CE%sT 1944 Apr 13 - 3:00 Russia MSK/MSD 1990 - 3:00 - MSK 1990 Jul 1 2:00 - 2:00 - EET 1992 -# Central Crimea used Moscow time 1994/1997. -# -# From Paul Eggert (2006-03-22): -# The _Economist_ (1994-05-28, p 45) reports that central Crimea switched -# from Kiev to Moscow time sometime after the January 1994 elections. -# Shanks (1999) says "date of change uncertain", but implies that it happened -# sometime between the 1994 DST switches. Shanks & Pottenger simply say -# 1994-09-25 03:00, but that can't be right. For now, guess it -# changed in May. - 2:00 E-Eur EE%sT 1994 May -# From IATA SSIM (1994/1997), which also says that Kerch is still like Kiev. - 3:00 E-Eur MSK/MSD 1996 Mar 31 0:00s - 3:00 1:00 MSD 1996 Oct 27 3:00s -# IATA SSIM (1997-09) says Crimea switched to EET/EEST. -# Assume it happened in March by not changing the clocks. - 3:00 Russia MSK/MSD 1997 - 3:00 - MSK 1997 Mar lastSun 1:00u -# From Alexander Krivenyshev (2014-03-17): -# time change at 2:00 (2am) on March 30, 2014 -# https://vz.ru/news/2014/3/17/677464.html -# From Paul Eggert (2014-03-30): -# Simferopol and Sevastopol reportedly changed their central town clocks -# late the previous day, but this appears to have been ceremonial -# and the discrepancies are small enough to not worry about. - 2:00 EU EE%sT 2014 Mar 30 2:00 - 4:00 - MSK 2014 Oct 26 2:00s - 3:00 - MSK - - -# From Paul Eggert (2016-03-18): -# Europe/Astrakhan covers: -# 30 RU-AST Astrakhan Oblast -# -# The 1989 transition is from USSR act No. 227 (1989-03-14). - -# From Alexander Krivenyshev (2016-01-12): -# On February 10, 2016 Astrakhan Oblast got approval by the Federation -# Council to change its time zone to UTC+4 (from current UTC+3 Moscow time).... -# This Federal Law shall enter into force on 27 March 2016 at 02:00. -# From Matt Johnson (2016-03-09): -# http://publication.pravo.gov.ru/Document/View/0001201602150056 - -Zone Europe/Astrakhan 3:12:12 - LMT 1924 May - 3:00 - +03 1930 Jun 21 - 4:00 Russia +04/+05 1989 Mar 26 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s - 4:00 - +04 1992 Mar 29 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 2014 Oct 26 2:00s - 3:00 - +03 2016 Mar 27 2:00s - 4:00 - +04 - -# From Paul Eggert (2016-11-11): -# Europe/Volgograd covers: -# 34 RU-VGG Volgograd Oblast -# The 1988 transition is from USSR act No. 5 (1988-01-04). - -Zone Europe/Volgograd 2:57:40 - LMT 1920 Jan 3 - 3:00 - +03 1930 Jun 21 - 4:00 - +04 1961 Nov 11 - 4:00 Russia +04/+05 1988 Mar 27 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s - 4:00 - +04 1992 Mar 29 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 2014 Oct 26 2:00s - 3:00 - +03 - -# From Paul Eggert (2016-11-11): -# Europe/Saratov covers: -# 64 RU-SAR Saratov Oblast - -# From Yuri Konotopov (2016-11-11): -# Dec 4, 2016 02:00 UTC+3.... Saratov Region's local time will be ... UTC+4. -# From Stepan Golosunov (2016-11-11): -# ... Byalokoz listed Saratov on 03:04:18. -# From Stepan Golosunov (2016-11-22): -# http://publication.pravo.gov.ru/Document/View/0001201611220031 - -Zone Europe/Saratov 3:04:18 - LMT 1919 Jul 1 0:00u - 3:00 - +03 1930 Jun 21 - 4:00 Russia +04/+05 1988 Mar 27 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s - 4:00 - +04 1992 Mar 29 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 2014 Oct 26 2:00s - 3:00 - +03 2016 Dec 4 2:00s - 4:00 - +04 - -# From Paul Eggert (2016-03-18): -# Europe/Kirov covers: -# 43 RU-KIR Kirov Oblast -# The 1989 transition is from USSR act No. 227 (1989-03-14). -# -Zone Europe/Kirov 3:18:48 - LMT 1919 Jul 1 0:00u - 3:00 - +03 1930 Jun 21 - 4:00 Russia +04/+05 1989 Mar 26 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s - 4:00 - +04 1992 Mar 29 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 2014 Oct 26 2:00s - 3:00 - +03 - -# From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): -# Europe/Samara covers... -# 18 RU-UD Udmurt Republic -# 63 RU-SAM Samara Oblast - -# From Paul Eggert (2016-03-18): -# Byalokoz 1919 says Samara was 3:20:20. -# The 1989 transition is from USSR act No. 227 (1989-03-14). - -Zone Europe/Samara 3:20:20 - LMT 1919 Jul 1 0:00u - 3:00 - +03 1930 Jun 21 - 4:00 - +04 1935 Jan 27 - 4:00 Russia +04/+05 1989 Mar 26 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s - 2:00 Russia +02/+03 1991 Sep 29 2:00s - 3:00 - +03 1991 Oct 20 3:00 - 4:00 Russia +04/+05 2010 Mar 28 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 - -# From Paul Eggert (2016-03-18): -# Europe/Ulyanovsk covers: -# 73 RU-ULY Ulyanovsk Oblast - -# The 1989 transition is from USSR act No. 227 (1989-03-14). - -# From Alexander Krivenyshev (2016-02-17): -# Ulyanovsk ... on their way to change time zones by March 27, 2016 at 2am. -# Ulyanovsk Oblast ... from MSK to MSK+1 (UTC+3 to UTC+4) ... -# 920582-6 ... 02/17/2016 The State Duma passed the bill in the first reading. -# From Matt Johnson (2016-03-09): -# http://publication.pravo.gov.ru/Document/View/0001201603090051 - -Zone Europe/Ulyanovsk 3:13:36 - LMT 1919 Jul 1 0:00u - 3:00 - +03 1930 Jun 21 - 4:00 Russia +04/+05 1989 Mar 26 2:00s - 3:00 Russia +03/+04 1991 Mar 31 2:00s - 2:00 Russia +02/+03 1992 Jan 19 2:00s - 3:00 Russia +03/+04 2011 Mar 27 2:00s - 4:00 - +04 2014 Oct 26 2:00s - 3:00 - +03 2016 Mar 27 2:00s - 4:00 - +04 - -# From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): -# Asia/Yekaterinburg covers... -# 02 RU-BA Bashkortostan, Republic of -# 90 RU-PER Perm Krai -# 45 RU-KGN Kurgan Oblast -# 56 RU-ORE Orenburg Oblast -# 66 RU-SVE Sverdlovsk Oblast -# 72 RU-TYU Tyumen Oblast -# 74 RU-CHE Chelyabinsk Oblast -# 86 RU-KHM Khanty-Mansi Autonomous Okrug - Yugra -# 89 RU-YAN Yamalo-Nenets Autonomous Okrug -# -# Note: Effective 2005-12-01, (59) Perm Oblast and (81) Komi-Permyak -# Autonomous Okrug merged to form (90, RU-PER) Perm Krai. - -# Milne says Yekaterinburg was 4:02:32.9; round to nearest. -# Byalokoz 1919 says its provincial time was based on Perm, at 3:45:05. -# Assume it switched on 1916-07-03, the time of the new standard. -# The 1919 and 1930 transitions are from Shanks. - -Zone Asia/Yekaterinburg 4:02:33 - LMT 1916 Jul 3 - 3:45:05 - PMT 1919 Jul 15 4:00 - 4:00 - +04 1930 Jun 21 - 5:00 Russia +05/+06 1991 Mar 31 2:00s - 4:00 Russia +04/+05 1992 Jan 19 2:00s - 5:00 Russia +05/+06 2011 Mar 27 2:00s - 6:00 - +06 2014 Oct 26 2:00s - 5:00 - +05 - - -# From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): -# Asia/Omsk covers... -# 55 RU-OMS Omsk Oblast - -# Byalokoz 1919 says Omsk was 4:53:30. - -Zone Asia/Omsk 4:53:30 - LMT 1919 Nov 14 - 5:00 - +05 1930 Jun 21 - 6:00 Russia +06/+07 1991 Mar 31 2:00s - 5:00 Russia +05/+06 1992 Jan 19 2:00s - 6:00 Russia +06/+07 2011 Mar 27 2:00s - 7:00 - +07 2014 Oct 26 2:00s - 6:00 - +06 - -# From Paul Eggert (2016-02-22): -# Asia/Barnaul covers: -# 04 RU-AL Altai Republic -# 22 RU-ALT Altai Krai - -# Data before 1991 are from Shanks & Pottenger. - -# From Stepan Golosunov (2016-03-07): -# Letter of Bank of Russia from 1995-05-25 -# http://www.bestpravo.ru/rossijskoje/lj-akty/y3a.htm -# suggests that Altai Republic transitioned to Moscow+3 on -# 1995-05-28. -# -# https://regnum.ru/news/society/1957270.html -# has some historical data for Altai Krai: -# before 1957: west part on UT+6, east on UT+7 -# after 1957: UT+7 -# since 1995: UT+6 -# http://barnaul.rusplt.ru/index/pochemu_altajskij_kraj_okazalsja_v_neprivychnom_chasovom_pojase-17648.html -# confirms that and provides more details including 1995-05-28 transition date. - -# From Alexander Krivenyshev (2016-02-17): -# Altai Krai and Altai Republic on their way to change time zones -# by March 27, 2016 at 2am.... -# Altai Republic / Gorno-Altaysk MSK+3 to MSK+4 (UTC+6 to UTC+7) ... -# Altai Krai / Barnaul MSK+3 to MSK+4 (UTC+6 to UTC+7) -# From Matt Johnson (2016-03-09): -# http://publication.pravo.gov.ru/Document/View/0001201603090043 -# http://publication.pravo.gov.ru/Document/View/0001201603090038 - -Zone Asia/Barnaul 5:35:00 - LMT 1919 Dec 10 - 6:00 - +06 1930 Jun 21 - 7:00 Russia +07/+08 1991 Mar 31 2:00s - 6:00 Russia +06/+07 1992 Jan 19 2:00s - 7:00 Russia +07/+08 1995 May 28 - 6:00 Russia +06/+07 2011 Mar 27 2:00s - 7:00 - +07 2014 Oct 26 2:00s - 6:00 - +06 2016 Mar 27 2:00s - 7:00 - +07 - -# From Paul Eggert (2016-03-18): -# Asia/Novosibirsk covers: -# 54 RU-NVS Novosibirsk Oblast - -# From Stepan Golosunov (2016-05-30): -# http://asozd2.duma.gov.ru/main.nsf/(Spravka)?OpenAgent&RN=1085784-6 -# moves Novosibirsk oblast from UTC+6 to UTC+7. -# From Stepan Golosunov (2016-07-04): -# The law was signed yesterday and published today on -# http://publication.pravo.gov.ru/Document/View/0001201607040064 - -Zone Asia/Novosibirsk 5:31:40 - LMT 1919 Dec 14 6:00 - 6:00 - +06 1930 Jun 21 - 7:00 Russia +07/+08 1991 Mar 31 2:00s - 6:00 Russia +06/+07 1992 Jan 19 2:00s - 7:00 Russia +07/+08 1993 May 23 # say Shanks & P. - 6:00 Russia +06/+07 2011 Mar 27 2:00s - 7:00 - +07 2014 Oct 26 2:00s - 6:00 - +06 2016 Jul 24 2:00s - 7:00 - +07 - -# From Paul Eggert (2016-03-18): -# Asia/Tomsk covers: -# 70 RU-TOM Tomsk Oblast - -# From Stepan Golosunov (2016-03-24): -# Byalokoz listed Tomsk at 5:39:51. - -# From Stanislaw A. Kuzikowski (1994-06-29): -# Tomsk is still 4 hours ahead of Moscow. - -# From Stepan Golosunov (2016-03-19): -# http://pravo.gov.ru/proxy/ips/?docbody=&nd=102075743 -# (fifth time belt being UTC+5+1(decree time) -# / UTC+5+1(decree time)+1(summer time)) ... -# Note that time belts (numbered from 2 (Moscow) to 12 according to their -# GMT/UTC offset and having too many exceptions like regions formally -# belonging to one belt but using time from another) were replaced -# with time zones in 2011 with different numbering (there was a -# 2-hour gap between second and third zones in 2011-2014). - -# From Stepan Golosunov (2016-04-12): -# http://asozd2.duma.gov.ru/main.nsf/(SpravkaNew)?OpenAgent&RN=1006865-6 -# This bill was approved in the first reading today. It moves Tomsk oblast -# from UTC+6 to UTC+7 and is supposed to come into effect on 2016-05-29 at -# 2:00. The bill needs to be approved in the second and the third readings by -# the State Duma, approved by the Federation Council, signed by the President -# and published to become a law. Minor changes in the text are to be expected -# before the second reading (references need to be updated to account for the -# recent changes). -# -# Judging by the ultra-short one-day amendments period, recent similar laws, -# the State Duma schedule and the Federation Council schedule -# http://www.duma.gov.ru/legislative/planning/day-shedule/por_vesna_2016/ -# http://council.gov.ru/activity/meetings/schedule/63303 -# I speculate that the final text of the bill will be proposed tomorrow, the -# bill will be approved in the second and the third readings on Friday, -# approved by the Federation Council on 2016-04-20, signed by the President and -# published as a law around 2016-04-26. - -# From Matt Johnson (2016-04-26): -# http://publication.pravo.gov.ru/Document/View/0001201604260048 - -Zone Asia/Tomsk 5:39:51 - LMT 1919 Dec 22 - 6:00 - +06 1930 Jun 21 - 7:00 Russia +07/+08 1991 Mar 31 2:00s - 6:00 Russia +06/+07 1992 Jan 19 2:00s - 7:00 Russia +07/+08 2002 May 1 3:00 - 6:00 Russia +06/+07 2011 Mar 27 2:00s - 7:00 - +07 2014 Oct 26 2:00s - 6:00 - +06 2016 May 29 2:00s - 7:00 - +07 - - -# From Tim Parenti (2014-07-03): -# Asia/Novokuznetsk covers... -# 42 RU-KEM Kemerovo Oblast - -# From Alexander Krivenyshev (2009-10-13): -# Kemerovo oblast' (Kemerovo region) in Russia will change current time zone on -# March 28, 2010: -# from current Russia Zone 6 - Krasnoyarsk Time Zone (KRA) UTC +0700 -# to Russia Zone 5 - Novosibirsk Time Zone (NOV) UTC +0600 -# -# This is according to Government of Russia decree No. 740, on September -# 14, 2009 "Application in the territory of the Kemerovo region the Fifth -# time zone." ("Russia Zone 5" or old "USSR Zone 5" is GMT +0600) -# -# Russian Government web site (Russian language) -# http://www.government.ru/content/governmentactivity/rfgovernmentdecisions/archive/2009/09/14/991633.htm -# or Russian-English translation by WorldTimeZone.com with reference -# map to local region and new Russia Time Zone map after March 28, 2010 -# http://www.worldtimezone.com/dst_news/dst_news_russia03.html -# -# Thus, when Russia will switch to DST on the night of March 28, 2010 -# Kemerovo region (Kemerovo oblast') will not change the clock. - -# From Tim Parenti (2014-07-02), per Alexander Krivenyshev (2014-07-02): -# The Kemerovo region will remain at UTC+7 through the 2014-10-26 change, thus -# realigning itself with KRAT. - -Zone Asia/Novokuznetsk 5:48:48 - LMT 1924 May 1 - 6:00 - +06 1930 Jun 21 - 7:00 Russia +07/+08 1991 Mar 31 2:00s - 6:00 Russia +06/+07 1992 Jan 19 2:00s - 7:00 Russia +07/+08 2010 Mar 28 2:00s - 6:00 Russia +06/+07 2011 Mar 27 2:00s - 7:00 - +07 - -# From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): -# Asia/Krasnoyarsk covers... -# 17 RU-TY Tuva Republic -# 19 RU-KK Khakassia, Republic of -# 24 RU-KYA Krasnoyarsk Krai -# -# Note: Effective 2007-01-01, (88) Evenk Autonomous Okrug and (84) Taymyr -# Autonomous Okrug were merged into (24, RU-KYA) Krasnoyarsk Krai. - -# Byalokoz 1919 says Krasnoyarsk was 6:11:26. - -Zone Asia/Krasnoyarsk 6:11:26 - LMT 1920 Jan 6 - 6:00 - +06 1930 Jun 21 - 7:00 Russia +07/+08 1991 Mar 31 2:00s - 6:00 Russia +06/+07 1992 Jan 19 2:00s - 7:00 Russia +07/+08 2011 Mar 27 2:00s - 8:00 - +08 2014 Oct 26 2:00s - 7:00 - +07 - - -# From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): -# Asia/Irkutsk covers... -# 03 RU-BU Buryatia, Republic of -# 38 RU-IRK Irkutsk Oblast -# -# Note: Effective 2008-01-01, (85) Ust-Orda Buryat Autonomous Okrug was -# merged into (38, RU-IRK) Irkutsk Oblast. - -# Milne 1899 says Irkutsk was 6:57:15. -# Byalokoz 1919 says Irkutsk was 6:57:05. -# Go with Byalokoz. - -Zone Asia/Irkutsk 6:57:05 - LMT 1880 - 6:57:05 - IMT 1920 Jan 25 # Irkutsk Mean Time - 7:00 - +07 1930 Jun 21 - 8:00 Russia +08/+09 1991 Mar 31 2:00s - 7:00 Russia +07/+08 1992 Jan 19 2:00s - 8:00 Russia +08/+09 2011 Mar 27 2:00s - 9:00 - +09 2014 Oct 26 2:00s - 8:00 - +08 - - -# From Tim Parenti (2014-07-06): -# Asia/Chita covers... -# 92 RU-ZAB Zabaykalsky Krai -# -# Note: Effective 2008-03-01, (75) Chita Oblast and (80) Agin-Buryat -# Autonomous Okrug merged to form (92, RU-ZAB) Zabaykalsky Krai. - -# From Alexander Krivenyshev (2016-01-02): -# [The] time zone in the Trans-Baikal Territory (Zabaykalsky Krai) - -# Asia/Chita [is changing] from UTC+8 to UTC+9. Effective date will -# be March 27, 2016 at 2:00am.... -# http://publication.pravo.gov.ru/Document/View/0001201512300107 - -Zone Asia/Chita 7:33:52 - LMT 1919 Dec 15 - 8:00 - +08 1930 Jun 21 - 9:00 Russia +09/+10 1991 Mar 31 2:00s - 8:00 Russia +08/+09 1992 Jan 19 2:00s - 9:00 Russia +09/+10 2011 Mar 27 2:00s - 10:00 - +10 2014 Oct 26 2:00s - 8:00 - +08 2016 Mar 27 2:00 - 9:00 - +09 - - -# From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2009-11-29): -# Asia/Yakutsk covers... -# 28 RU-AMU Amur Oblast -# -# ...and parts of (14, RU-SA) Sakha (Yakutia) Republic: -# 14-02 **** Aldansky District -# 14-04 **** Amginsky District -# 14-05 **** Anabarsky District -# 14-06 **** Bulunsky District -# 14-07 **** Verkhnevilyuysky District -# 14-10 **** Vilyuysky District -# 14-11 **** Gorny District -# 14-12 **** Zhigansky District -# 14-13 **** Kobyaysky District -# 14-14 **** Lensky District -# 14-15 **** Megino-Kangalassky District -# 14-16 **** Mirninsky District -# 14-18 **** Namsky District -# 14-19 **** Neryungrinsky District -# 14-21 **** Nyurbinsky District -# 14-23 **** Olenyoksky District -# 14-24 **** Olyokminsky District -# 14-26 **** Suntarsky District -# 14-27 **** Tattinsky District -# 14-29 **** Ust-Aldansky District -# 14-32 **** Khangalassky District -# 14-33 **** Churapchinsky District -# 14-34 **** Eveno-Bytantaysky National District - -# From Tim Parenti (2014-07-03): -# Our commentary seems to have lost mention of (14-19) Neryungrinsky District. -# Since the surrounding districts of Sakha are all YAKT, assume this is, too. -# Also assume its history has been the same as the rest of Asia/Yakutsk. - -# Byalokoz 1919 says Yakutsk was 8:38:58. - -Zone Asia/Yakutsk 8:38:58 - LMT 1919 Dec 15 - 8:00 - +08 1930 Jun 21 - 9:00 Russia +09/+10 1991 Mar 31 2:00s - 8:00 Russia +08/+09 1992 Jan 19 2:00s - 9:00 Russia +09/+10 2011 Mar 27 2:00s - 10:00 - +10 2014 Oct 26 2:00s - 9:00 - +09 - - -# From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2009-11-29): -# Asia/Vladivostok covers... -# 25 RU-PRI Primorsky Krai -# 27 RU-KHA Khabarovsk Krai -# 79 RU-YEV Jewish Autonomous Oblast -# -# ...and parts of (14, RU-SA) Sakha (Yakutia) Republic: -# 14-09 **** Verkhoyansky District -# 14-31 **** Ust-Yansky District - -# Milne 1899 says Vladivostok was 8:47:33.5. -# Byalokoz 1919 says Vladivostok was 8:47:31. -# Go with Byalokoz. - -Zone Asia/Vladivostok 8:47:31 - LMT 1922 Nov 15 - 9:00 - +09 1930 Jun 21 - 10:00 Russia +10/+11 1991 Mar 31 2:00s - 9:00 Russia +09/+10 1992 Jan 19 2:00s - 10:00 Russia +10/+11 2011 Mar 27 2:00s - 11:00 - +11 2014 Oct 26 2:00s - 10:00 - +10 - - -# From Tim Parenti (2014-07-03): -# Asia/Khandyga covers parts of (14, RU-SA) Sakha (Yakutia) Republic: -# 14-28 **** Tomponsky District -# 14-30 **** Ust-Maysky District - -# From Arthur David Olson (2012-05-09): -# Tomponskij and Ust'-Majskij switched from Vladivostok time to Yakutsk time -# in 2011. - -# From Paul Eggert (2012-11-25): -# Shanks and Pottenger (2003) has Khandyga on Yakutsk time. -# Make a wild guess that it switched to Vladivostok time in 2004. -# This transition is no doubt wrong, but we have no better info. - -Zone Asia/Khandyga 9:02:13 - LMT 1919 Dec 15 - 8:00 - +08 1930 Jun 21 - 9:00 Russia +09/+10 1991 Mar 31 2:00s - 8:00 Russia +08/+09 1992 Jan 19 2:00s - 9:00 Russia +09/+10 2004 - 10:00 Russia +10/+11 2011 Mar 27 2:00s - 11:00 - +11 2011 Sep 13 0:00s # Decree 725? - 10:00 - +10 2014 Oct 26 2:00s - 9:00 - +09 - - -# From Tim Parenti (2014-07-03): -# Asia/Sakhalin covers... -# 65 RU-SAK Sakhalin Oblast -# ...with the exception of: -# 65-11 **** Severo-Kurilsky District (North Kuril Islands) - -# From Matt Johnson (2016-02-22): -# Asia/Sakhalin is moving (in entirety) from UTC+10 to UTC+11 ... -# (2016-03-09): -# http://publication.pravo.gov.ru/Document/View/0001201603090044 - -# The Zone name should be Asia/Yuzhno-Sakhalinsk, but that's too long. -Zone Asia/Sakhalin 9:30:48 - LMT 1905 Aug 23 - 9:00 - +09 1945 Aug 25 - 11:00 Russia +11/+12 1991 Mar 31 2:00s # Sakhalin T - 10:00 Russia +10/+11 1992 Jan 19 2:00s - 11:00 Russia +11/+12 1997 Mar lastSun 2:00s - 10:00 Russia +10/+11 2011 Mar 27 2:00s - 11:00 - +11 2014 Oct 26 2:00s - 10:00 - +10 2016 Mar 27 2:00s - 11:00 - +11 - - -# From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2009-11-29): -# Asia/Magadan covers... -# 49 RU-MAG Magadan Oblast - -# From Tim Parenti (2014-07-06), per Alexander Krivenyshev (2014-07-02): -# Magadan Oblast is moving from UTC+12 to UTC+10 on 2014-10-26; however, -# several districts of Sakha Republic as well as Severo-Kurilsky District of -# the Sakhalin Oblast (also known as the North Kuril Islands), represented -# until now by Asia/Magadan, will instead move to UTC+11. These regions will -# need their own zone. - -# From Alexander Krivenyshev (2016-03-27): -# ... draft bill 948300-6 to change its time zone from UTC+10 to UTC+11 ... -# will take ... effect ... on April 24, 2016 at 2 o'clock -# -# From Matt Johnson (2016-04-05): -# ... signed by the President today ... -# http://publication.pravo.gov.ru/Document/View/0001201604050038 - -Zone Asia/Magadan 10:03:12 - LMT 1924 May 2 - 10:00 - +10 1930 Jun 21 # Magadan Time - 11:00 Russia +11/+12 1991 Mar 31 2:00s - 10:00 Russia +10/+11 1992 Jan 19 2:00s - 11:00 Russia +11/+12 2011 Mar 27 2:00s - 12:00 - +12 2014 Oct 26 2:00s - 10:00 - +10 2016 Apr 24 2:00s - 11:00 - +11 - - -# From Tim Parenti (2014-07-06): -# Asia/Srednekolymsk covers parts of (14, RU-SA) Sakha (Yakutia) Republic: -# 14-01 **** Abyysky District -# 14-03 **** Allaikhovsky District -# 14-08 **** Verkhnekolymsky District -# 14-17 **** Momsky District -# 14-20 **** Nizhnekolymsky District -# 14-25 **** Srednekolymsky District -# -# ...and parts of (65, RU-SAK) Sakhalin Oblast: -# 65-11 **** Severo-Kurilsky District (North Kuril Islands) - -# From Tim Parenti (2014-07-02): -# Oymyakonsky District of Sakha Republic (represented by Ust-Nera), along with -# most of Sakhalin Oblast (represented by Sakhalin) will be moving to UTC+10 on -# 2014-10-26 to stay aligned with VLAT/SAKT; however, Severo-Kurilsky District -# of the Sakhalin Oblast (also known as the North Kuril Islands, represented by -# Severo-Kurilsk) will remain on UTC+11. - -# From Tim Parenti (2014-07-06): -# Assume North Kuril Islands have history like Magadan before 2011-03-27. -# There is a decent chance this is wrong, in which case a new zone -# Asia/Severo-Kurilsk would become necessary. -# -# Srednekolymsk and Zyryanka are the most populous places amongst these -# districts, but have very similar populations. In fact, Wikipedia currently -# lists them both as having 3528 people, exactly 1668 males and 1860 females -# each! (Yikes!) -# https://en.wikipedia.org/w/?title=Srednekolymsky_District&oldid=603435276 -# https://en.wikipedia.org/w/?title=Verkhnekolymsky_District&oldid=594378493 -# Assume this is a mistake, albeit an amusing one. -# -# Looking at censuses, the populations of the two municipalities seem to have -# fluctuated recently. Zyryanka was more populous than Srednekolymsk in the -# 1989 and 2002 censuses, but Srednekolymsk was more populous in the most -# recent (2010) census, 3525 to 3170. (See pages 195 and 197 of -# http://www.gks.ru/free_doc/new_site/perepis2010/croc/Documents/Vol1/pub-01-05.pdf -# in Russian.) In addition, Srednekolymsk appears to be a much older -# settlement and the population of Zyryanka seems to be declining. -# Go with Srednekolymsk. - -Zone Asia/Srednekolymsk 10:14:52 - LMT 1924 May 2 - 10:00 - +10 1930 Jun 21 - 11:00 Russia +11/+12 1991 Mar 31 2:00s - 10:00 Russia +10/+11 1992 Jan 19 2:00s - 11:00 Russia +11/+12 2011 Mar 27 2:00s - 12:00 - +12 2014 Oct 26 2:00s - 11:00 - +11 - - -# From Tim Parenti (2014-07-03): -# Asia/Ust-Nera covers parts of (14, RU-SA) Sakha (Yakutia) Republic: -# 14-22 **** Oymyakonsky District - -# From Arthur David Olson (2012-05-09): -# Ojmyakonskij [and the Kuril Islands] switched from -# Magadan time to Vladivostok time in 2011. -# -# From Tim Parenti (2014-07-06), per Alexander Krivenyshev (2014-07-02): -# It's unlikely that any of the Kuril Islands were involved in such a switch, -# as the South and Middle Kurils have been on UTC+11 (SAKT) with the rest of -# Sakhalin Oblast since at least 2011-09, and the North Kurils have been on -# UTC+12 since at least then, too. - -Zone Asia/Ust-Nera 9:32:54 - LMT 1919 Dec 15 - 8:00 - +08 1930 Jun 21 - 9:00 Russia +09/+10 1981 Apr 1 - 11:00 Russia +11/+12 1991 Mar 31 2:00s - 10:00 Russia +10/+11 1992 Jan 19 2:00s - 11:00 Russia +11/+12 2011 Mar 27 2:00s - 12:00 - +12 2011 Sep 13 0:00s # Decree 725? - 11:00 - +11 2014 Oct 26 2:00s - 10:00 - +10 - - -# From Tim Parenti (2014-07-03), per Oscar van Vlijmen (2001-08-25): -# Asia/Kamchatka covers... -# 91 RU-KAM Kamchatka Krai -# -# Note: Effective 2007-07-01, (41) Kamchatka Oblast and (82) Koryak -# Autonomous Okrug merged to form (91, RU-KAM) Kamchatka Krai. - -# The Zone name should be Asia/Petropavlovsk-Kamchatski or perhaps -# Asia/Petropavlovsk-Kamchatsky, but these are too long. -Zone Asia/Kamchatka 10:34:36 - LMT 1922 Nov 10 - 11:00 - +11 1930 Jun 21 - 12:00 Russia +12/+13 1991 Mar 31 2:00s - 11:00 Russia +11/+12 1992 Jan 19 2:00s - 12:00 Russia +12/+13 2010 Mar 28 2:00s - 11:00 Russia +11/+12 2011 Mar 27 2:00s - 12:00 - +12 - - -# From Tim Parenti (2014-07-03): -# Asia/Anadyr covers... -# 87 RU-CHU Chukotka Autonomous Okrug - -Zone Asia/Anadyr 11:49:56 - LMT 1924 May 2 - 12:00 - +12 1930 Jun 21 - 13:00 Russia +13/+14 1982 Apr 1 0:00s - 12:00 Russia +12/+13 1991 Mar 31 2:00s - 11:00 Russia +11/+12 1992 Jan 19 2:00s - 12:00 Russia +12/+13 2010 Mar 28 2:00s - 11:00 Russia +11/+12 2011 Mar 27 2:00s - 12:00 - +12 - - -# San Marino -# See Europe/Rome. - -# Serbia -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Belgrade 1:22:00 - LMT 1884 - 1:00 - CET 1941 Apr 18 23:00 - 1:00 C-Eur CE%sT 1945 - 1:00 - CET 1945 May 8 2:00s - 1:00 1:00 CEST 1945 Sep 16 2:00s -# Metod Koželj reports that the legal date of -# transition to EU rules was 1982-11-27, for all of Yugoslavia at the time. -# Shanks & Pottenger don't give as much detail, so go with Koželj. - 1:00 - CET 1982 Nov 27 - 1:00 EU CE%sT -Link Europe/Belgrade Europe/Ljubljana # Slovenia -Link Europe/Belgrade Europe/Podgorica # Montenegro -Link Europe/Belgrade Europe/Sarajevo # Bosnia and Herzegovina -Link Europe/Belgrade Europe/Skopje # Macedonia -Link Europe/Belgrade Europe/Zagreb # Croatia - -# Slovakia -Link Europe/Prague Europe/Bratislava - -# Slovenia -# See Europe/Belgrade. - -# Spain -# -# From Paul Eggert (2016-12-14): -# -# The source for Europe/Madrid before 2013 is: -# Planesas P. La hora oficial en España y sus cambios. -# Anuario del Observatorio Astronómico de Madrid (2013, in Spanish). -# http://astronomia.ign.es/rknowsys-theme/images/webAstro/paginas/documentos/Anuario/lahoraoficialenespana.pdf -# As this source says that historical time in the Canaries is obscure, -# and it does not discuss Ceuta, stick with Shanks for now for that data. -# -# In the 1918 and 1919 fallback transitions in Spain, the clock for -# the hour-longer day officially kept going after midnight, so that -# the repeated instances of that day's 00:00 hour were 24 hours apart, -# with a fallback transition from the second occurrence of 00:59... to -# the next day's 00:00. Our data format cannot represent this -# directly, and instead repeats the first hour of the next day, with a -# fallback transition from the next day's 00:59... to 00:00. - -# From Michael Deckers (2016-12-15): -# The Royal Decree of 1900-06-26 quoted by Planesas, online at -# https://www.boe.es/datos/pdfs/BOE//1900/209/A00383-00384.pdf -# says in its article 5 (my translation): -# These dispositions will enter into force beginning with the -# instant at which, according to the time indicated in article 1, -# the 1st day of January of 1901 will begin. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Spain 1918 only - Apr 15 23:00 1:00 S -Rule Spain 1918 1919 - Oct 6 24:00s 0 - -Rule Spain 1919 only - Apr 6 23:00 1:00 S -Rule Spain 1924 only - Apr 16 23:00 1:00 S -Rule Spain 1924 only - Oct 4 24:00s 0 - -Rule Spain 1926 only - Apr 17 23:00 1:00 S -Rule Spain 1926 1929 - Oct Sat>=1 24:00s 0 - -Rule Spain 1927 only - Apr 9 23:00 1:00 S -Rule Spain 1928 only - Apr 15 0:00 1:00 S -Rule Spain 1929 only - Apr 20 23:00 1:00 S -# Republican Spain during the civil war; it controlled Madrid until 1939-03-28. -Rule Spain 1937 only - Jun 16 23:00 1:00 S -Rule Spain 1937 only - Oct 2 24:00s 0 - -Rule Spain 1938 only - Apr 2 23:00 1:00 S -Rule Spain 1938 only - Apr 30 23:00 2:00 M -Rule Spain 1938 only - Oct 2 24:00 1:00 S -# The following rules are for unified Spain again. -# -# Planesas does not say what happened in Madrid between its fall on -# 1939-03-28 and the Nationalist spring-forward transition on -# 1939-04-15. For lack of better info, assume Madrid's clocks did not -# change during that period. -# -# The first rule is commented out, as it is redundant for Republican Spain. -#Rule Spain 1939 only - Apr 15 23:00 1:00 S -Rule Spain 1939 only - Oct 7 24:00s 0 - -Rule Spain 1942 only - May 2 23:00 1:00 S -Rule Spain 1942 only - Sep 1 1:00 0 - -Rule Spain 1943 1946 - Apr Sat>=13 23:00 1:00 S -Rule Spain 1943 1944 - Oct Sun>=1 1:00 0 - -Rule Spain 1945 1946 - Sep lastSun 1:00 0 - -Rule Spain 1949 only - Apr 30 23:00 1:00 S -Rule Spain 1949 only - Oct 2 1:00 0 - -Rule Spain 1974 1975 - Apr Sat>=12 23:00 1:00 S -Rule Spain 1974 1975 - Oct Sun>=1 1:00 0 - -Rule Spain 1976 only - Mar 27 23:00 1:00 S -Rule Spain 1976 1977 - Sep lastSun 1:00 0 - -Rule Spain 1977 only - Apr 2 23:00 1:00 S -Rule Spain 1978 only - Apr 2 2:00s 1:00 S -Rule Spain 1978 only - Oct 1 2:00s 0 - -# Nationalist Spain during the civil war -#Rule NatSpain 1937 only - May 22 23:00 1:00 S -#Rule NatSpain 1937 1938 - Oct Sat>=1 24:00s 0 - -#Rule NatSpain 1938 only - Mar 26 23:00 1:00 S -# The following rules are copied from Morocco from 1967 through 1978. -Rule SpainAfrica 1967 only - Jun 3 12:00 1:00 S -Rule SpainAfrica 1967 only - Oct 1 0:00 0 - -Rule SpainAfrica 1974 only - Jun 24 0:00 1:00 S -Rule SpainAfrica 1974 only - Sep 1 0:00 0 - -Rule SpainAfrica 1976 1977 - May 1 0:00 1:00 S -Rule SpainAfrica 1976 only - Aug 1 0:00 0 - -Rule SpainAfrica 1977 only - Sep 28 0:00 0 - -Rule SpainAfrica 1978 only - Jun 1 0:00 1:00 S -Rule SpainAfrica 1978 only - Aug 4 0:00 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Madrid -0:14:44 - LMT 1900 Dec 31 23:45:16 - 0:00 Spain WE%sT 1940 Mar 16 23:00 - 1:00 Spain CE%sT 1979 - 1:00 EU CE%sT -Zone Africa/Ceuta -0:21:16 - LMT 1900 Dec 31 23:38:44 - 0:00 - WET 1918 May 6 23:00 - 0:00 1:00 WEST 1918 Oct 7 23:00 - 0:00 - WET 1924 - 0:00 Spain WE%sT 1929 - 0:00 SpainAfrica WE%sT 1984 Mar 16 - 1:00 - CET 1986 - 1:00 EU CE%sT -Zone Atlantic/Canary -1:01:36 - LMT 1922 Mar # Las Palmas de Gran C. - -1:00 - -01 1946 Sep 30 1:00 - 0:00 - WET 1980 Apr 6 0:00s - 0:00 1:00 WEST 1980 Sep 28 1:00u - 0:00 EU WE%sT -# IATA SSIM (1996-09) says the Canaries switch at 2:00u, not 1:00u. -# Ignore this for now, as the Canaries are part of the EU. - -# Sweden - -# From Ivan Nilsson (2001-04-13), superseding Shanks & Pottenger: -# -# The law "Svensk författningssamling 1878, no 14" about standard time in 1879: -# From the beginning of 1879 (that is 01-01 00:00) the time for all -# places in the country is "the mean solar time for the meridian at -# three degrees, or twelve minutes of time, to the west of the -# meridian of the Observatory of Stockholm". The law is dated 1878-05-31. -# -# The observatory at that time had the meridian 18° 03' 30" -# eastern longitude = 01:12:14 in time. Less 12 minutes gives the -# national standard time as 01:00:14 ahead of GMT.... -# -# About the beginning of CET in Sweden. The lawtext ("Svensk -# författningssamling 1899, no 44") states, that "from the beginning -# of 1900... ... the same as the mean solar time for the meridian at -# the distance of one hour of time from the meridian of the English -# observatory at Greenwich, or at 12 minutes 14 seconds to the west -# from the meridian of the Observatory of Stockholm". The law is dated -# 1899-06-16. In short: At 1900-01-01 00:00:00 the new standard time -# in Sweden is 01:00:00 ahead of GMT. -# -# 1916: The lawtext ("Svensk författningssamling 1916, no 124") states -# that "1916-05-15 is considered to begin one hour earlier". It is -# pretty obvious that at 05-14 23:00 the clocks are set to 05-15 00:00.... -# Further the law says, that "1916-09-30 is considered to end one hour later". -# -# The laws regulating [DST] are available on the site of the Swedish -# Parliament beginning with 1985 - the laws regulating 1980/1984 are -# not available on the site (to my knowledge they are only available -# in Swedish): (type -# "sommartid" without the quotes in the field "Fritext" and then click -# the Sök-button). -# -# (2001-05-13): -# -# I have now found a newspaper stating that at 1916-10-01 01:00 -# summertime the church-clocks etc were set back one hour to show -# 1916-10-01 00:00 standard time. The article also reports that some -# people thought the switch to standard time would take place already -# at 1916-10-01 00:00 summer time, but they had to wait for another -# hour before the event took place. -# -# Source: The newspaper "Dagens Nyheter", 1916-10-01, page 7 upper left. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Stockholm 1:12:12 - LMT 1879 Jan 1 - 1:00:14 - SET 1900 Jan 1 # Swedish Time - 1:00 - CET 1916 May 14 23:00 - 1:00 1:00 CEST 1916 Oct 1 1:00 - 1:00 - CET 1980 - 1:00 EU CE%sT - -# Switzerland -# From Howse: -# By the end of the 18th century clocks and watches became commonplace -# and their performance improved enormously. Communities began to keep -# mean time in preference to apparent time - Geneva from 1780 .... -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -# From Whitman (who writes "Midnight?"): -# Rule Swiss 1940 only - Nov 2 0:00 1:00 S -# Rule Swiss 1940 only - Dec 31 0:00 0 - -# From Shanks & Pottenger: -# Rule Swiss 1941 1942 - May Sun>=1 2:00 1:00 S -# Rule Swiss 1941 1942 - Oct Sun>=1 0:00 0 - - -# From Alois Treindl (2008-12-17): -# I have researched the DST usage in Switzerland during the 1940ies. -# -# As I wrote in an earlier message, I suspected the current tzdata values -# to be wrong. This is now verified. -# -# I have found copies of the original ruling by the Swiss Federal -# government, in 'Eidgenössische Gesetzessammlung 1941 and 1942' (Swiss -# federal law collection)... -# -# DST began on Monday 5 May 1941, 1:00 am by shifting the clocks to 2:00 am -# DST ended on Monday 6 Oct 1941, 2:00 am by shifting the clocks to 1:00 am. -# -# DST began on Monday, 4 May 1942 at 01:00 am -# DST ended on Monday, 5 Oct 1942 at 02:00 am -# -# There was no DST in 1940, I have checked the law collection carefully. -# It is also indicated by the fact that the 1942 entry in the law -# collection points back to 1941 as a reference, but no reference to any -# other years are made. -# -# Newspaper articles I have read in the archives on 6 May 1941 reported -# about the introduction of DST (Sommerzeit in German) during the previous -# night as an absolute novelty, because this was the first time that such -# a thing had happened in Switzerland. -# -# I have also checked 1916, because one book source (Gabriel, Traité de -# l'heure dans le monde) claims that Switzerland had DST in 1916. This is -# false, no official document could be found. Probably Gabriel got misled -# by references to Germany, which introduced DST in 1916 for the first time. -# -# The tzdata rules for Switzerland must be changed to: -# Rule Swiss 1941 1942 - May Mon>=1 1:00 1:00 S -# Rule Swiss 1941 1942 - Oct Mon>=1 2:00 0 - -# -# The 1940 rules must be deleted. -# -# One further detail for Switzerland, which is probably out of scope for -# most users of tzdata: The [Europe/Zurich zone] ... -# describes all of Switzerland correctly, with the exception of -# the Canton de Genève (Geneva, Genf). Between 1848 and 1894 Geneva did not -# follow Bern Mean Time but kept its own local mean time. -# To represent this, an extra zone would be needed. -# -# From Alois Treindl (2013-09-11): -# The Federal regulations say -# https://www.admin.ch/opc/de/classified-compilation/20071096/index.html -# ... the meridian for Bern mean time ... is 7° 26' 22.50". -# Expressed in time, it is 0h29m45.5s. - -# From Pierre-Yves Berger (2013-09-11): -# the "Circulaire du conseil fédéral" (December 11 1893) -# http://www.amtsdruckschriften.bar.admin.ch/viewOrigDoc.do?id=10071353 -# clearly states that the [1894-06-01] change should be done at midnight -# but if no one is present after 11 at night, could be postponed until one -# hour before the beginning of service. - -# From Paul Eggert (2013-09-11): -# Round BMT to the nearest even second, 0:29:46. -# -# We can find no reliable source for Shanks's assertion that all of Switzerland -# except Geneva switched to Bern Mean Time at 00:00 on 1848-09-12. This book: -# -# Jakob Messerli. Gleichmässig, pünktlich, schnell. Zeiteinteilung und -# Zeitgebrauch in der Schweiz im 19. Jahrhundert. Chronos, Zurich 1995, -# ISBN 3-905311-68-2, OCLC 717570797. -# -# suggests that the transition was more gradual, and that the Swiss did not -# agree about civil time during the transition. The timekeeping it gives the -# most detail for is postal and telegraph time: here, federal legislation (the -# "Bundesgesetz über die Erstellung von elektrischen Telegraphen") passed on -# 1851-11-23, and an official implementation notice was published 1853-07-16 -# (Bundesblatt 1853, Bd. II, S. 859). On p 72 Messerli writes that in -# practice since July 1853 Bernese time was used in "all postal and telegraph -# offices in Switzerland from Geneva to St. Gallen and Basel to Chiasso" -# (Google translation). For now, model this transition as occurring on -# 1853-07-16, though it probably occurred at some other date in Zurich, and -# legal civil time probably changed at still some other transition date. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Swiss 1941 1942 - May Mon>=1 1:00 1:00 S -Rule Swiss 1941 1942 - Oct Mon>=1 2:00 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Zurich 0:34:08 - LMT 1853 Jul 16 # See above comment. - 0:29:46 - BMT 1894 Jun # Bern Mean Time - 1:00 Swiss CE%sT 1981 - 1:00 EU CE%sT - -# Turkey - -# From Kıvanç Yazan (2016-09-25): -# 1) For 1986-2006, DST started at 01:00 local and ended at 02:00 local, with -# no exceptions. -# 2) 1994's lastSun was overridden with Mar 20 ... -# Here are official papers: -# http://www.resmigazete.gov.tr/arsiv/19032.pdf - page 2 for 1986 -# http://www.resmigazete.gov.tr/arsiv/19400.pdf - page 4 for 1987 -# http://www.resmigazete.gov.tr/arsiv/19752.pdf - page 15 for 1988 -# http://www.resmigazete.gov.tr/arsiv/20102.pdf - page 6 for 1989 -# http://www.resmigazete.gov.tr/arsiv/20464.pdf - page 1 for 1990 - 1992 -# http://www.resmigazete.gov.tr/arsiv/21531.pdf - page 15 for 1993 - 1995 -# http://www.resmigazete.gov.tr/arsiv/21879.pdf - page 1 for overriding 1994 -# http://www.resmigazete.gov.tr/arsiv/22588.pdf - page 1 for 1996, 1997 -# http://www.resmigazete.gov.tr/arsiv/23286.pdf - page 10 for 1998 - 2000 -# http://www.resmigazete.gov.tr/eskiler/2001/03/20010324.htm#2 - for 2001 -# http://www.resmigazete.gov.tr/eskiler/2002/03/20020316.htm#2 - for 2002-2006 -# From Paul Eggert (2016-09-25): -# Prefer the above sources to Shanks & Pottenger for time stamps after 1985. - -# From Steffen Thorsen (2007-03-09): -# Starting 2007 though, it seems that they are adopting EU's 1:00 UTC -# start/end time, according to the following page (2007-03-07): -# http://www.ntvmsnbc.com/news/402029.asp -# The official document is located here - it is in Turkish...: -# http://rega.basbakanlik.gov.tr/eskiler/2007/03/20070307-7.htm -# I was able to locate the following seemingly official document -# (on a non-government server though) describing dates between 2002 and 2006: -# http://www.alomaliye.com/bkk_2002_3769.htm - -# From Gökdeniz Karadağ (2011-03-10): -# According to the articles linked below, Turkey will change into summer -# time zone (GMT+3) on March 28, 2011 at 3:00 a.m. instead of March 27. -# This change is due to a nationwide exam on 27th. -# https://www.worldbulletin.net/?aType=haber&ArticleID=70872 -# Turkish: -# https://www.hurriyet.com.tr/yaz-saati-uygulamasi-bir-gun-ileri-alindi-17230464 - -# From Faruk Pasin (2014-02-14): -# The DST for Turkey has been changed for this year because of the -# Turkish Local election.... -# http://www.sabah.com.tr/Ekonomi/2014/02/12/yaz-saatinde-onemli-degisiklik -# ... so Turkey will move clocks forward one hour on March 31 at 3:00 a.m. -# From Randal L. Schwartz (2014-04-15): -# Having landed on a flight from the states to Istanbul (via AMS) on March 31, -# I can tell you that NOBODY (even the airlines) respected this timezone DST -# change delay. Maybe the word just didn't get out in time. -# From Paul Eggert (2014-06-15): -# The press reported massive confusion, as election officials obeyed the rule -# change but cell phones (and airline baggage systems) did not. See: -# Kostidis M. Eventful elections in Turkey. Balkan News Agency -# http://www.balkaneu.com/eventful-elections-turkey/ 2014-03-30. -# I guess the best we can do is document the official time. - -# From Fatih (2015-09-29): -# It's officially announced now by the Ministry of Energy. -# Turkey delays winter time to 8th of November 04:00 -# http://www.aa.com.tr/tr/turkiye/yaz-saati-uygulamasi-8-kasimda-sona-erecek/362217 -# -# From BBC News (2015-10-25): -# Confused Turks are asking "what's the time?" after automatic clocks defied a -# government decision ... "For the next two weeks #Turkey is on EEST... Erdogan -# Engineered Standard Time," said Twitter user @aysekarahasan. -# http://www.bbc.com/news/world-europe-34631326 - -# From Burak AYDIN (2016-09-08): -# Turkey will stay in Daylight Saving Time even in winter.... -# http://www.resmigazete.gov.tr/eskiler/2016/09/20160908-2.pdf -# -# From Paul Eggert (2016-09-07): -# The change is permanent, so this is the new standard time in Turkey. -# It takes effect today, which is not much notice. - -# From Kıvanç Yazan (2017-10-28): -# Turkey will go back to Daylight Saving Time starting 2018-10. -# http://www.resmigazete.gov.tr/eskiler/2017/10/20171028-5.pdf -# -# From Even Scharning (2017-11-08): -# ... today it was announced that the DST will become "continuous": -# http://www.hurriyet.com.tr/son-dakika-yaz-saati-uygulamasi-surekli-hale-geldi-40637482 -# From Paul Eggert (2017-11-08): -# Although Google Translate misfires on that source, it looks like -# Turkey reversed last month's decision, and so will stay at +03. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Turkey 1916 only - May 1 0:00 1:00 S -Rule Turkey 1916 only - Oct 1 0:00 0 - -Rule Turkey 1920 only - Mar 28 0:00 1:00 S -Rule Turkey 1920 only - Oct 25 0:00 0 - -Rule Turkey 1921 only - Apr 3 0:00 1:00 S -Rule Turkey 1921 only - Oct 3 0:00 0 - -Rule Turkey 1922 only - Mar 26 0:00 1:00 S -Rule Turkey 1922 only - Oct 8 0:00 0 - -# Whitman gives 1923 Apr 28 - Sep 16 and no DST in 1924-1925; -# go with Shanks & Pottenger. -Rule Turkey 1924 only - May 13 0:00 1:00 S -Rule Turkey 1924 1925 - Oct 1 0:00 0 - -Rule Turkey 1925 only - May 1 0:00 1:00 S -Rule Turkey 1940 only - Jun 30 0:00 1:00 S -Rule Turkey 1940 only - Oct 5 0:00 0 - -Rule Turkey 1940 only - Dec 1 0:00 1:00 S -Rule Turkey 1941 only - Sep 21 0:00 0 - -Rule Turkey 1942 only - Apr 1 0:00 1:00 S -# Whitman omits the next two transition and gives 1945 Oct 1; -# go with Shanks & Pottenger. -Rule Turkey 1942 only - Nov 1 0:00 0 - -Rule Turkey 1945 only - Apr 2 0:00 1:00 S -Rule Turkey 1945 only - Oct 8 0:00 0 - -Rule Turkey 1946 only - Jun 1 0:00 1:00 S -Rule Turkey 1946 only - Oct 1 0:00 0 - -Rule Turkey 1947 1948 - Apr Sun>=16 0:00 1:00 S -Rule Turkey 1947 1950 - Oct Sun>=2 0:00 0 - -Rule Turkey 1949 only - Apr 10 0:00 1:00 S -Rule Turkey 1950 only - Apr 19 0:00 1:00 S -Rule Turkey 1951 only - Apr 22 0:00 1:00 S -Rule Turkey 1951 only - Oct 8 0:00 0 - -Rule Turkey 1962 only - Jul 15 0:00 1:00 S -Rule Turkey 1962 only - Oct 8 0:00 0 - -Rule Turkey 1964 only - May 15 0:00 1:00 S -Rule Turkey 1964 only - Oct 1 0:00 0 - -Rule Turkey 1970 1972 - May Sun>=2 0:00 1:00 S -Rule Turkey 1970 1972 - Oct Sun>=2 0:00 0 - -Rule Turkey 1973 only - Jun 3 1:00 1:00 S -Rule Turkey 1973 only - Nov 4 3:00 0 - -Rule Turkey 1974 only - Mar 31 2:00 1:00 S -Rule Turkey 1974 only - Nov 3 5:00 0 - -Rule Turkey 1975 only - Mar 30 0:00 1:00 S -Rule Turkey 1975 1976 - Oct lastSun 0:00 0 - -Rule Turkey 1976 only - Jun 1 0:00 1:00 S -Rule Turkey 1977 1978 - Apr Sun>=1 0:00 1:00 S -Rule Turkey 1977 only - Oct 16 0:00 0 - -Rule Turkey 1979 1980 - Apr Sun>=1 3:00 1:00 S -Rule Turkey 1979 1982 - Oct Mon>=11 0:00 0 - -Rule Turkey 1981 1982 - Mar lastSun 3:00 1:00 S -Rule Turkey 1983 only - Jul 31 0:00 1:00 S -Rule Turkey 1983 only - Oct 2 0:00 0 - -Rule Turkey 1985 only - Apr 20 0:00 1:00 S -Rule Turkey 1985 only - Sep 28 0:00 0 - -Rule Turkey 1986 1993 - Mar lastSun 1:00s 1:00 S -Rule Turkey 1986 1995 - Sep lastSun 1:00s 0 - -Rule Turkey 1994 only - Mar 20 1:00s 1:00 S -Rule Turkey 1995 2006 - Mar lastSun 1:00s 1:00 S -Rule Turkey 1996 2006 - Oct lastSun 1:00s 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Istanbul 1:55:52 - LMT 1880 - 1:56:56 - IMT 1910 Oct # Istanbul Mean Time? - 2:00 Turkey EE%sT 1978 Oct 15 - 3:00 Turkey +03/+04 1985 Apr 20 - 2:00 Turkey EE%sT 2007 - 2:00 EU EE%sT 2011 Mar 27 1:00u - 2:00 - EET 2011 Mar 28 1:00u - 2:00 EU EE%sT 2014 Mar 30 1:00u - 2:00 - EET 2014 Mar 31 1:00u - 2:00 EU EE%sT 2015 Oct 25 1:00u - 2:00 1:00 EEST 2015 Nov 8 1:00u - 2:00 EU EE%sT 2016 Sep 7 - 3:00 - +03 -Link Europe/Istanbul Asia/Istanbul # Istanbul is in both continents. - -# Ukraine -# -# From Igor Karpov, who works for the Ukrainian Ministry of Justice, -# via Garrett Wollman (2003-01-27): -# BTW, I've found the official document on this matter. It's government -# regulations No. 509, May 13, 1996. In my poor translation it says: -# "Time in Ukraine is set to second timezone (Kiev time). Each last Sunday -# of March at 3am the time is changing to 4am and each last Sunday of -# October the time at 4am is changing to 3am" - -# From Alexander Krivenyshev (2011-09-20): -# On September 20, 2011 the deputies of the Verkhovna Rada agreed to -# abolish the transfer clock to winter time. -# -# Bill No. 8330 of MP from the Party of Regions Oleg Nadoshi got -# approval from 266 deputies. -# -# Ukraine abolishes transfer back to the winter time (in Russian) -# http://news.mail.ru/politics/6861560/ -# -# The Ukrainians will no longer change the clock (in Russian) -# http://www.segodnya.ua/news/14290482.html -# -# Deputies cancelled the winter time (in Russian) -# https://www.pravda.com.ua/rus/news/2011/09/20/6600616/ -# -# From Philip Pizzey (2011-10-18): -# Today my Ukrainian colleagues have informed me that the -# Ukrainian parliament have decided that they will go to winter -# time this year after all. -# -# From Udo Schwedt (2011-10-18): -# As far as I understand, the recent change to the Ukrainian time zone -# (Europe/Kiev) to introduce permanent daylight saving time (similar -# to Russia) was reverted today: -# http://portal.rada.gov.ua/rada/control/en/publish/article/info_left?art_id=287324&cat_id=105995 -# -# Also reported by Alexander Bokovoy (2011-10-18) who also noted: -# The law documents themselves are at -# http://w1.c1.rada.gov.ua/pls/zweb_n/webproc4_1?id=&pf3511=41484 - -# From Vladimir in Moscow via Alois Treindl re Kiev time 1991/2 (2014-02-28): -# First in Ukraine they changed Time zone from UTC+3 to UTC+2 with DST: -# 03 25 1990 02:00 -03.00 1 Time Zone 3 with DST -# 07 01 1990 02:00 -02.00 1 Time Zone 2 with DST -# * Ukrainian Government's Resolution of 18.06.1990, No. 134. -# http://search.ligazakon.ua/l_doc2.nsf/link1/T001500.html -# -# They did not end DST in September, 1990 (according to the law, -# "summer time" was still in action): -# 09 30 1990 03:00 -02.00 1 Time Zone 2 with DST -# * Ukrainian Government's Resolution of 21.09.1990, No. 272. -# http://search.ligazakon.ua/l_doc2.nsf/link1/KP900272.html -# -# Again no change in March, 1991 ("summer time" in action): -# 03 31 1991 02:00 -02.00 1 Time Zone 2 with DST -# -# DST ended in September 1991 ("summer time" ended): -# 09 29 1991 03:00 -02.00 0 Time Zone 2, no DST -# * Ukrainian Government's Resolution of 25.09.1991, No. 225. -# http://www.uazakon.com/documents/date_21/pg_iwgdoc.htm -# This is an answer. -# -# Since 1992 they had normal DST procedure: -# 03 29 1992 02:00 -02.00 1 DST started -# 09 27 1992 03:00 -02.00 0 DST ended -# * Ukrainian Government's Resolution of 20.03.1992, No. 139. -# http://www.uazakon.com/documents/date_8u/pg_grcasa.htm - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -# Most of Ukraine since 1970 has been like Kiev. -# "Kyiv" is the transliteration of the Ukrainian name, but -# "Kiev" is more common in English. -Zone Europe/Kiev 2:02:04 - LMT 1880 - 2:02:04 - KMT 1924 May 2 # Kiev Mean Time - 2:00 - EET 1930 Jun 21 - 3:00 - MSK 1941 Sep 20 - 1:00 C-Eur CE%sT 1943 Nov 6 - 3:00 Russia MSK/MSD 1990 Jul 1 2:00 - 2:00 1:00 EEST 1991 Sep 29 3:00 - 2:00 E-Eur EE%sT 1995 - 2:00 EU EE%sT -# Ruthenia used CET 1990/1991. -# "Uzhhorod" is the transliteration of the Rusyn/Ukrainian pronunciation, but -# "Uzhgorod" is more common in English. -Zone Europe/Uzhgorod 1:29:12 - LMT 1890 Oct - 1:00 - CET 1940 - 1:00 C-Eur CE%sT 1944 Oct - 1:00 1:00 CEST 1944 Oct 26 - 1:00 - CET 1945 Jun 29 - 3:00 Russia MSK/MSD 1990 - 3:00 - MSK 1990 Jul 1 2:00 - 1:00 - CET 1991 Mar 31 3:00 - 2:00 - EET 1992 - 2:00 E-Eur EE%sT 1995 - 2:00 EU EE%sT -# Zaporozh'ye and eastern Lugansk oblasts observed DST 1990/1991. -# "Zaporizhia" is the transliteration of the Ukrainian name, but -# "Zaporozh'ye" is more common in English. Use the common English -# spelling, except omit the apostrophe as it is not allowed in -# portable Posix file names. -Zone Europe/Zaporozhye 2:20:40 - LMT 1880 - 2:20 - +0220 1924 May 2 - 2:00 - EET 1930 Jun 21 - 3:00 - MSK 1941 Aug 25 - 1:00 C-Eur CE%sT 1943 Oct 25 - 3:00 Russia MSK/MSD 1991 Mar 31 2:00 - 2:00 E-Eur EE%sT 1995 - 2:00 EU EE%sT - -# Vatican City -# See Europe/Rome. - -############################################################################### - -# One source shows that Bulgaria, Cyprus, Finland, and Greece observe DST from -# the last Sunday in March to the last Sunday in September in 1986. -# The source shows Romania changing a day later than everybody else. -# -# According to Bernard Sieloff's source, Poland is in the MET time zone but -# uses the WE DST rules. The Western USSR uses EET+1 and ME DST rules. -# Bernard Sieloff's source claims Romania switches on the same day, but at -# 00:00 standard time (i.e., 01:00 DST). It also claims that Turkey -# switches on the same day, but switches on at 01:00 standard time -# and off at 00:00 standard time (i.e., 01:00 DST) - -# ... -# Date: Wed, 28 Jan 87 16:56:27 -0100 -# From: Tom Hofmann -# ... -# -# ...the European time rules are...standardized since 1981, when -# most European countries started DST. Before that year, only -# a few countries (UK, France, Italy) had DST, each according -# to own national rules. In 1981, however, DST started on -# 'Apr firstSun', and not on 'Mar lastSun' as in the following -# years... -# But also since 1981 there are some more national exceptions -# than listed in 'europe': Switzerland, for example, joined DST -# one year later, Denmark ended DST on 'Oct 1' instead of 'Sep -# lastSun' in 1981 - I don't know how they handle now. -# -# Finally, DST ist always from 'Apr 1' to 'Oct 1' in the -# Soviet Union (as far as I know). -# -# Tom Hofmann, Scientific Computer Center, CIBA-GEIGY AG, -# 4002 Basle, Switzerland -# ... - -# ... -# Date: Wed, 4 Feb 87 22:35:22 +0100 -# From: Dik T. Winter -# ... -# -# The information from Tom Hofmann is (as far as I know) not entirely correct. -# After a request from chongo at amdahl I tried to retrieve all information -# about DST in Europe. I was able to find all from about 1969. -# -# ...standardization on DST in Europe started in about 1977 with switches on -# first Sunday in April and last Sunday in September... -# In 1981 UK joined Europe insofar that -# the starting day for both shifted to last Sunday in March. And from 1982 -# the whole of Europe used DST, with switch dates April 1 and October 1 in -# the Sov[i]et Union. In 1985 the SU reverted to standard Europe[a]n switch -# dates... -# -# It should also be remembered that time-zones are not constants; e.g. -# Portugal switched in 1976 from MET (or CET) to WET with DST... -# Note also that though there were rules for switch dates not -# all countries abided to these dates, and many individual deviations -# occurred, though not since 1982 I believe. Another note: it is always -# assumed that DST is 1 hour ahead of normal time, this need not be the -# case; at least in the Netherlands there have been times when DST was 2 hours -# in advance of normal time. -# -# ... -# dik t. winter, cwi, amsterdam, nederland -# ... - -# From Bob Devine (1988-01-28): -# ... -# Greece: Last Sunday in April to last Sunday in September (iffy on dates). -# Since 1978. Change at midnight. -# ... -# Monaco: has same DST as France. -# ... diff --git a/date_time/leapseconds b/date_time/leapseconds deleted file mode 100644 index 358e741052..0000000000 --- a/date_time/leapseconds +++ /dev/null @@ -1,61 +0,0 @@ -# Allowance for leap seconds added to each time zone file. - -# This file is in the public domain. - -# This file is generated automatically from the data in the public-domain -# leap-seconds.list file, which is copied from: -# ftp://ftp.nist.gov/pub/time/leap-seconds.list -# For more about leap-seconds.list, please see -# The NTP Timescale and Leap Seconds -# https://www.eecis.udel.edu/~mills/leap.html - -# The International Earth Rotation and Reference Systems Service -# periodically uses leap seconds to keep UTC to within 0.9 s of UT1 -# (which measures the true angular orientation of the earth in space); see -# Levine J. Coordinated Universal Time and the leap second. -# URSI Radio Sci Bull. 2016;89(4):30-6. doi:10.23919/URSIRSB.2016.7909995 -# http://ieeexplore.ieee.org/document/7909995/ -# There were no leap seconds before 1972, because the official mechanism -# accounting for the discrepancy between atomic time and the earth's rotation -# did not exist until the early 1970s. - -# The correction (+ or -) is made at the given time, so lines -# will typically look like: -# Leap YEAR MON DAY 23:59:60 + R/S -# or -# Leap YEAR MON DAY 23:59:59 - R/S - -# If the leapsecond is Rolling (R) the given time is local time. -# If the leapsecond is Stationary (S) the given time is UTC. - -# Leap YEAR MONTH DAY HH:MM:SS CORR R/S -Leap 1972 Jun 30 23:59:60 + S -Leap 1972 Dec 31 23:59:60 + S -Leap 1973 Dec 31 23:59:60 + S -Leap 1974 Dec 31 23:59:60 + S -Leap 1975 Dec 31 23:59:60 + S -Leap 1976 Dec 31 23:59:60 + S -Leap 1977 Dec 31 23:59:60 + S -Leap 1978 Dec 31 23:59:60 + S -Leap 1979 Dec 31 23:59:60 + S -Leap 1981 Jun 30 23:59:60 + S -Leap 1982 Jun 30 23:59:60 + S -Leap 1983 Jun 30 23:59:60 + S -Leap 1985 Jun 30 23:59:60 + S -Leap 1987 Dec 31 23:59:60 + S -Leap 1989 Dec 31 23:59:60 + S -Leap 1990 Dec 31 23:59:60 + S -Leap 1992 Jun 30 23:59:60 + S -Leap 1993 Jun 30 23:59:60 + S -Leap 1994 Jun 30 23:59:60 + S -Leap 1995 Dec 31 23:59:60 + S -Leap 1997 Jun 30 23:59:60 + S -Leap 1998 Dec 31 23:59:60 + S -Leap 2005 Dec 31 23:59:60 + S -Leap 2008 Dec 31 23:59:60 + S -Leap 2012 Jun 30 23:59:60 + S -Leap 2015 Jun 30 23:59:60 + S -Leap 2016 Dec 31 23:59:60 + S - -# Updated through IERS Bulletin C55 -# File expires on: 28 December 2018 diff --git a/date_time/northamerica b/date_time/northamerica deleted file mode 100644 index 06db1a805c..0000000000 --- a/date_time/northamerica +++ /dev/null @@ -1,3436 +0,0 @@ -# This file is in the public domain, so clarified as of -# 2009-05-17 by Arthur David Olson. - -# also includes Central America and the Caribbean - -# This file is by no means authoritative; if you think you know better, -# go ahead and edit the file (and please send any changes to -# tz@iana.org for general use in the future). For more, please see -# the file CONTRIBUTING in the tz distribution. - -# From Paul Eggert (1999-03-22): -# A reliable and entertaining source about time zones is -# Derek Howse, Greenwich time and longitude, Philip Wilson Publishers (1997). - -############################################################################### - -# United States - -# From Paul Eggert (1999-03-31): -# Howse writes (pp 121-125) that time zones were invented by -# Professor Charles Ferdinand Dowd (1825-1904), -# Principal of Temple Grove Ladies' Seminary (Saratoga Springs, NY). -# His pamphlet "A System of National Time for Railroads" (1870) -# was the result of his proposals at the Convention of Railroad Trunk Lines -# in New York City (1869-10). His 1870 proposal was based on Washington, DC, -# but in 1872-05 he moved the proposed origin to Greenwich. - -# From Paul Eggert (2018-03-20): -# Dowd's proposal left many details unresolved, such as where to draw -# lines between time zones. The key individual who made time zones -# work in the US was William Frederick Allen - railway engineer, -# managing editor of the Travelers' Guide, and secretary of the -# General Time Convention, a railway standardization group. Allen -# spent months in dialogs with scientific and railway leaders, -# developed a workable plan to institute time zones, and presented it -# to the General Time Convention on 1883-04-11, saying that his plan -# meant "local time would be practically abolished" - a plus for -# railway scheduling. By the next convention on 1883-10-11 nearly all -# railroads had agreed and it took effect on 1883-11-18. That Sunday -# was called the "day of two noons", as some locations observed noon -# twice. Allen witnessed the transition in New York City, writing: -# -# I heard the bells of St. Paul's strike on the old time. Four -# minutes later, obedient to the electrical signal from the Naval -# Observatory ... the time-ball made its rapid descent, the chimes -# of old Trinity rang twelve measured strokes, and local time was -# abandoned, probably forever. -# -# Most of the US soon followed suit. See: -# Bartky IR. The adoption of standard time. Technol Cult 1989 Jan;30(1):25-56. -# http://dx.doi.org/10.2307/3105430 - -# From Paul Eggert (2005-04-16): -# That 1883 transition occurred at 12:00 new time, not at 12:00 old time. -# See p 46 of David Prerau, Seize the daylight, Thunder's Mouth Press (2005). - -# From Paul Eggert (2006-03-22): -# A good source for time zone historical data in the US is -# Thomas G. Shanks, The American Atlas (5th edition), -# San Diego: ACS Publications, Inc. (1991). -# Make sure you have the errata sheet; the book is somewhat useless without it. -# It is the source for most of the pre-1991 US entries below. - -# From Paul Eggert (2001-03-06): -# Daylight Saving Time was first suggested as a joke by Benjamin Franklin -# in his whimsical essay "An Economical Project for Diminishing the Cost -# of Light" published in the Journal de Paris (1784-04-26). -# Not everyone is happy with the results: -# -# I don't really care how time is reckoned so long as there is some -# agreement about it, but I object to being told that I am saving -# daylight when my reason tells me that I am doing nothing of the kind. -# I even object to the implication that I am wasting something -# valuable if I stay in bed after the sun has risen. As an admirer -# of moonlight I resent the bossy insistence of those who want to -# reduce my time for enjoying it. At the back of the Daylight Saving -# scheme I detect the bony, blue-fingered hand of Puritanism, eager -# to push people into bed earlier, and get them up earlier, to make -# them healthy, wealthy and wise in spite of themselves. -# -# -- Robertson Davies, The diary of Samuel Marchbanks, -# Clarke, Irwin (1947), XIX, Sunday -# -# For more about the first ten years of DST in the United States, see -# Robert Garland, Ten years of daylight saving from the Pittsburgh standpoint -# (Carnegie Library of Pittsburgh, 1927). -# http://www.clpgh.org/exhibit/dst.html -# -# Shanks says that DST was called "War Time" in the US in 1918 and 1919. -# However, DST was imposed by the Standard Time Act of 1918, which -# was the first nationwide legal time standard, and apparently -# time was just called "Standard Time" or "Daylight Saving Time". - -# From Arthur David Olson: -# US Daylight Saving Time ended on the last Sunday of *October* in 1974. -# See, for example, the front page of the Saturday, 1974-10-26 -# and Sunday, 1974-10-27 editions of the Washington Post. - -# From Arthur David Olson: -# Before the Uniform Time Act of 1966 took effect in 1967, observance of -# Daylight Saving Time in the US was by local option, except during wartime. - -# From Arthur David Olson (2000-09-25): -# Last night I heard part of a rebroadcast of a 1945 Arch Oboler radio drama. -# In the introduction, Oboler spoke of "Eastern Peace Time." -# An AltaVista search turned up: -# https://web.archive.org/web/20000926032210/http://rowayton.org/rhs/hstaug45.html -# "When the time is announced over the radio now, it is 'Eastern Peace -# Time' instead of the old familiar 'Eastern War Time.' Peace is wonderful." -# (August 1945) by way of confirmation. -# -# From Paul Eggert (2017-09-23): -# This was the V-J Day issue of the Clamdigger, a Rowayton, CT newsletter. - -# From Joseph Gallant citing -# George H. Douglas, _The Early Days of Radio Broadcasting_ (1987): -# At 7 P.M. (Eastern War Time) [on 1945-08-14], the networks were set -# to switch to London for Attlee's address, but the American people -# never got to hear his speech live. According to one press account, -# CBS' Bob Trout was first to announce the word of Japan's surrender, -# but a few seconds later, NBC, ABC and Mutual also flashed the word -# of surrender, all of whom interrupting the bells of Big Ben in -# London which were to precede Mr. Attlee's speech. - -# From Paul Eggert (2003-02-09): It was Robert St John, not Bob Trout. From -# Myrna Oliver's obituary of St John on page B16 of today's Los Angeles Times: -# -# ... a war-weary U.S. clung to radios, awaiting word of Japan's surrender. -# Any announcement from Asia would reach St. John's New York newsroom on a -# wire service teletype machine, which had prescribed signals for major news. -# Associated Press, for example, would ring five bells before spewing out -# typed copy of an important story, and 10 bells for news "of transcendental -# importance." -# -# On Aug. 14, stalling while talking steadily into the NBC networks' open -# microphone, St. John heard five bells and waited only to hear a sixth bell, -# before announcing confidently: "Ladies and gentlemen, World War II is over. -# The Japanese have agreed to our surrender terms." -# -# He had scored a 20-second scoop on other broadcasters. - -# From Arthur David Olson (2005-08-22): -# Paul has been careful to use the "US" rules only in those locations -# that are part of the United States; this reflects the real scope of -# U.S. government action. So even though the "US" rules have changed -# in the latest release, other countries won't be affected. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule US 1918 1919 - Mar lastSun 2:00 1:00 D -Rule US 1918 1919 - Oct lastSun 2:00 0 S -Rule US 1942 only - Feb 9 2:00 1:00 W # War -Rule US 1945 only - Aug 14 23:00u 1:00 P # Peace -Rule US 1945 only - Sep lastSun 2:00 0 S -Rule US 1967 2006 - Oct lastSun 2:00 0 S -Rule US 1967 1973 - Apr lastSun 2:00 1:00 D -Rule US 1974 only - Jan 6 2:00 1:00 D -Rule US 1975 only - Feb 23 2:00 1:00 D -Rule US 1976 1986 - Apr lastSun 2:00 1:00 D -Rule US 1987 2006 - Apr Sun>=1 2:00 1:00 D -Rule US 2007 max - Mar Sun>=8 2:00 1:00 D -Rule US 2007 max - Nov Sun>=1 2:00 0 S - -# From Arthur David Olson, 2005-12-19 -# We generate the files specified below to guard against old files with -# obsolete information being left in the time zone binary directory. -# We limit the list to names that have appeared in previous versions of -# this time zone package. -# We do these as separate Zones rather than as Links to avoid problems if -# a particular place changes whether it observes DST. -# We put these specifications here in the northamerica file both to -# increase the chances that they'll actually get compiled and to -# avoid the need to duplicate the US rules in another file. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone EST -5:00 - EST -Zone MST -7:00 - MST -Zone HST -10:00 - HST -Zone EST5EDT -5:00 US E%sT -Zone CST6CDT -6:00 US C%sT -Zone MST7MDT -7:00 US M%sT -Zone PST8PDT -8:00 US P%sT - -# From U. S. Naval Observatory (1989-01-19): -# USA EASTERN 5 H BEHIND UTC NEW YORK, WASHINGTON -# USA EASTERN 4 H BEHIND UTC APR 3 - OCT 30 -# USA CENTRAL 6 H BEHIND UTC CHICAGO, HOUSTON -# USA CENTRAL 5 H BEHIND UTC APR 3 - OCT 30 -# USA MOUNTAIN 7 H BEHIND UTC DENVER -# USA MOUNTAIN 6 H BEHIND UTC APR 3 - OCT 30 -# USA PACIFIC 8 H BEHIND UTC L.A., SAN FRANCISCO -# USA PACIFIC 7 H BEHIND UTC APR 3 - OCT 30 -# USA ALASKA STD 9 H BEHIND UTC MOST OF ALASKA (AKST) -# USA ALASKA STD 8 H BEHIND UTC APR 3 - OCT 30 (AKDT) -# USA ALEUTIAN 10 H BEHIND UTC ISLANDS WEST OF 170W -# USA " 9 H BEHIND UTC APR 3 - OCT 30 -# USA HAWAII 10 H BEHIND UTC -# USA BERING 11 H BEHIND UTC SAMOA, MIDWAY - -# From Arthur David Olson (1989-01-21): -# The above dates are for 1988. -# Note the "AKST" and "AKDT" abbreviations, the claim that there's -# no DST in Samoa, and the claim that there is DST in Alaska and the -# Aleutians. - -# From Arthur David Olson (1988-02-13): -# Legal standard time zone names, from United States Code (1982 Edition and -# Supplement III), Title 15, Chapter 6, Section 260 and forward. First, names -# up to 1967-04-01 (when most provisions of the Uniform Time Act of 1966 -# took effect), as explained in sections 263 and 261: -# (none) -# United States standard eastern time -# United States standard mountain time -# United States standard central time -# United States standard Pacific time -# (none) -# United States standard Alaska time -# (none) -# Next, names from 1967-04-01 until 1983-11-30 (the date for -# public law 98-181): -# Atlantic standard time -# eastern standard time -# central standard time -# mountain standard time -# Pacific standard time -# Yukon standard time -# Alaska-Hawaii standard time -# Bering standard time -# And after 1983-11-30: -# Atlantic standard time -# eastern standard time -# central standard time -# mountain standard time -# Pacific standard time -# Alaska standard time -# Hawaii-Aleutian standard time -# Samoa standard time -# The law doesn't give abbreviations. -# -# From Paul Eggert (2016-12-19): -# Here are URLs for the 1918 and 1966 legislation: -# http://uscode.house.gov/statviewer.htm?volume=40&page=451 -# http://uscode.house.gov/statviewer.htm?volume=80&page=108 -# Although the 1918 names were officially "United States Standard -# Eastern Time" and similarly for "Central", "Mountain", "Pacific", -# and "Alaska", in practice "Standard" was placed just before "Time", -# as codified in 1966. In practice, Alaska time was abbreviated "AST" -# before 1968. Summarizing the 1967 name changes: -# 1918 names 1967 names -# -08 Standard Pacific Time (PST) Pacific standard time (PST) -# -09 (unofficial) Yukon (YST) Yukon standard time (YST) -# -10 Standard Alaska Time (AST) Alaska-Hawaii standard time (AHST) -# -11 (unofficial) Nome (NST) Bering standard time (BST) -# -# From Paul Eggert (2000-01-08), following a heads-up from Rives McDow: -# Public law 106-564 (2000-12-23) introduced ... "Chamorro Standard Time" -# for time in Guam and the Northern Marianas. See the file "australasia". -# -# From Paul Eggert (2015-04-17): -# HST and HDT are standardized abbreviations for Hawaii-Aleutian -# standard and daylight times. See section 9.47 (p 234) of the -# U.S. Government Printing Office Style Manual (2008) -# https://www.gpo.gov/fdsys/pkg/GPO-STYLEMANUAL-2008/pdf/GPO-STYLEMANUAL-2008.pdf - -# From Arthur David Olson, 2005-08-09 -# The following was signed into law on 2005-08-08. -# -# H.R. 6, Energy Policy Act of 2005, SEC. 110. DAYLIGHT SAVINGS. -# (a) Amendment.--Section 3(a) of the Uniform Time Act of 1966 (15 -# U.S.C. 260a(a)) is amended-- -# (1) by striking "first Sunday of April" and inserting "second -# Sunday of March"; and -# (2) by striking "last Sunday of October" and inserting "first -# Sunday of November'. -# (b) Effective Date.--Subsection (a) shall take effect 1 year after the -# date of enactment of this Act or March 1, 2007, whichever is later. -# (c) Report to Congress.--Not later than 9 months after the effective -# date stated in subsection (b), the Secretary shall report to Congress -# on the impact of this section on energy consumption in the United -# States. -# (d) Right to Revert.--Congress retains the right to revert the -# Daylight Saving Time back to the 2005 time schedules once the -# Department study is complete. - -# US eastern time, represented by New York - -# Connecticut, Delaware, District of Columbia, most of Florida, -# Georgia, southeast Indiana (Dearborn and Ohio counties), eastern Kentucky -# (except America/Kentucky/Louisville below), Maine, Maryland, Massachusetts, -# New Hampshire, New Jersey, New York, North Carolina, Ohio, -# Pennsylvania, Rhode Island, South Carolina, eastern Tennessee, -# Vermont, Virginia, West Virginia - -# From Dave Cantor (2004-11-02): -# Early this summer I had the occasion to visit the Mount Washington -# Observatory weather station atop (of course!) Mount Washington [, NH].... -# One of the staff members said that the station was on Eastern Standard Time -# and didn't change their clocks for Daylight Saving ... so that their -# reports will always have times which are 5 hours behind UTC. - -# From Paul Eggert (2005-08-26): -# According to today's Huntsville Times -# http://www.al.com/news/huntsvilletimes/index.ssf?/base/news/1125047783228320.xml&coll=1 -# a few towns on Alabama's "eastern border with Georgia, such as Phenix City -# in Russell County, Lanett in Chambers County and some towns in Lee County, -# set their watches and clocks on Eastern time." It quotes H.H. "Bubba" -# Roberts, city administrator in Phenix City. as saying "We are in the Central -# time zone, but we do go by the Eastern time zone because so many people work -# in Columbus." -# -# From Paul Eggert (2017-02-22): -# Four cities are involved. The two not mentioned above are Smiths Station -# and Valley. Barbara Brooks, Valley's assistant treasurer, heard it started -# because West Point Pepperell textile mills were in Alabama while the -# corporate office was in Georgia, and residents voted to keep Eastern -# time even after the mills closed. See: Kazek K. Did you know which -# Alabama towns are in a different time zone? al.com 2017-02-06. -# http://www.al.com/living/index.ssf/2017/02/do_you_know_which_alabama_town.html - -# From Paul Eggert (2014-09-06): -# Monthly Notices of the Royal Astronomical Society 44, 4 (1884-02-08), 208 -# says that New York City Hall time was 3 minutes 58.4 seconds fast of -# Eastern time (i.e., -4:56:01.6) just before the 1883 switch. Round to the -# nearest second. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER -Rule NYC 1920 only - Mar lastSun 2:00 1:00 D -Rule NYC 1920 only - Oct lastSun 2:00 0 S -Rule NYC 1921 1966 - Apr lastSun 2:00 1:00 D -Rule NYC 1921 1954 - Sep lastSun 2:00 0 S -Rule NYC 1955 1966 - Oct lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/New_York -4:56:02 - LMT 1883 Nov 18 12:03:58 - -5:00 US E%sT 1920 - -5:00 NYC E%sT 1942 - -5:00 US E%sT 1946 - -5:00 NYC E%sT 1967 - -5:00 US E%sT - -# US central time, represented by Chicago - -# Alabama, Arkansas, Florida panhandle (Bay, Calhoun, Escambia, -# Gulf, Holmes, Jackson, Okaloosa, Santa Rosa, Walton, and -# Washington counties), Illinois, western Indiana -# (Gibson, Jasper, Lake, LaPorte, Newton, Porter, Posey, Spencer, -# Vanderburgh, and Warrick counties), Iowa, most of Kansas, western -# Kentucky, Louisiana, Minnesota, Mississippi, Missouri, eastern -# Nebraska, eastern North Dakota, Oklahoma, eastern South Dakota, -# western Tennessee, most of Texas, Wisconsin - -# From Paul Eggert (2018-01-07): -# In 1869 the Chicago Astronomical Society contracted with the city to keep -# time. Though delayed by the Great Fire, by 1880 a wire ran from the -# Dearborn Observatory (on the University of Chicago campus) to City Hall, -# which then sent signals to police and fire stations. However, railroads got -# their time signals from the Allegheny Observatory, the Madison Observatory, -# the Ann Arbor Observatory, etc., so their clocks did not agree with each -# other or with the city's official time. The confusion took some years to -# clear up. See: -# Moser M. How Chicago gave America its time zones. Chicago. 2018-01-04. -# http://www.chicagomag.com/city-life/January-2018/How-Chicago-Gave-America-Its-Time-Zones/ - -# From Larry M. Smith (2006-04-26) re Wisconsin: -# https://docs.legis.wisconsin.gov/statutes/statutes/175.pdf -# is currently enforced at the 01:00 time of change. Because the local -# "bar time" in the state corresponds to 02:00, a number of citations -# are issued for the "sale of class 'B' alcohol after prohibited -# hours" within the deviated hour of this change every year.... -# -# From Douglas R. Bomberg (2007-03-12): -# Wisconsin has enacted (nearly eleventh-hour) legislation to get WI -# Statue 175 closer in synch with the US Congress' intent.... -# https://docs.legis.wisconsin.gov/2007/related/acts/3 - -# From an email administrator of the City of Fort Pierre, SD (2015-12-21): -# Fort Pierre is technically located in the Mountain time zone as is -# the rest of Stanley County. Most of Stanley County and Fort Pierre -# uses the Central time zone due to doing most of their business in -# Pierre so it simplifies schedules. I have lived in Stanley County -# all my life and it has been that way since I can remember. (43 years!) -# -# From Paul Eggert (2015-12-25): -# Assume this practice predates 1970, so Fort Pierre can use America/Chicago. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER -Rule Chicago 1920 only - Jun 13 2:00 1:00 D -Rule Chicago 1920 1921 - Oct lastSun 2:00 0 S -Rule Chicago 1921 only - Mar lastSun 2:00 1:00 D -Rule Chicago 1922 1966 - Apr lastSun 2:00 1:00 D -Rule Chicago 1922 1954 - Sep lastSun 2:00 0 S -Rule Chicago 1955 1966 - Oct lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Chicago -5:50:36 - LMT 1883 Nov 18 12:09:24 - -6:00 US C%sT 1920 - -6:00 Chicago C%sT 1936 Mar 1 2:00 - -5:00 - EST 1936 Nov 15 2:00 - -6:00 Chicago C%sT 1942 - -6:00 US C%sT 1946 - -6:00 Chicago C%sT 1967 - -6:00 US C%sT -# Oliver County, ND switched from mountain to central time on 1992-10-25. -Zone America/North_Dakota/Center -6:45:12 - LMT 1883 Nov 18 12:14:48 - -7:00 US M%sT 1992 Oct 25 2:00 - -6:00 US C%sT -# Morton County, ND, switched from mountain to central time on -# 2003-10-26, except for the area around Mandan which was already central time. -# See . -# Officially this switch also included part of Sioux County, and -# Jones, Mellette, and Todd Counties in South Dakota; -# but in practice these other counties were already observing central time. -# See . -Zone America/North_Dakota/New_Salem -6:45:39 - LMT 1883 Nov 18 12:14:21 - -7:00 US M%sT 2003 Oct 26 2:00 - -6:00 US C%sT - -# From Josh Findley (2011-01-21): -# ...it appears that Mercer County, North Dakota, changed from the -# mountain time zone to the central time zone at the last transition from -# daylight-saving to standard time (on Nov. 7, 2010): -# https://www.gpo.gov/fdsys/pkg/FR-2010-09-29/html/2010-24376.htm -# http://www.bismarcktribune.com/news/local/article_1eb1b588-c758-11df-b472-001cc4c03286.html - -# From Andy Lipscomb (2011-01-24): -# ...according to the Census Bureau, the largest city is Beulah (although -# it's commonly referred to as Beulah-Hazen, with Hazen being the next -# largest city in Mercer County). Google Maps places Beulah's city hall -# at 47° 15' 51" N, 101° 46' 40" W, which yields an offset of 6h47'07". - -Zone America/North_Dakota/Beulah -6:47:07 - LMT 1883 Nov 18 12:12:53 - -7:00 US M%sT 2010 Nov 7 2:00 - -6:00 US C%sT - -# US mountain time, represented by Denver -# -# Colorado, far western Kansas, Montana, western -# Nebraska, Nevada border (Jackpot, Owyhee, and Mountain City), -# New Mexico, southwestern North Dakota, -# western South Dakota, far western Texas (El Paso County, Hudspeth County, -# and Pine Springs and Nickel Creek in Culberson County), Utah, Wyoming -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER -Rule Denver 1920 1921 - Mar lastSun 2:00 1:00 D -Rule Denver 1920 only - Oct lastSun 2:00 0 S -Rule Denver 1921 only - May 22 2:00 0 S -Rule Denver 1965 1966 - Apr lastSun 2:00 1:00 D -Rule Denver 1965 1966 - Oct lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Denver -6:59:56 - LMT 1883 Nov 18 12:00:04 - -7:00 US M%sT 1920 - -7:00 Denver M%sT 1942 - -7:00 US M%sT 1946 - -7:00 Denver M%sT 1967 - -7:00 US M%sT - -# US Pacific time, represented by Los Angeles -# -# California, northern Idaho (Benewah, Bonner, Boundary, Clearwater, -# Kootenai, Latah, Lewis, Nez Perce, and Shoshone counties, Idaho county -# north of the Salmon River, and the towns of Burgdorf and Warren), -# Nevada (except West Wendover), Oregon (except the northern ¾ of -# Malheur county), and Washington - -# From Paul Eggert (2016-08-20): -# In early February 1948, in response to California's electricity shortage, -# PG&E changed power frequency from 60 to 59.5 Hz during daylight hours, -# causing electric clocks to lose six minutes per day. (This did not change -# legal time, and is not part of the data here.) See: -# Ross SA. An energy crisis from the past: Northern California in 1948. -# Working Paper No. 8, Institute of Governmental Studies, UC Berkeley, -# 1973-11. https://escholarship.org/uc/item/8x22k30c -# -# In another measure to save electricity, DST was instituted from 1948-03-14 -# at 02:01 to 1949-01-16 at 02:00, with the governor having the option to move -# the fallback transition earlier. See pages 3-4 of: -# http://clerk.assembly.ca.gov/sites/clerk.assembly.ca.gov/files/archive/Statutes/1948/48Vol1_Chapters.pdf -# -# In response: -# -# Governor Warren received a torrent of objecting mail, and it is not too much -# to speculate that the objections to Daylight Saving Time were one important -# factor in the defeat of the Dewey-Warren Presidential ticket in California. -# -- Ross, p 25 -# -# On December 8 the governor exercised the option, setting the date to January 1 -# (LA Times 1948-12-09). The transition time was 02:00 (LA Times 1949-01-01). -# -# Despite the controversy, in 1949 California voters approved Proposition 12, -# which established DST from April's last Sunday at 01:00 until September's -# last Sunday at 02:00. This was amended by 1962's Proposition 6, which changed -# the fall-back date to October's last Sunday. See: -# https://repository.uchastings.edu/cgi/viewcontent.cgi?article=1501&context=ca_ballot_props -# https://repository.uchastings.edu/cgi/viewcontent.cgi?article=1636&context=ca_ballot_props -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER -Rule CA 1948 only - Mar 14 2:01 1:00 D -Rule CA 1949 only - Jan 1 2:00 0 S -Rule CA 1950 1966 - Apr lastSun 1:00 1:00 D -Rule CA 1950 1961 - Sep lastSun 2:00 0 S -Rule CA 1962 1966 - Oct lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Los_Angeles -7:52:58 - LMT 1883 Nov 18 12:07:02 - -8:00 US P%sT 1946 - -8:00 CA P%sT 1967 - -8:00 US P%sT - -# Alaska -# AK%sT is the modern abbreviation for -09 per USNO. -# -# From Paul Eggert (2017-06-15): -# Howse writes that Alaska switched from the Julian to the Gregorian calendar, -# and from east-of-GMT to west-of-GMT days, when the US bought it from Russia. -# On Friday, 1867-10-18 (Gregorian), at precisely 15:30 local time, the -# Russian forts and fleet at Sitka fired salutes to mark the ceremony of -# formal transfer. See the Sacramento Daily Union (1867-11-14), p 3, col 2. -# https://cdnc.ucr.edu/cgi-bin/cdnc?a=d&d=SDU18671114.2.12.1 -# Sitka workers did not change their calendars until Sunday, 1867-10-20, -# and so celebrated two Sundays that week. See: Ahllund T (tr Hallamaa P). -# From the memoirs of a Finnish workman. Alaska History. 2006 Fall;21(2):1-25. -# http://alaskahistoricalsociety.org/wp-content/uploads/2016/12/Ahllund-2006-Memoirs-of-a-Finnish-Workman.pdf -# Include only the time zone part of this transition, ignoring the switch -# from Julian to Gregorian, since we can't represent the Julian calendar. -# -# As far as we know, of the locations mentioned below only Sitka was -# permanently inhabited in 1867 by anyone using either calendar. -# (Yakutat was colonized by the Russians in 1799, but the settlement was -# destroyed in 1805 by a Yakutat-kon war party.) Many of Alaska's inhabitants -# were unaware of the US acquisition of Alaska, much less of any calendar or -# time change. However, the Russian-influenced part of Alaska did observe -# Russian time, and it is more accurate to model this than to ignore it. -# The database format requires an exact transition time; use the Russian -# salute as a somewhat-arbitrary time for the formal transfer of control for -# all of Alaska. Sitka's UTC offset is -9:01:13; adjust its 15:30 to the -# local times of other Alaskan locations so that they change simultaneously. - -# From Paul Eggert (2014-07-18): -# One opinion of the early-1980s turmoil in Alaska over time zones and -# daylight saving time appeared as graffiti on a Juneau airport wall: -# "Welcome to Juneau. Please turn your watch back to the 19th century." -# See: Turner W. Alaska's four time zones now two. NY Times 1983-11-01. -# http://www.nytimes.com/1983/11/01/us/alaska-s-four-time-zones-now-two.html -# -# Steve Ferguson (2011-01-31) referred to the following source: -# Norris F. Keeping time in Alaska: national directives, local response. -# Alaska History 2001;16(1-2). -# http://alaskahistoricalsociety.org/discover-alaska/glimpses-of-the-past/keeping-time-in-alaska/ - -# From Arthur David Olson (2011-02-01): -# Here's database-relevant material from the 2001 "Alaska History" article: -# -# On September 20 [1979]...DOT...officials decreed that on April 27, -# 1980, Juneau and other nearby communities would move to Yukon Time. -# Sitka, Petersburg, Wrangell, and Ketchikan, however, would remain on -# Pacific Time. -# -# ...on September 22, 1980, DOT Secretary Neil E. Goldschmidt rescinded the -# Department's September 1979 decision. Juneau and other communities in -# northern Southeast reverted to Pacific Time on October 26. -# -# On October 28 [1983]...the Metlakatla Indian Community Council voted -# unanimously to keep the reservation on Pacific Time. -# -# According to DOT official Joanne Petrie, Indian reservations are not -# bound to follow time zones imposed by neighboring jurisdictions. -# -# (The last is consistent with how the database now handles the Navajo -# Nation.) - -# From Arthur David Olson (2011-02-09): -# I just spoke by phone with a staff member at the Metlakatla Indian -# Community office (using contact information available at -# http://www.commerce.state.ak.us/dca/commdb/CIS.cfm?Comm_Boro_name=Metlakatla -# It's shortly after 1:00 here on the east coast of the United States; -# the staffer said it was shortly after 10:00 there. When I asked whether -# that meant they were on Pacific time, they said no - they were on their -# own time. I asked about daylight saving; they said it wasn't used. I -# did not inquire about practices in the past. - -# From Arthur David Olson (2011-08-17): -# For lack of better information, assume that Metlakatla's -# abandonment of use of daylight saving resulted from the 1983 vote. - -# From Steffen Thorsen (2015-11-09): -# It seems Metlakatla did go off PST on Sunday, November 1, changing -# their time to AKST and are going to follow Alaska's DST, switching -# between AKST and AKDT from now on.... -# https://www.krbd.org/2015/10/30/annette-island-times-they-are-a-changing/ - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Juneau 15:02:19 - LMT 1867 Oct 19 15:33:32 - -8:57:41 - LMT 1900 Aug 20 12:00 - -8:00 - PST 1942 - -8:00 US P%sT 1946 - -8:00 - PST 1969 - -8:00 US P%sT 1980 Apr 27 2:00 - -9:00 US Y%sT 1980 Oct 26 2:00 - -8:00 US P%sT 1983 Oct 30 2:00 - -9:00 US Y%sT 1983 Nov 30 - -9:00 US AK%sT -Zone America/Sitka 14:58:47 - LMT 1867 Oct 19 15:30 - -9:01:13 - LMT 1900 Aug 20 12:00 - -8:00 - PST 1942 - -8:00 US P%sT 1946 - -8:00 - PST 1969 - -8:00 US P%sT 1983 Oct 30 2:00 - -9:00 US Y%sT 1983 Nov 30 - -9:00 US AK%sT -Zone America/Metlakatla 15:13:42 - LMT 1867 Oct 19 15:44:55 - -8:46:18 - LMT 1900 Aug 20 12:00 - -8:00 - PST 1942 - -8:00 US P%sT 1946 - -8:00 - PST 1969 - -8:00 US P%sT 1983 Oct 30 2:00 - -8:00 - PST 2015 Nov 1 2:00 - -9:00 US AK%sT -Zone America/Yakutat 14:41:05 - LMT 1867 Oct 19 15:12:18 - -9:18:55 - LMT 1900 Aug 20 12:00 - -9:00 - YST 1942 - -9:00 US Y%sT 1946 - -9:00 - YST 1969 - -9:00 US Y%sT 1983 Nov 30 - -9:00 US AK%sT -Zone America/Anchorage 14:00:24 - LMT 1867 Oct 19 14:31:37 - -9:59:36 - LMT 1900 Aug 20 12:00 - -10:00 - AST 1942 - -10:00 US A%sT 1967 Apr - -10:00 - AHST 1969 - -10:00 US AH%sT 1983 Oct 30 2:00 - -9:00 US Y%sT 1983 Nov 30 - -9:00 US AK%sT -Zone America/Nome 12:58:22 - LMT 1867 Oct 19 13:29:35 - -11:01:38 - LMT 1900 Aug 20 12:00 - -11:00 - NST 1942 - -11:00 US N%sT 1946 - -11:00 - NST 1967 Apr - -11:00 - BST 1969 - -11:00 US B%sT 1983 Oct 30 2:00 - -9:00 US Y%sT 1983 Nov 30 - -9:00 US AK%sT -Zone America/Adak 12:13:22 - LMT 1867 Oct 19 12:44:35 - -11:46:38 - LMT 1900 Aug 20 12:00 - -11:00 - NST 1942 - -11:00 US N%sT 1946 - -11:00 - NST 1967 Apr - -11:00 - BST 1969 - -11:00 US B%sT 1983 Oct 30 2:00 - -10:00 US AH%sT 1983 Nov 30 - -10:00 US H%sT -# The following switches don't quite make our 1970 cutoff. -# -# Shanks writes that part of southwest Alaska (e.g. Aniak) -# switched from -11:00 to -10:00 on 1968-09-22 at 02:00, -# and another part (e.g. Akiak) made the same switch five weeks later. -# -# From David Flater (2004-11-09): -# In e-mail, 2004-11-02, Ray Hudson, historian/liaison to the Unalaska -# Historic Preservation Commission, provided this information, which -# suggests that Unalaska deviated from statutory time from early 1967 -# possibly until 1983: -# -# Minutes of the Unalaska City Council Meeting, January 10, 1967: -# "Except for St. Paul and Akutan, Unalaska is the only important -# location not on Alaska Standard Time. The following resolution was -# made by William Robinson and seconded by Henry Swanson: Be it -# resolved that the City of Unalaska hereby goes to Alaska Standard -# Time as of midnight Friday, January 13, 1967 (1 A.M. Saturday, -# January 14, Alaska Standard Time.) This resolution was passed with -# three votes for and one against." - -# Hawaii - -# From Arthur David Olson (2010-12-09): -# "Hawaiian Time" by Robert C. Schmitt and Doak C. Cox appears on pages 207-225 -# of volume 26 of The Hawaiian Journal of History (1992). As of 2010-12-09, -# the article is available at -# https://evols.library.manoa.hawaii.edu/bitstream/10524/239/2/JL26215.pdf -# and indicates that standard time was adopted effective noon, January -# 13, 1896 (page 218), that in "1933, the Legislature decreed daylight -# saving for the period between the last Sunday of each April and the -# last Sunday of each September, but less than a month later repealed the -# act," (page 220), that year-round daylight saving time was in effect -# from 1942-02-09 to 1945-09-30 (page 221, with no time of day given for -# when clocks changed) and that clocks were changed by 30 minutes -# effective the second Sunday of June, 1947 (page 219, with no time of -# day given for when clocks changed). A footnote for the 1933 changes -# cites Session Laws of Hawaii 1933, "Act. 90 (approved 26 Apr. 1933) -# and Act 163 (approved 21 May 1933)." - -# From Arthur David Olson (2011-01-19): -# The following is from "Laws of the Territory of Hawaii Passed by the -# Seventeenth Legislature: Regular Session 1933," available (as of -# 2011-01-19) at American University's Pence Law Library. Page 85: "Act -# 90...At 2 o'clock ante meridian of the last Sunday in April of each -# year, the standard time of this Territory shall be advanced one -# hour...This Act shall take effect upon its approval. Approved this 26th -# day of April, A. D. 1933. LAWRENCE M JUDD, Governor of the Territory of -# Hawaii." Page 172: "Act 163...Act 90 of the Session Laws of 1933 is -# hereby repealed...This Act shall take effect upon its approval, upon -# which date the standard time of this Territory shall be restored to -# that existing immediately prior to the taking effect of said Act 90. -# Approved this 21st day of May, A. D. 1933. LAWRENCE M. JUDD, Governor -# of the Territory of Hawaii." -# -# Note that 1933-05-21 was a Sunday. -# We're left to guess the time of day when Act 163 was approved; guess noon. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Honolulu -10:31:26 - LMT 1896 Jan 13 12:00 - -10:30 - HST 1933 Apr 30 2:00 - -10:30 1:00 HDT 1933 May 21 12:00 - -10:30 - HST 1942 Feb 9 2:00 - -10:30 1:00 HDT 1945 Sep 30 2:00 - -10:30 - HST 1947 Jun 8 2:00 - -10:00 - HST - -# Now we turn to US areas that have diverged from the consensus since 1970. - -# Arizona mostly uses MST. - -# From Paul Eggert (2002-10-20): -# -# The information in the rest of this paragraph is derived from the -# Daylight Saving Time web page -# (2002-01-23) -# maintained by the Arizona State Library, Archives and Public Records. -# Between 1944-01-01 and 1944-04-01 the State of Arizona used standard -# time, but by federal law railroads, airlines, bus lines, military -# personnel, and some engaged in interstate commerce continued to -# observe war (i.e., daylight saving) time. The 1944-03-17 Phoenix -# Gazette says that was the date the law changed, and that 04-01 was -# the date the state's clocks would change. In 1945 the State of -# Arizona used standard time all year, again with exceptions only as -# mandated by federal law. Arizona observed DST in 1967, but Arizona -# Laws 1968, ch. 183 (effective 1968-03-21) repealed DST. -# -# Shanks says the 1944 experiment came to an end on 1944-03-17. -# Go with the Arizona State Library instead. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Phoenix -7:28:18 - LMT 1883 Nov 18 11:31:42 - -7:00 US M%sT 1944 Jan 1 0:01 - -7:00 - MST 1944 Apr 1 0:01 - -7:00 US M%sT 1944 Oct 1 0:01 - -7:00 - MST 1967 - -7:00 US M%sT 1968 Mar 21 - -7:00 - MST -# From Arthur David Olson (1988-02-13): -# A writer from the Inter Tribal Council of Arizona, Inc., -# notes in private correspondence dated 1987-12-28 that "Presently, only the -# Navajo Nation participates in the Daylight Saving Time policy, due to its -# large size and location in three states." (The "only" means that other -# tribal nations don't use DST.) -# -# From Paul Eggert (2013-08-26): -# See America/Denver for a zone appropriate for the Navajo Nation. - -# Southern Idaho (Ada, Adams, Bannock, Bear Lake, Bingham, Blaine, -# Boise, Bonneville, Butte, Camas, Canyon, Caribou, Cassia, Clark, -# Custer, Elmore, Franklin, Fremont, Gem, Gooding, Jefferson, Jerome, -# Lemhi, Lincoln, Madison, Minidoka, Oneida, Owyhee, Payette, Power, -# Teton, Twin Falls, Valley, Washington counties, and the southern -# quarter of Idaho county) and eastern Oregon (most of Malheur County) -# switched four weeks late in 1974. -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Boise -7:44:49 - LMT 1883 Nov 18 12:15:11 - -8:00 US P%sT 1923 May 13 2:00 - -7:00 US M%sT 1974 - -7:00 - MST 1974 Feb 3 2:00 - -7:00 US M%sT - -# Indiana -# -# For a map of Indiana's time zone regions, see: -# https://en.wikipedia.org/wiki/Time_in_Indiana -# -# From Paul Eggert (2007-08-17): -# Since 1970, most of Indiana has been like America/Indiana/Indianapolis, -# with the following exceptions: -# -# - Gibson, Jasper, Lake, LaPorte, Newton, Porter, Posey, Spencer, -# Vanderburgh, and Warrick counties have been like America/Chicago. -# -# - Dearborn and Ohio counties have been like America/New_York. -# -# - Clark, Floyd, and Harrison counties have been like -# America/Kentucky/Louisville. -# -# - Crawford, Daviess, Dubois, Knox, Martin, Perry, Pike, Pulaski, Starke, -# and Switzerland counties have their own time zone histories as noted below. -# -# Shanks partitioned Indiana into 345 regions, each with its own time history, -# and wrote "Even newspaper reports present contradictory information." -# Those Hoosiers! Such a flighty and changeable people! -# Fortunately, most of the complexity occurred before our cutoff date of 1970. -# -# Other than Indianapolis, the Indiana place names are so nondescript -# that they would be ambiguous if we left them at the 'America' level. -# So we reluctantly put them all in a subdirectory 'America/Indiana'. - -# From Paul Eggert (2014-06-26): -# https://www.federalregister.gov/articles/2006/01/20/06-563/standard-time-zone-boundary-in-the-state-of-indiana -# says "DOT is relocating the time zone boundary in Indiana to move Starke, -# Pulaski, Knox, Daviess, Martin, Pike, Dubois, and Perry Counties from the -# Eastern Time Zone to the Central Time Zone.... The effective date of -# this rule is 2 a.m. EST Sunday, April 2, 2006, which is the -# changeover date from standard time to Daylight Saving Time." -# Strictly speaking, this meant the affected counties changed their -# clocks twice that night, but this obviously was in error. The intent -# was that 01:59:59 EST be followed by 02:00:00 CDT. - -# From Gwillim Law (2007-02-10): -# The Associated Press has been reporting that Pulaski County, Indiana is -# going to switch from Central to Eastern Time on March 11, 2007.... -# http://www.indystar.com/apps/pbcs.dll/article?AID=/20070207/LOCAL190108/702070524/0/LOCAL - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER -Rule Indianapolis 1941 only - Jun 22 2:00 1:00 D -Rule Indianapolis 1941 1954 - Sep lastSun 2:00 0 S -Rule Indianapolis 1946 1954 - Apr lastSun 2:00 1:00 D -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Indiana/Indianapolis -5:44:38 - LMT 1883 Nov 18 12:15:22 - -6:00 US C%sT 1920 - -6:00 Indianapolis C%sT 1942 - -6:00 US C%sT 1946 - -6:00 Indianapolis C%sT 1955 Apr 24 2:00 - -5:00 - EST 1957 Sep 29 2:00 - -6:00 - CST 1958 Apr 27 2:00 - -5:00 - EST 1969 - -5:00 US E%sT 1971 - -5:00 - EST 2006 - -5:00 US E%sT -# -# Eastern Crawford County, Indiana, left its clocks alone in 1974, -# as well as from 1976 through 2005. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER -Rule Marengo 1951 only - Apr lastSun 2:00 1:00 D -Rule Marengo 1951 only - Sep lastSun 2:00 0 S -Rule Marengo 1954 1960 - Apr lastSun 2:00 1:00 D -Rule Marengo 1954 1960 - Sep lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Indiana/Marengo -5:45:23 - LMT 1883 Nov 18 12:14:37 - -6:00 US C%sT 1951 - -6:00 Marengo C%sT 1961 Apr 30 2:00 - -5:00 - EST 1969 - -5:00 US E%sT 1974 Jan 6 2:00 - -6:00 1:00 CDT 1974 Oct 27 2:00 - -5:00 US E%sT 1976 - -5:00 - EST 2006 - -5:00 US E%sT -# -# Daviess, Dubois, Knox, and Martin Counties, Indiana, -# switched from eastern to central time in April 2006, then switched back -# in November 2007. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER -Rule Vincennes 1946 only - Apr lastSun 2:00 1:00 D -Rule Vincennes 1946 only - Sep lastSun 2:00 0 S -Rule Vincennes 1953 1954 - Apr lastSun 2:00 1:00 D -Rule Vincennes 1953 1959 - Sep lastSun 2:00 0 S -Rule Vincennes 1955 only - May 1 0:00 1:00 D -Rule Vincennes 1956 1963 - Apr lastSun 2:00 1:00 D -Rule Vincennes 1960 only - Oct lastSun 2:00 0 S -Rule Vincennes 1961 only - Sep lastSun 2:00 0 S -Rule Vincennes 1962 1963 - Oct lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Indiana/Vincennes -5:50:07 - LMT 1883 Nov 18 12:09:53 - -6:00 US C%sT 1946 - -6:00 Vincennes C%sT 1964 Apr 26 2:00 - -5:00 - EST 1969 - -5:00 US E%sT 1971 - -5:00 - EST 2006 Apr 2 2:00 - -6:00 US C%sT 2007 Nov 4 2:00 - -5:00 US E%sT -# -# Perry County, Indiana, switched from eastern to central time in April 2006. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER -Rule Perry 1946 only - Apr lastSun 2:00 1:00 D -Rule Perry 1946 only - Sep lastSun 2:00 0 S -Rule Perry 1953 1954 - Apr lastSun 2:00 1:00 D -Rule Perry 1953 1959 - Sep lastSun 2:00 0 S -Rule Perry 1955 only - May 1 0:00 1:00 D -Rule Perry 1956 1963 - Apr lastSun 2:00 1:00 D -Rule Perry 1960 only - Oct lastSun 2:00 0 S -Rule Perry 1961 only - Sep lastSun 2:00 0 S -Rule Perry 1962 1963 - Oct lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Indiana/Tell_City -5:47:03 - LMT 1883 Nov 18 12:12:57 - -6:00 US C%sT 1946 - -6:00 Perry C%sT 1964 Apr 26 2:00 - -5:00 - EST 1969 - -5:00 US E%sT 1971 - -5:00 - EST 2006 Apr 2 2:00 - -6:00 US C%sT -# -# Pike County, Indiana moved from central to eastern time in 1977, -# then switched back in 2006, then switched back again in 2007. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER -Rule Pike 1955 only - May 1 0:00 1:00 D -Rule Pike 1955 1960 - Sep lastSun 2:00 0 S -Rule Pike 1956 1964 - Apr lastSun 2:00 1:00 D -Rule Pike 1961 1964 - Oct lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Indiana/Petersburg -5:49:07 - LMT 1883 Nov 18 12:10:53 - -6:00 US C%sT 1955 - -6:00 Pike C%sT 1965 Apr 25 2:00 - -5:00 - EST 1966 Oct 30 2:00 - -6:00 US C%sT 1977 Oct 30 2:00 - -5:00 - EST 2006 Apr 2 2:00 - -6:00 US C%sT 2007 Nov 4 2:00 - -5:00 US E%sT -# -# Starke County, Indiana moved from central to eastern time in 1991, -# then switched back in 2006. -# From Arthur David Olson (1991-10-28): -# An article on page A3 of the Sunday, 1991-10-27 Washington Post -# notes that Starke County switched from Central time to Eastern time as of -# 1991-10-27. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER -Rule Starke 1947 1961 - Apr lastSun 2:00 1:00 D -Rule Starke 1947 1954 - Sep lastSun 2:00 0 S -Rule Starke 1955 1956 - Oct lastSun 2:00 0 S -Rule Starke 1957 1958 - Sep lastSun 2:00 0 S -Rule Starke 1959 1961 - Oct lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Indiana/Knox -5:46:30 - LMT 1883 Nov 18 12:13:30 - -6:00 US C%sT 1947 - -6:00 Starke C%sT 1962 Apr 29 2:00 - -5:00 - EST 1963 Oct 27 2:00 - -6:00 US C%sT 1991 Oct 27 2:00 - -5:00 - EST 2006 Apr 2 2:00 - -6:00 US C%sT -# -# Pulaski County, Indiana, switched from eastern to central time in -# April 2006 and then switched back in March 2007. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER -Rule Pulaski 1946 1960 - Apr lastSun 2:00 1:00 D -Rule Pulaski 1946 1954 - Sep lastSun 2:00 0 S -Rule Pulaski 1955 1956 - Oct lastSun 2:00 0 S -Rule Pulaski 1957 1960 - Sep lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Indiana/Winamac -5:46:25 - LMT 1883 Nov 18 12:13:35 - -6:00 US C%sT 1946 - -6:00 Pulaski C%sT 1961 Apr 30 2:00 - -5:00 - EST 1969 - -5:00 US E%sT 1971 - -5:00 - EST 2006 Apr 2 2:00 - -6:00 US C%sT 2007 Mar 11 2:00 - -5:00 US E%sT -# -# Switzerland County, Indiana, did not observe DST from 1973 through 2005. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Indiana/Vevay -5:40:16 - LMT 1883 Nov 18 12:19:44 - -6:00 US C%sT 1954 Apr 25 2:00 - -5:00 - EST 1969 - -5:00 US E%sT 1973 - -5:00 - EST 2006 - -5:00 US E%sT - -# From Paul Eggert (2018-03-20): -# The Louisville & Nashville Railroad's 1883-11-18 change occurred at -# 10:00 old local time; train were supposed to come to a standstill -# for precisely 18 minutes. See Bartky Fig. 1 (page 50). It is not -# clear how this matched civil time in Louisville, so for now continue -# to assume Louisville switched at noon new local time, like New York. -# -# Part of Kentucky left its clocks alone in 1974. -# This also includes Clark, Floyd, and Harrison counties in Indiana. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER -Rule Louisville 1921 only - May 1 2:00 1:00 D -Rule Louisville 1921 only - Sep 1 2:00 0 S -Rule Louisville 1941 1961 - Apr lastSun 2:00 1:00 D -Rule Louisville 1941 only - Sep lastSun 2:00 0 S -Rule Louisville 1946 only - Jun 2 2:00 0 S -Rule Louisville 1950 1955 - Sep lastSun 2:00 0 S -Rule Louisville 1956 1960 - Oct lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Kentucky/Louisville -5:43:02 - LMT 1883 Nov 18 12:16:58 - -6:00 US C%sT 1921 - -6:00 Louisville C%sT 1942 - -6:00 US C%sT 1946 - -6:00 Louisville C%sT 1961 Jul 23 2:00 - -5:00 - EST 1968 - -5:00 US E%sT 1974 Jan 6 2:00 - -6:00 1:00 CDT 1974 Oct 27 2:00 - -5:00 US E%sT -# -# Wayne County, Kentucky -# -# From Lake Cumberland LIFE -# http://www.lake-cumberland.com/life/archive/news990129time.shtml -# (1999-01-29) via WKYM-101.7: -# Clinton County has joined Wayne County in asking the DoT to change from -# the Central to the Eastern time zone.... The Wayne County government made -# the same request in December. And while Russell County officials have not -# taken action, the majority of respondents to a poll conducted there in -# August indicated they would like to change to "fast time" also. -# The three Lake Cumberland counties are the farthest east of any U.S. -# location in the Central time zone. -# -# From Rich Wales (2000-08-29): -# After prolonged debate, and despite continuing deep differences of opinion, -# Wayne County (central Kentucky) is switching from Central (-0600) to Eastern -# (-0500) time. They won't "fall back" this year. See Sara Shipley, -# The difference an hour makes, Nando Times (2000-08-29 15:33 -0400). -# -# From Paul Eggert (2001-07-16): -# The final rule was published in the -# Federal Register 65, 160 (2000-08-17), pp 50154-50158. -# https://www.gpo.gov/fdsys/pkg/FR-2000-08-17/html/00-20854.htm -# -Zone America/Kentucky/Monticello -5:39:24 - LMT 1883 Nov 18 12:20:36 - -6:00 US C%sT 1946 - -6:00 - CST 1968 - -6:00 US C%sT 2000 Oct 29 2:00 - -5:00 US E%sT - - -# From Rives McDow (2000-08-30): -# Here ... are all the changes in the US since 1985. -# Kearny County, KS (put all of county on central; -# previously split between MST and CST) ... 1990-10 -# Starke County, IN (from CST to EST) ... 1991-10 -# Oliver County, ND (from MST to CST) ... 1992-10 -# West Wendover, NV (from PST TO MST) ... 1999-10 -# Wayne County, KY (from CST to EST) ... 2000-10 -# -# From Paul Eggert (2001-07-17): -# We don't know where the line used to be within Kearny County, KS, -# so omit that change for now. -# See America/Indiana/Knox for the Starke County, IN change. -# See America/North_Dakota/Center for the Oliver County, ND change. -# West Wendover, NV officially switched from Pacific to mountain time on -# 1999-10-31. See the -# Federal Register 64, 203 (1999-10-21), pp 56705-56707. -# https://www.gpo.gov/fdsys/pkg/FR-1999-10-21/html/99-27240.htm -# However, the Federal Register says that West Wendover already operated -# on mountain time, and the rule merely made this official; -# hence a separate tz entry is not needed. - -# Michigan -# -# From Bob Devine (1988-01-28): -# Michigan didn't observe DST from 1968 to 1973. -# -# From Paul Eggert (1999-03-31): -# Shanks writes that Michigan started using standard time on 1885-09-18, -# but Howse writes (pp 124-125, referring to Popular Astronomy, 1901-01) -# that Detroit kept -# -# local time until 1900 when the City Council decreed that clocks should -# be put back twenty-eight minutes to Central Standard Time. Half the -# city obeyed, half refused. After considerable debate, the decision -# was rescinded and the city reverted to Sun time. A derisive offer to -# erect a sundial in front of the city hall was referred to the -# Committee on Sewers. Then, in 1905, Central time was adopted -# by city vote. -# -# This story is too entertaining to be false, so go with Howse over Shanks. -# -# From Paul Eggert (2001-03-06): -# Garland (1927) writes "Cleveland and Detroit advanced their clocks -# one hour in 1914." This change is not in Shanks. We have no more -# info, so omit this for now. -# -# From Paul Eggert (2017-07-26): -# Although Shanks says Detroit observed DST in 1967 from 06-14 00:01 -# until 10-29 00:01, I now see multiple reports that this is incorrect. -# For example, according to a 50-year anniversary report about the 1967 -# Detroit riots and a major-league doubleheader on 1967-07-23, "By the time -# the last fly ball of the doubleheader settled into the glove of leftfielder -# Lenny Green, it was after 7 p.m. Detroit did not observe daylight saving -# time, so light was already starting to fail. Twilight was made even deeper -# by billowing columns of smoke that ascended in an unbroken wall north of the -# ballpark." See: Dow B. Detroit '67: As violence unfolded, Tigers played two -# at home vs. Yankees. Detroit Free Press 2017-07-23. -# https://www.freep.com/story/sports/mlb/tigers/2017/07/23/detroit-tigers-1967-riot-new-york-yankees/499951001/ -# -# Most of Michigan observed DST from 1973 on, but was a bit late in 1975. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER -Rule Detroit 1948 only - Apr lastSun 2:00 1:00 D -Rule Detroit 1948 only - Sep lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Detroit -5:32:11 - LMT 1905 - -6:00 - CST 1915 May 15 2:00 - -5:00 - EST 1942 - -5:00 US E%sT 1946 - -5:00 Detroit E%sT 1973 - -5:00 US E%sT 1975 - -5:00 - EST 1975 Apr 27 2:00 - -5:00 US E%sT -# -# Dickinson, Gogebic, Iron, and Menominee Counties, Michigan, -# switched from EST to CST/CDT in 1973. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER -Rule Menominee 1946 only - Apr lastSun 2:00 1:00 D -Rule Menominee 1946 only - Sep lastSun 2:00 0 S -Rule Menominee 1966 only - Apr lastSun 2:00 1:00 D -Rule Menominee 1966 only - Oct lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Menominee -5:50:27 - LMT 1885 Sep 18 12:00 - -6:00 US C%sT 1946 - -6:00 Menominee C%sT 1969 Apr 27 2:00 - -5:00 - EST 1973 Apr 29 2:00 - -6:00 US C%sT - -# Navassa -# administered by the US Fish and Wildlife Service -# claimed by US under the provisions of the 1856 Guano Islands Act -# also claimed by Haiti -# occupied 1857/1900 by the Navassa Phosphate Co -# US lighthouse 1917/1996-09 -# currently uninhabited -# see Mark Fineman, "An Isle Rich in Guano and Discord", -# _Los Angeles Times_ (1998-11-10), A1, A10; it cites -# Jimmy Skaggs, _The Great Guano Rush_ (1994). - -################################################################################ - - -# From Paul Eggert (2017-02-10): -# -# Unless otherwise specified, the source for data through 1990 is: -# Thomas G. Shanks and Rique Pottenger, The International Atlas (6th edition), -# San Diego: ACS Publications, Inc. (2003). -# Unfortunately this book contains many errors and cites no sources. -# -# Many years ago Gwillim Law wrote that a good source -# for time zone data was the International Air Transport -# Association's Standard Schedules Information Manual (IATA SSIM), -# published semiannually. Law sent in several helpful summaries -# of the IATA's data after 1990. Except where otherwise noted, -# IATA SSIM is the source for entries after 1990. -# -# Other sources occasionally used include: -# -# Edward W. Whitman, World Time Differences, -# Whitman Publishing Co, 2 Niagara Av, Ealing, London (undated), -# which I found in the UCLA library. -# -# William Willett, The Waste of Daylight, 19th edition -# -# [PDF] (1914-03) -# -# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94 -# . -# -# See the 'europe' file for Greenland. - -# Canada - -# From Alain LaBonté (1994-11-14): -# I post here the time zone abbreviations standardized in Canada -# for both English and French in the CAN/CSA-Z234.4-89 standard.... -# -# UTC Standard time Daylight saving time -# offset French English French English -# -2:30 - - HAT NDT -# -3 - - HAA ADT -# -3:30 HNT NST - - -# -4 HNA AST HAE EDT -# -5 HNE EST HAC CDT -# -6 HNC CST HAR MDT -# -7 HNR MST HAP PDT -# -8 HNP PST HAY YDT -# -9 HNY YST - - -# -# HN: Heure Normale ST: Standard Time -# HA: Heure Avancée DT: Daylight saving Time -# -# A: de l'Atlantique Atlantic -# C: du Centre Central -# E: de l'Est Eastern -# M: Mountain -# N: Newfoundland -# P: du Pacifique Pacific -# R: des Rocheuses -# T: de Terre-Neuve -# Y: du Yukon Yukon -# -# From Paul Eggert (1994-11-22): -# Alas, this sort of thing must be handled by localization software. - -# Unless otherwise specified, the data entries for Canada are all from Shanks -# & Pottenger. - -# From Chris Walton (2006-04-01, 2006-04-25, 2006-06-26, 2007-01-31, -# 2007-03-01): -# The British Columbia government announced yesterday that it will -# adjust daylight savings next year to align with changes in the -# U.S. and the rest of Canada.... -# https://archive.news.gov.bc.ca/releases/news_releases_2005-2009/2006AG0014-000330.htm -# ... -# Nova Scotia -# Daylight saving time will be extended by four weeks starting in 2007.... -# https://www.novascotia.ca/just/regulations/rg2/2006/ma1206.pdf -# -# [For New Brunswick] the new legislation dictates that the time change is to -# be done at 02:00 instead of 00:01. -# https://www.gnb.ca/0062/acts/BBA-2006/Chap-19.pdf -# ... -# Manitoba has traditionally changed the clock every fall at 03:00. -# As of 2006, the transition is to take place one hour earlier at 02:00. -# https://web2.gov.mb.ca/laws/statutes/ccsm/o030e.php -# ... -# [Alberta, Ontario, Quebec] will follow US rules. -# http://www.qp.gov.ab.ca/documents/spring/CH03_06.CFM -# http://www.e-laws.gov.on.ca/DBLaws/Source/Regs/English/2006/R06111_e.htm -# http://www2.publicationsduquebec.gouv.qc.ca/dynamicSearch/telecharge.php?type=5&file=2006C39A.PDF -# ... -# P.E.I. will follow US rules.... -# http://www.assembly.pe.ca/bills/pdf_chapter/62/3/chapter-41.pdf -# ... -# Province of Newfoundland and Labrador.... -# http://www.hoa.gov.nl.ca/hoa/bills/Bill0634.htm -# ... -# Yukon -# https://www.gov.yk.ca/legislation/regs/oic2006_127.pdf -# ... -# N.W.T. will follow US rules. Whoever maintains the government web site -# does not seem to believe in bookmarks. To see the news release, click the -# following link and search for "Daylight Savings Time Change". Press the -# "Daylight Savings Time Change" link; it will fire off a popup using -# JavaScript. -# http://www.exec.gov.nt.ca/currentnews/currentPR.asp?mode=archive -# ... -# Nunavut -# An amendment to the Interpretation Act was registered on February 19/2007.... -# http://action.attavik.ca/home/justice-gn/attach/2007/gaz02part2.pdf - -# From Paul Eggert (2014-10-18): -# H. David Matthews and Mary Vincent's map -# "It's about TIME", _Canadian Geographic_ (September-October 1998) -# http://www.canadiangeographic.ca/Magazine/SO98/alacarte.asp -# contains detailed boundaries for regions observing nonstandard -# time and daylight saving time arrangements in Canada circa 1998. -# -# National Research Council Canada maintains info about time zones and DST. -# https://www.nrc-cnrc.gc.ca/eng/services/time/time_zones.html -# https://www.nrc-cnrc.gc.ca/eng/services/time/faq/index.html#Q5 -# Its unofficial information is often taken from Matthews and Vincent. - -# From Paul Eggert (2006-06-27): -# For now, assume all of DST-observing Canada will fall into line with the -# new US DST rules, - -# From Chris Walton (2011-12-01) -# In the first of Tammy Hardwick's articles -# http://www.ilovecreston.com/?p=articles&t=spec&ar=260 -# she quotes the Friday November 1/1918 edition of the Creston Review. -# The quote includes these two statements: -# 'Sunday the CPR went back to the old system of time...' -# '... The daylight saving scheme was dropped all over Canada at the same time,' -# These statements refer to a transition from daylight time to standard time -# that occurred nationally on Sunday October 27/1918. This transition was -# also documented in the Saturday October 26/1918 edition of the Toronto Star. - -# In light of that evidence, we alter the date from the earlier believed -# Oct 31, to Oct 27, 1918 (and Sunday is a more likely transition day -# than Thursday) in all Canadian rulesets. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Canada 1918 only - Apr 14 2:00 1:00 D -Rule Canada 1918 only - Oct 27 2:00 0 S -Rule Canada 1942 only - Feb 9 2:00 1:00 W # War -Rule Canada 1945 only - Aug 14 23:00u 1:00 P # Peace -Rule Canada 1945 only - Sep 30 2:00 0 S -Rule Canada 1974 1986 - Apr lastSun 2:00 1:00 D -Rule Canada 1974 2006 - Oct lastSun 2:00 0 S -Rule Canada 1987 2006 - Apr Sun>=1 2:00 1:00 D -Rule Canada 2007 max - Mar Sun>=8 2:00 1:00 D -Rule Canada 2007 max - Nov Sun>=1 2:00 0 S - - -# Newfoundland and Labrador - -# From Paul Eggert (2017-10-14): -# Legally Labrador should observe Newfoundland time; see: -# McLeod J. Labrador time - legal or not? St. John's Telegram, 2017-10-07 -# http://www.thetelegram.com/news/local/labrador-time--legal-or-not-154860/ -# Matthews and Vincent (1998) write that the only part of Labrador -# that follows the rules is the southeast corner, including Port Hope -# Simpson and Mary's Harbour, but excluding, say, Black Tickle. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule StJohns 1917 only - Apr 8 2:00 1:00 D -Rule StJohns 1917 only - Sep 17 2:00 0 S -# Whitman gives 1919 Apr 5 and 1920 Apr 5; go with Shanks & Pottenger. -Rule StJohns 1919 only - May 5 23:00 1:00 D -Rule StJohns 1919 only - Aug 12 23:00 0 S -# For 1931-1935 Whitman gives Apr same date; go with Shanks & Pottenger. -Rule StJohns 1920 1935 - May Sun>=1 23:00 1:00 D -Rule StJohns 1920 1935 - Oct lastSun 23:00 0 S -# For 1936-1941 Whitman gives May Sun>=8 and Oct Sun>=1; go with Shanks & -# Pottenger. -Rule StJohns 1936 1941 - May Mon>=9 0:00 1:00 D -Rule StJohns 1936 1941 - Oct Mon>=2 0:00 0 S -# Whitman gives the following transitions: -# 1942 03-01/12-31, 1943 05-30/09-05, 1944 07-10/09-02, 1945 01-01/10-07 -# but go with Shanks & Pottenger and assume they used Canadian rules. -# For 1946-9 Whitman gives May 5,4,9,1 - Oct 1,5,3,2, and for 1950 he gives -# Apr 30 - Sep 24; go with Shanks & Pottenger. -Rule StJohns 1946 1950 - May Sun>=8 2:00 1:00 D -Rule StJohns 1946 1950 - Oct Sun>=2 2:00 0 S -Rule StJohns 1951 1986 - Apr lastSun 2:00 1:00 D -Rule StJohns 1951 1959 - Sep lastSun 2:00 0 S -Rule StJohns 1960 1986 - Oct lastSun 2:00 0 S -# From Paul Eggert (2000-10-02): -# INMS (2000-09-12) says that, since 1988 at least, Newfoundland switches -# at 00:01 local time. For now, assume it started in 1987. - -# From Michael Pelley (2011-09-12): -# We received today, Monday, September 12, 2011, notification that the -# changes to the Newfoundland Standard Time Act have been proclaimed. -# The change in the Act stipulates that the change from Daylight Savings -# Time to Standard Time and from Standard Time to Daylight Savings Time -# now occurs at 2:00AM. -# ... -# http://www.assembly.nl.ca/legislation/sr/annualstatutes/2011/1106.chp.htm -# ... -# MICHAEL PELLEY | Manager of Enterprise Architecture - Solution Delivery -# Office of the Chief Information Officer -# Executive Council -# Government of Newfoundland & Labrador - -Rule StJohns 1987 only - Apr Sun>=1 0:01 1:00 D -Rule StJohns 1987 2006 - Oct lastSun 0:01 0 S -Rule StJohns 1988 only - Apr Sun>=1 0:01 2:00 DD -Rule StJohns 1989 2006 - Apr Sun>=1 0:01 1:00 D -Rule StJohns 2007 2011 - Mar Sun>=8 0:01 1:00 D -Rule StJohns 2007 2010 - Nov Sun>=1 0:01 0 S -# -# St John's has an apostrophe, but Posix file names can't have apostrophes. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/St_Johns -3:30:52 - LMT 1884 - -3:30:52 StJohns N%sT 1918 - -3:30:52 Canada N%sT 1919 - -3:30:52 StJohns N%sT 1935 Mar 30 - -3:30 StJohns N%sT 1942 May 11 - -3:30 Canada N%sT 1946 - -3:30 StJohns N%sT 2011 Nov - -3:30 Canada N%sT - -# most of east Labrador - -# The name 'Happy Valley-Goose Bay' is too long; use 'Goose Bay'. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Goose_Bay -4:01:40 - LMT 1884 # Happy Valley-Goose Bay - -3:30:52 - NST 1918 - -3:30:52 Canada N%sT 1919 - -3:30:52 - NST 1935 Mar 30 - -3:30 - NST 1936 - -3:30 StJohns N%sT 1942 May 11 - -3:30 Canada N%sT 1946 - -3:30 StJohns N%sT 1966 Mar 15 2:00 - -4:00 StJohns A%sT 2011 Nov - -4:00 Canada A%sT - - -# west Labrador, Nova Scotia, Prince Edward I - -# From Brian Inglis (2015-07-20): -# From the historical weather station records available at: -# https://weatherspark.com/history/28351/1971/Sydney-Nova-Scotia-Canada -# Sydney shares the same time history as Glace Bay, so was -# likely to be the same across the island.... -# Sydney, as the capital and most populous location, or Cape Breton, would -# have been better names for the zone had we known this in 1996. - -# From Paul Eggert (2015-07-20): -# Shanks & Pottenger write that since 1970 most of this region has been like -# Halifax. Many locales did not observe peacetime DST until 1972; -# the Cape Breton area, represented by Glace Bay, is the largest we know of -# (Glace Bay was perhaps not the best name choice but no point changing now). -# Shanks & Pottenger also write that Liverpool, NS was the only town -# in Canada to observe DST in 1971 but not 1970; for now we'll assume -# this is a typo. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Halifax 1916 only - Apr 1 0:00 1:00 D -Rule Halifax 1916 only - Oct 1 0:00 0 S -Rule Halifax 1920 only - May 9 0:00 1:00 D -Rule Halifax 1920 only - Aug 29 0:00 0 S -Rule Halifax 1921 only - May 6 0:00 1:00 D -Rule Halifax 1921 1922 - Sep 5 0:00 0 S -Rule Halifax 1922 only - Apr 30 0:00 1:00 D -Rule Halifax 1923 1925 - May Sun>=1 0:00 1:00 D -Rule Halifax 1923 only - Sep 4 0:00 0 S -Rule Halifax 1924 only - Sep 15 0:00 0 S -Rule Halifax 1925 only - Sep 28 0:00 0 S -Rule Halifax 1926 only - May 16 0:00 1:00 D -Rule Halifax 1926 only - Sep 13 0:00 0 S -Rule Halifax 1927 only - May 1 0:00 1:00 D -Rule Halifax 1927 only - Sep 26 0:00 0 S -Rule Halifax 1928 1931 - May Sun>=8 0:00 1:00 D -Rule Halifax 1928 only - Sep 9 0:00 0 S -Rule Halifax 1929 only - Sep 3 0:00 0 S -Rule Halifax 1930 only - Sep 15 0:00 0 S -Rule Halifax 1931 1932 - Sep Mon>=24 0:00 0 S -Rule Halifax 1932 only - May 1 0:00 1:00 D -Rule Halifax 1933 only - Apr 30 0:00 1:00 D -Rule Halifax 1933 only - Oct 2 0:00 0 S -Rule Halifax 1934 only - May 20 0:00 1:00 D -Rule Halifax 1934 only - Sep 16 0:00 0 S -Rule Halifax 1935 only - Jun 2 0:00 1:00 D -Rule Halifax 1935 only - Sep 30 0:00 0 S -Rule Halifax 1936 only - Jun 1 0:00 1:00 D -Rule Halifax 1936 only - Sep 14 0:00 0 S -Rule Halifax 1937 1938 - May Sun>=1 0:00 1:00 D -Rule Halifax 1937 1941 - Sep Mon>=24 0:00 0 S -Rule Halifax 1939 only - May 28 0:00 1:00 D -Rule Halifax 1940 1941 - May Sun>=1 0:00 1:00 D -Rule Halifax 1946 1949 - Apr lastSun 2:00 1:00 D -Rule Halifax 1946 1949 - Sep lastSun 2:00 0 S -Rule Halifax 1951 1954 - Apr lastSun 2:00 1:00 D -Rule Halifax 1951 1954 - Sep lastSun 2:00 0 S -Rule Halifax 1956 1959 - Apr lastSun 2:00 1:00 D -Rule Halifax 1956 1959 - Sep lastSun 2:00 0 S -Rule Halifax 1962 1973 - Apr lastSun 2:00 1:00 D -Rule Halifax 1962 1973 - Oct lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Halifax -4:14:24 - LMT 1902 Jun 15 - -4:00 Halifax A%sT 1918 - -4:00 Canada A%sT 1919 - -4:00 Halifax A%sT 1942 Feb 9 2:00s - -4:00 Canada A%sT 1946 - -4:00 Halifax A%sT 1974 - -4:00 Canada A%sT -Zone America/Glace_Bay -3:59:48 - LMT 1902 Jun 15 - -4:00 Canada A%sT 1953 - -4:00 Halifax A%sT 1954 - -4:00 - AST 1972 - -4:00 Halifax A%sT 1974 - -4:00 Canada A%sT - -# New Brunswick - -# From Paul Eggert (2007-01-31): -# The Time Definition Act -# says they changed at 00:01 through 2006, and -# makes it -# clear that this was the case since at least 1993. -# For now, assume it started in 1993. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Moncton 1933 1935 - Jun Sun>=8 1:00 1:00 D -Rule Moncton 1933 1935 - Sep Sun>=8 1:00 0 S -Rule Moncton 1936 1938 - Jun Sun>=1 1:00 1:00 D -Rule Moncton 1936 1938 - Sep Sun>=1 1:00 0 S -Rule Moncton 1939 only - May 27 1:00 1:00 D -Rule Moncton 1939 1941 - Sep Sat>=21 1:00 0 S -Rule Moncton 1940 only - May 19 1:00 1:00 D -Rule Moncton 1941 only - May 4 1:00 1:00 D -Rule Moncton 1946 1972 - Apr lastSun 2:00 1:00 D -Rule Moncton 1946 1956 - Sep lastSun 2:00 0 S -Rule Moncton 1957 1972 - Oct lastSun 2:00 0 S -Rule Moncton 1993 2006 - Apr Sun>=1 0:01 1:00 D -Rule Moncton 1993 2006 - Oct lastSun 0:01 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Moncton -4:19:08 - LMT 1883 Dec 9 - -5:00 - EST 1902 Jun 15 - -4:00 Canada A%sT 1933 - -4:00 Moncton A%sT 1942 - -4:00 Canada A%sT 1946 - -4:00 Moncton A%sT 1973 - -4:00 Canada A%sT 1993 - -4:00 Moncton A%sT 2007 - -4:00 Canada A%sT - -# Quebec - -# From Paul Eggert (2015-03-24): -# See America/Toronto for most of Quebec, including Montreal. -# -# Matthews and Vincent (1998) also write that Quebec east of the -63 -# meridian is supposed to observe AST, but residents as far east as -# Natashquan use EST/EDT, and residents east of Natashquan use AST. -# The Quebec department of justice writes in -# "The situation in Minganie and Basse-Côte-Nord" -# http://www.justice.gouv.qc.ca/english/publications/generale/temps-minganie-a.htm -# that the coastal strip from just east of Natashquan to Blanc-Sablon -# observes Atlantic standard time all year round. -# https://www.assnat.qc.ca/Media/Process.aspx?MediaId=ANQ.Vigie.Bll.DocumentGenerique_8845en -# says this common practice was codified into law as of 2007. -# For lack of better info, guess this practice began around 1970, contra to -# Shanks & Pottenger who have this region observing AST/ADT. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Blanc-Sablon -3:48:28 - LMT 1884 - -4:00 Canada A%sT 1970 - -4:00 - AST - -# Ontario - -# From Paul Eggert (2006-07-09): -# Shanks & Pottenger write that since 1970 most of Ontario has been like -# Toronto. -# Thunder Bay skipped DST in 1973. -# Many smaller locales did not observe peacetime DST until 1974; -# Nipigon (EST) and Rainy River (CST) are the largest that we know of. -# Far west Ontario is like Winnipeg; far east Quebec is like Halifax. - -# From Mark Brader (2003-07-26): -# [According to the Toronto Star] Orillia, Ontario, adopted DST -# effective Saturday, 1912-06-22, 22:00; the article mentions that -# Port Arthur (now part of Thunder Bay, Ontario) as well as Moose Jaw -# have already done so. In Orillia DST was to run until Saturday, -# 1912-08-31 (no time mentioned), but it was met with considerable -# hostility from certain segments of the public, and was revoked after -# only two weeks - I copied it as Saturday, 1912-07-07, 22:00, but -# presumably that should be -07-06. (1912-06-19, -07-12; also letters -# earlier in June). -# -# Kenora, Ontario, was to abandon DST on 1914-06-01 (-05-21). -# -# From Paul Eggert (2017-07-08): -# For more on Orillia, see: Daubs K. Bold attempt at daylight saving -# time became a comic failure in Orillia. Toronto Star 2017-07-08. -# https://www.thestar.com/news/insight/2017/07/08/bold-attempt-at-daylight-saving-time-became-a-comic-failure-in-orillia.html - -# From Paul Eggert (1997-10-17): -# Mark Brader writes that an article in the 1997-10-14 Toronto Star -# says that Atikokan, Ontario currently does not observe DST, -# but will vote on 11-10 whether to use EST/EDT. -# He also writes that the Ontario Time Act (1990, Chapter T.9) -# http://www.gov.on.ca/MBS/english/publications/statregs/conttext.html -# says that Ontario east of 90W uses EST/EDT, and west of 90W uses CST/CDT. -# Officially Atikokan is therefore on CST/CDT, and most likely this report -# concerns a non-official time observed as a matter of local practice. -# -# From Paul Eggert (2000-10-02): -# Matthews and Vincent (1998) write that Atikokan, Pickle Lake, and -# New Osnaburgh observe CST all year, that Big Trout Lake observes -# CST/CDT, and that Upsala and Shebandowan observe EST/EDT, all in -# violation of the official Ontario rules. -# -# From Paul Eggert (2006-07-09): -# Chris Walton (2006-07-06) mentioned an article by Stephanie MacLellan in the -# 2005-07-21 Chronicle-Journal, which said: -# -# The clocks in Atikokan stay set on standard time year-round. -# This means they spend about half the time on central time and -# the other half on eastern time. -# -# For the most part, the system works, Mayor Dennis Brown said. -# -# "The majority of businesses in Atikokan deal more with Eastern -# Canada, but there are some that deal with Western Canada," he -# said. "I don't see any changes happening here." -# -# Walton also writes "Supposedly Pickle Lake and Mishkeegogamang -# [New Osnaburgh] follow the same practice." - -# From Garry McKinnon (2006-07-14) via Chris Walton: -# I chatted with a member of my board who has an outstanding memory -# and a long history in Atikokan (and in the telecom industry) and he -# can say for certain that Atikokan has been practicing the current -# time keeping since 1952, at least. - -# From Paul Eggert (2006-07-17): -# Shanks & Pottenger say that Atikokan has agreed with Rainy River -# ever since standard time was introduced, but the information from -# McKinnon sounds more authoritative. For now, assume that Atikokan -# switched to EST immediately after WWII era daylight saving time -# ended. This matches the old (less-populous) America/Coral_Harbour -# entry since our cutoff date of 1970, so we can move -# America/Coral_Harbour to the 'backward' file. - -# From Mark Brader (2010-03-06): -# -# Currently the database has: -# -# # Ontario -# -# # From Paul Eggert (2006-07-09): -# # Shanks & Pottenger write that since 1970 most of Ontario has been like -# # Toronto. -# # Thunder Bay skipped DST in 1973. -# # Many smaller locales did not observe peacetime DST until 1974; -# # Nipigon (EST) and Rainy River (CST) are the largest that we know of. -# -# In the (Toronto) Globe and Mail for Saturday, 1955-09-24, in the bottom -# right corner of page 1, it says that Toronto will return to standard -# time at 2 am Sunday morning (which agrees with the database), and that: -# -# The one-hour setback will go into effect throughout most of Ontario, -# except in areas like Windsor which remains on standard time all year. -# -# Windsor is, of course, a lot larger than Nipigon. -# -# I only came across this incidentally. I don't know if Windsor began -# observing DST when Detroit did, or in 1974, or on some other date. -# -# By the way, the article continues by noting that: -# -# Some cities in the United States have pushed the deadline back -# three weeks and will change over from daylight saving in October. - -# From Arthur David Olson (2010-07-17): -# -# "Standard Time and Time Zones in Canada" appeared in -# The Journal of The Royal Astronomical Society of Canada, -# volume 26, number 2 (February 1932) and, as of 2010-07-17, -# was available at -# http://adsabs.harvard.edu/full/1932JRASC..26...49S -# -# It includes the text below (starting on page 57): -# -# A list of the places in Canada using daylight saving time would -# require yearly revision. From information kindly furnished by -# the provincial governments and by the postmasters in many cities -# and towns, it is found that the following places used daylight sav- -# ing in 1930. The information for the province of Quebec is definite, -# for the other provinces only approximate: -# -# Province Daylight saving time used -# Prince Edward Island Not used. -# Nova Scotia In Halifax only. -# New Brunswick In St. John only. -# Quebec In the following places: -# Montreal Lachine -# Quebec Mont-Royal -# Lévis Iberville -# St. Lambert Cap de la Madelèine -# Verdun Loretteville -# Westmount Richmond -# Outremont St. Jérôme -# Longueuil Greenfield Park -# Arvida Waterloo -# Chambly-Canton Beaulieu -# Melbourne La Tuque -# St. Théophile Buckingham -# Ontario Used generally in the cities and towns along -# the southerly part of the province. Not -# used in the northwesterly part. -# Manitoba Not used. -# Saskatchewan In Regina only. -# Alberta Not used. -# British Columbia Not used. -# -# With some exceptions, the use of daylight saving may be said to be limited -# to those cities and towns lying between Quebec city and Windsor, Ont. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Toronto 1919 only - Mar 30 23:30 1:00 D -Rule Toronto 1919 only - Oct 26 0:00 0 S -Rule Toronto 1920 only - May 2 2:00 1:00 D -Rule Toronto 1920 only - Sep 26 0:00 0 S -Rule Toronto 1921 only - May 15 2:00 1:00 D -Rule Toronto 1921 only - Sep 15 2:00 0 S -Rule Toronto 1922 1923 - May Sun>=8 2:00 1:00 D -# Shanks & Pottenger say 1923-09-19; assume it's a typo and that "-16" -# was meant. -Rule Toronto 1922 1926 - Sep Sun>=15 2:00 0 S -Rule Toronto 1924 1927 - May Sun>=1 2:00 1:00 D -# The 1927-to-1939 rules can be expressed more simply as -# Rule Toronto 1927 1937 - Sep Sun>=25 2:00 0 S -# Rule Toronto 1928 1937 - Apr Sun>=25 2:00 1:00 D -# Rule Toronto 1938 1940 - Apr lastSun 2:00 1:00 D -# Rule Toronto 1938 1939 - Sep lastSun 2:00 0 S -# The rules below avoid use of Sun>=25 -# (which pre-2004 versions of zic cannot handle). -Rule Toronto 1927 1932 - Sep lastSun 2:00 0 S -Rule Toronto 1928 1931 - Apr lastSun 2:00 1:00 D -Rule Toronto 1932 only - May 1 2:00 1:00 D -Rule Toronto 1933 1940 - Apr lastSun 2:00 1:00 D -Rule Toronto 1933 only - Oct 1 2:00 0 S -Rule Toronto 1934 1939 - Sep lastSun 2:00 0 S -Rule Toronto 1945 1946 - Sep lastSun 2:00 0 S -Rule Toronto 1946 only - Apr lastSun 2:00 1:00 D -Rule Toronto 1947 1949 - Apr lastSun 0:00 1:00 D -Rule Toronto 1947 1948 - Sep lastSun 0:00 0 S -Rule Toronto 1949 only - Nov lastSun 0:00 0 S -Rule Toronto 1950 1973 - Apr lastSun 2:00 1:00 D -Rule Toronto 1950 only - Nov lastSun 2:00 0 S -Rule Toronto 1951 1956 - Sep lastSun 2:00 0 S -# Shanks & Pottenger say Toronto ended DST a week early in 1971, -# namely on 1971-10-24, but Mark Brader wrote (2003-05-31) that this -# is wrong, and that he had confirmed it by checking the 1971-10-30 -# Toronto Star, which said that DST was ending 1971-10-31 as usual. -Rule Toronto 1957 1973 - Oct lastSun 2:00 0 S - -# From Paul Eggert (2003-07-27): -# Willett (1914-03) writes (p. 17) "In the Cities of Fort William, and -# Port Arthur, Ontario, the principle of the Bill has been in -# operation for the past three years, and in the City of Moose Jaw, -# Saskatchewan, for one year." - -# From David Bryan via Tory Tronrud, Director/Curator, -# Thunder Bay Museum (2003-11-12): -# There is some suggestion, however, that, by-law or not, daylight -# savings time was being practiced in Fort William and Port Arthur -# before 1909.... [I]n 1910, the line between the Eastern and Central -# Time Zones was permanently moved about two hundred miles west to -# include the Thunder Bay area.... When Canada adopted daylight -# savings time in 1916, Fort William and Port Arthur, having done so -# already, did not change their clocks.... During the Second World -# War,... [t]he cities agreed to implement DST during the summer -# months for the remainder of the war years. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Toronto -5:17:32 - LMT 1895 - -5:00 Canada E%sT 1919 - -5:00 Toronto E%sT 1942 Feb 9 2:00s - -5:00 Canada E%sT 1946 - -5:00 Toronto E%sT 1974 - -5:00 Canada E%sT -Zone America/Thunder_Bay -5:57:00 - LMT 1895 - -6:00 - CST 1910 - -5:00 - EST 1942 - -5:00 Canada E%sT 1970 - -5:00 Toronto E%sT 1973 - -5:00 - EST 1974 - -5:00 Canada E%sT -Zone America/Nipigon -5:53:04 - LMT 1895 - -5:00 Canada E%sT 1940 Sep 29 - -5:00 1:00 EDT 1942 Feb 9 2:00s - -5:00 Canada E%sT -Zone America/Rainy_River -6:18:16 - LMT 1895 - -6:00 Canada C%sT 1940 Sep 29 - -6:00 1:00 CDT 1942 Feb 9 2:00s - -6:00 Canada C%sT -Zone America/Atikokan -6:06:28 - LMT 1895 - -6:00 Canada C%sT 1940 Sep 29 - -6:00 1:00 CDT 1942 Feb 9 2:00s - -6:00 Canada C%sT 1945 Sep 30 2:00 - -5:00 - EST - - -# Manitoba - -# From Rob Douglas (2006-04-06): -# the old Manitoba Time Act - as amended by Bill 2, assented to -# March 27, 1987 ... said ... -# "between two o'clock Central Standard Time in the morning of -# the first Sunday of April of each year and two o'clock Central -# Standard Time in the morning of the last Sunday of October next -# following, one hour in advance of Central Standard Time."... -# I believe that the English legislation [of the old time act] had -# been assented to (March 22, 1967).... -# Also, as far as I can tell, there was no order-in-council varying -# the time of Daylight Saving Time for 2005 and so the provisions of -# the 1987 version would apply - the changeover was at 2:00 Central -# Standard Time (i.e. not until 3:00 Central Daylight Time). - -# From Paul Eggert (2006-04-10): -# Shanks & Pottenger say Manitoba switched at 02:00 (not 02:00s) -# starting 1966. Since 02:00s is clearly correct for 1967 on, assume -# it was also 02:00s in 1966. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Winn 1916 only - Apr 23 0:00 1:00 D -Rule Winn 1916 only - Sep 17 0:00 0 S -Rule Winn 1918 only - Apr 14 2:00 1:00 D -Rule Winn 1918 only - Oct 27 2:00 0 S -Rule Winn 1937 only - May 16 2:00 1:00 D -Rule Winn 1937 only - Sep 26 2:00 0 S -Rule Winn 1942 only - Feb 9 2:00 1:00 W # War -Rule Winn 1945 only - Aug 14 23:00u 1:00 P # Peace -Rule Winn 1945 only - Sep lastSun 2:00 0 S -Rule Winn 1946 only - May 12 2:00 1:00 D -Rule Winn 1946 only - Oct 13 2:00 0 S -Rule Winn 1947 1949 - Apr lastSun 2:00 1:00 D -Rule Winn 1947 1949 - Sep lastSun 2:00 0 S -Rule Winn 1950 only - May 1 2:00 1:00 D -Rule Winn 1950 only - Sep 30 2:00 0 S -Rule Winn 1951 1960 - Apr lastSun 2:00 1:00 D -Rule Winn 1951 1958 - Sep lastSun 2:00 0 S -Rule Winn 1959 only - Oct lastSun 2:00 0 S -Rule Winn 1960 only - Sep lastSun 2:00 0 S -Rule Winn 1963 only - Apr lastSun 2:00 1:00 D -Rule Winn 1963 only - Sep 22 2:00 0 S -Rule Winn 1966 1986 - Apr lastSun 2:00s 1:00 D -Rule Winn 1966 2005 - Oct lastSun 2:00s 0 S -Rule Winn 1987 2005 - Apr Sun>=1 2:00s 1:00 D -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Winnipeg -6:28:36 - LMT 1887 Jul 16 - -6:00 Winn C%sT 2006 - -6:00 Canada C%sT - - -# Saskatchewan - -# From Mark Brader (2003-07-26): -# The first actual adoption of DST in Canada was at the municipal -# level. As the [Toronto] Star put it (1912-06-07), "While people -# elsewhere have long been talking of legislation to save daylight, -# the city of Moose Jaw [Saskatchewan] has acted on its own hook." -# DST in Moose Jaw began on Saturday, 1912-06-01 (no time mentioned: -# presumably late evening, as below), and would run until "the end of -# the summer". The discrepancy between municipal time and railroad -# time was noted. - -# From Paul Eggert (2003-07-27): -# Willett (1914-03) notes that DST "has been in operation ... in the -# City of Moose Jaw, Saskatchewan, for one year." - -# From Paul Eggert (2006-03-22): -# Shanks & Pottenger say that since 1970 this region has mostly been as Regina. -# Some western towns (e.g. Swift Current) switched from MST/MDT to CST in 1972. -# Other western towns (e.g. Lloydminster) are like Edmonton. -# Matthews and Vincent (1998) write that Denare Beach and Creighton -# are like Winnipeg, in violation of Saskatchewan law. - -# From W. Jones (1992-11-06): -# The. . .below is based on information I got from our law library, the -# provincial archives, and the provincial Community Services department. -# A precise history would require digging through newspaper archives, and -# since you didn't say what you wanted, I didn't bother. -# -# Saskatchewan is split by a time zone meridian (105W) and over the years -# the boundary became pretty ragged as communities near it reevaluated -# their affiliations in one direction or the other. In 1965 a provincial -# referendum favoured legislating common time practices. -# -# On 15 April 1966 the Time Act (c. T-14, Revised Statutes of -# Saskatchewan 1978) was proclaimed, and established that the eastern -# part of Saskatchewan would use CST year round, that districts in -# northwest Saskatchewan would by default follow CST but could opt to -# follow Mountain Time rules (thus 1 hour difference in the winter and -# zero in the summer), and that districts in southwest Saskatchewan would -# by default follow MT but could opt to follow CST. -# -# It took a few years for the dust to settle (I know one story of a town -# on one time zone having its school in another, such that a mom had to -# serve her family lunch in two shifts), but presently it seems that only -# a few towns on the border with Alberta (e.g. Lloydminster) follow MT -# rules any more; all other districts appear to have used CST year round -# since sometime in the 1960s. - -# From Chris Walton (2006-06-26): -# The Saskatchewan time act which was last updated in 1996 is about 30 pages -# long and rather painful to read. -# http://www.qp.gov.sk.ca/documents/English/Statutes/Statutes/T14.pdf - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Regina 1918 only - Apr 14 2:00 1:00 D -Rule Regina 1918 only - Oct 27 2:00 0 S -Rule Regina 1930 1934 - May Sun>=1 0:00 1:00 D -Rule Regina 1930 1934 - Oct Sun>=1 0:00 0 S -Rule Regina 1937 1941 - Apr Sun>=8 0:00 1:00 D -Rule Regina 1937 only - Oct Sun>=8 0:00 0 S -Rule Regina 1938 only - Oct Sun>=1 0:00 0 S -Rule Regina 1939 1941 - Oct Sun>=8 0:00 0 S -Rule Regina 1942 only - Feb 9 2:00 1:00 W # War -Rule Regina 1945 only - Aug 14 23:00u 1:00 P # Peace -Rule Regina 1945 only - Sep lastSun 2:00 0 S -Rule Regina 1946 only - Apr Sun>=8 2:00 1:00 D -Rule Regina 1946 only - Oct Sun>=8 2:00 0 S -Rule Regina 1947 1957 - Apr lastSun 2:00 1:00 D -Rule Regina 1947 1957 - Sep lastSun 2:00 0 S -Rule Regina 1959 only - Apr lastSun 2:00 1:00 D -Rule Regina 1959 only - Oct lastSun 2:00 0 S -# -Rule Swift 1957 only - Apr lastSun 2:00 1:00 D -Rule Swift 1957 only - Oct lastSun 2:00 0 S -Rule Swift 1959 1961 - Apr lastSun 2:00 1:00 D -Rule Swift 1959 only - Oct lastSun 2:00 0 S -Rule Swift 1960 1961 - Sep lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Regina -6:58:36 - LMT 1905 Sep - -7:00 Regina M%sT 1960 Apr lastSun 2:00 - -6:00 - CST -Zone America/Swift_Current -7:11:20 - LMT 1905 Sep - -7:00 Canada M%sT 1946 Apr lastSun 2:00 - -7:00 Regina M%sT 1950 - -7:00 Swift M%sT 1972 Apr lastSun 2:00 - -6:00 - CST - - -# Alberta - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Edm 1918 1919 - Apr Sun>=8 2:00 1:00 D -Rule Edm 1918 only - Oct 27 2:00 0 S -Rule Edm 1919 only - May 27 2:00 0 S -Rule Edm 1920 1923 - Apr lastSun 2:00 1:00 D -Rule Edm 1920 only - Oct lastSun 2:00 0 S -Rule Edm 1921 1923 - Sep lastSun 2:00 0 S -Rule Edm 1942 only - Feb 9 2:00 1:00 W # War -Rule Edm 1945 only - Aug 14 23:00u 1:00 P # Peace -Rule Edm 1945 only - Sep lastSun 2:00 0 S -Rule Edm 1947 only - Apr lastSun 2:00 1:00 D -Rule Edm 1947 only - Sep lastSun 2:00 0 S -Rule Edm 1967 only - Apr lastSun 2:00 1:00 D -Rule Edm 1967 only - Oct lastSun 2:00 0 S -Rule Edm 1969 only - Apr lastSun 2:00 1:00 D -Rule Edm 1969 only - Oct lastSun 2:00 0 S -Rule Edm 1972 1986 - Apr lastSun 2:00 1:00 D -Rule Edm 1972 2006 - Oct lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Edmonton -7:33:52 - LMT 1906 Sep - -7:00 Edm M%sT 1987 - -7:00 Canada M%sT - - -# British Columbia - -# From Paul Eggert (2006-03-22): -# Shanks & Pottenger write that since 1970 most of this region has -# been like Vancouver. -# Dawson Creek uses MST. Much of east BC is like Edmonton. -# Matthews and Vincent (1998) write that Creston is like Dawson Creek. - -# It seems though that (re: Creston) is not entirely correct: - -# From Chris Walton (2011-12-01): -# There are two areas within the Canadian province of British Columbia -# that do not currently observe daylight saving: -# a) The Creston Valley (includes the town of Creston and surrounding area) -# b) The eastern half of the Peace River Regional District -# (includes the cities of Dawson Creek and Fort St. John) - -# Earlier this year I stumbled across a detailed article about the time -# keeping history of Creston; it was written by Tammy Hardwick who is the -# manager of the Creston & District Museum. The article was written in May 2009. -# http://www.ilovecreston.com/?p=articles&t=spec&ar=260 -# According to the article, Creston has not changed its clocks since June 1918. -# i.e. Creston has been stuck on UT-7 for 93 years. -# Dawson Creek, on the other hand, changed its clocks as recently as April 1972. - -# Unfortunately the exact date for the time change in June 1918 remains -# unknown and will be difficult to ascertain. I e-mailed Tammy a few months -# ago to ask if Sunday June 2 was a reasonable guess. She said it was just -# as plausible as any other date (in June). She also said that after writing -# the article she had discovered another time change in 1916; this is the -# subject of another article which she wrote in October 2010. -# http://www.creston.museum.bc.ca/index.php?module=comments&uop=view_comment&cm+id=56 - -# Here is a summary of the three clock change events in Creston's history: -# 1. 1884 or 1885: adoption of Mountain Standard Time (GMT-7) -# Exact date unknown -# 2. Oct 1916: switch to Pacific Standard Time (GMT-8) -# Exact date in October unknown; Sunday October 1 is a reasonable guess. -# 3. June 1918: switch to Pacific Daylight Time (GMT-7) -# Exact date in June unknown; Sunday June 2 is a reasonable guess. -# note 1: -# On Oct 27/1918 when daylight saving ended in the rest of Canada, -# Creston did not change its clocks. -# note 2: -# During WWII when the Federal Government legislated a mandatory clock change, -# Creston did not oblige. -# note 3: -# There is no guarantee that Creston will remain on Mountain Standard Time -# (UTC-7) forever. -# The subject was debated at least once this year by the town Council. -# http://www.bclocalnews.com/kootenay_rockies/crestonvalleyadvance/news/116760809.html - -# During a period WWII, summer time (Daylight saying) was mandatory in Canada. -# In Creston, that was handled by shifting the area to PST (-8:00) then applying -# summer time to cause the offset to be -7:00, the same as it had been before -# the change. It can be argued that the timezone abbreviation during this -# period should be PDT rather than MST, but that doesn't seem important enough -# (to anyone) to further complicate the rules. - -# The transition dates (and times) are guesses. - -# From Matt Johnson (2015-09-21): -# Fort Nelson, BC, Canada will cancel DST this year. So while previously they -# were aligned with America/Vancouver, they're now aligned with -# America/Dawson_Creek. -# http://www.northernrockies.ca/EN/meta/news/archives/2015/northern-rockies-time-change.html -# -# From Tim Parenti (2015-09-23): -# This requires a new zone for the Northern Rockies Regional Municipality, -# America/Fort_Nelson. The resolution of 2014-12-08 was reached following a -# 2014-11-15 poll with nearly 75% support. Effectively, the municipality has -# been on MST (-0700) like Dawson Creek since it advanced its clocks on -# 2015-03-08. -# -# From Paul Eggert (2015-09-23): -# Shanks says Fort Nelson did not observe DST in 1946, unlike Vancouver. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Vanc 1918 only - Apr 14 2:00 1:00 D -Rule Vanc 1918 only - Oct 27 2:00 0 S -Rule Vanc 1942 only - Feb 9 2:00 1:00 W # War -Rule Vanc 1945 only - Aug 14 23:00u 1:00 P # Peace -Rule Vanc 1945 only - Sep 30 2:00 0 S -Rule Vanc 1946 1986 - Apr lastSun 2:00 1:00 D -Rule Vanc 1946 only - Oct 13 2:00 0 S -Rule Vanc 1947 1961 - Sep lastSun 2:00 0 S -Rule Vanc 1962 2006 - Oct lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Vancouver -8:12:28 - LMT 1884 - -8:00 Vanc P%sT 1987 - -8:00 Canada P%sT -Zone America/Dawson_Creek -8:00:56 - LMT 1884 - -8:00 Canada P%sT 1947 - -8:00 Vanc P%sT 1972 Aug 30 2:00 - -7:00 - MST -Zone America/Fort_Nelson -8:10:47 - LMT 1884 - -8:00 Vanc P%sT 1946 - -8:00 - PST 1947 - -8:00 Vanc P%sT 1987 - -8:00 Canada P%sT 2015 Mar 8 2:00 - -7:00 - MST -Zone America/Creston -7:46:04 - LMT 1884 - -7:00 - MST 1916 Oct 1 - -8:00 - PST 1918 Jun 2 - -7:00 - MST - -# Northwest Territories, Nunavut, Yukon - -# From Paul Eggert (2006-03-22): -# Dawson switched to PST in 1973. Inuvik switched to MST in 1979. -# Mathew Englander (1996-10-07) gives the following refs: -# * 1967. Paragraph 28(34)(g) of the Interpretation Act, S.C. 1967-68, -# c. 7 defines Yukon standard time as UTC-9.... -# see Interpretation Act, R.S.C. 1985, c. I-21, s. 35(1). -# [https://www.canlii.org/en/ca/laws/stat/rsc-1985-c-i-21/latest/rsc-1985-c-i-21.html] -# * C.O. 1973/214 switched Yukon to PST on 1973-10-28 00:00. -# * O.I.C. 1980/02 established DST. -# * O.I.C. 1987/056 changed DST to Apr firstSun 2:00 to Oct lastSun 2:00. - -# From Brian Inglis (2015-04-14): -# -# I tried to trace the history of Yukon time and found the following -# regulations, giving the reference title and URL if found, regulation name, -# and relevant quote if available. Each regulation specifically revokes its -# predecessor. The final reference is to the current Interpretation Act -# authorizing and resulting from these regulatory changes. -# -# Only recent regulations were retrievable via Yukon government site search or -# index, and only some via Canadian legal sources. Other sources used include -# articles titled "Standard Time and Time Zones in Canada" from JRASC via ADS -# Abstracts, cited by ADO for 1932 ..., and updated versions from 1958 and -# 1970 quoted below; each article includes current extracts from provincial -# and territorial ST and DST regulations at the end, summaries and details of -# standard times and daylight saving time at many locations across Canada, -# with time zone maps, tables and calculations for Canadian Sunrise, Sunset, -# and LMST; they also cover many countries and global locations, with a chart -# and table showing current Universal Time offsets, and may be useful as -# another source of information for 1970 and earlier. -# -# * Standard Time and Time Zones in Canada; Smith, C.C.; JRASC, Vol. 26, -# pp.49-77; February 1932; SAO/NASA Astrophysics Data System (ADS) -# http://adsabs.harvard.edu/abs/1932JRASC..26...49S from p.75: -# Yukon Interpretation Ordinance -# Yukon standard time is the local mean time at the one hundred and -# thirty-fifth meridian. -# -# * Standard Time and Time Zones in Canada; Smith, C.C.; Thomson, Malcolm M.; -# JRASC, Vol. 52, pp.193-223; October 1958; SAO/NASA Astrophysics Data System -# (ADS) http://adsabs.harvard.edu/abs/1958JRASC..52..193S from pp.220-1: -# Yukon Interpretation Ordinance, 1955, Chap. 16. -# -# (1) Subject to this section, standard time shall be reckoned as nine -# hours behind Greenwich Time and called Yukon Standard Time. -# -# (2) Notwithstanding subsection (1), the Commissioner may make regulations -# varying the manner of reckoning standard time. -# -# * Yukon Territory Commissioner's Order 1966-20 Interpretation Ordinance -# http://? - no online source found -# -# * Standard Time and Time Zones in Canada; Thomson, Malcolm M.; JRASC, -# Vol. 64, pp.129-162; June 1970; SAO/NASA Astrophysics Data System (ADS) -# http://adsabs.harvard.edu/abs/1970JRASC..64..129T from p.156: Yukon -# Territory Commissioner's Order 1967-59 Interpretation Ordinance ... -# -# 1. Commissioner's Order 1966-20 dated at Whitehorse in the Yukon -# Territory on 27th January, 1966, is hereby revoked. -# -# 2. Yukon (East) Standard Time as defined by section 36 of the -# Interpretation Ordinance from and after mid-night on the 28th day of May, -# 1967 shall be reckoned in the same manner as Pacific Standard Time, that -# is to say, eight hours behind Greenwich Time in the area of the Yukon -# Territory lying east of the 138th degree longitude west. -# -# 3. In the remainder of the Territory, lying west of the 138th degree -# longitude west, Yukon (West) Standard Time shall be reckoned as nine -# hours behind Greenwich Time. -# -# * Yukon Standard Time defined as Pacific Standard Time, YCO 1973/214 -# https://www.canlii.org/en/yk/laws/regu/yco-1973-214/latest/yco-1973-214.html -# C.O. 1973/214 INTERPRETATION ACT ... -# -# 1. Effective October 28, 1973 Commissioner's Order 1967/59 is hereby -# revoked. -# -# 2. Yukon Standard Time as defined by section 36 of the Interpretation -# Act from and after midnight on the twenty-eighth day of October, 1973 -# shall be reckoned in the same manner as Pacific Standard Time, that is -# to say eight hours behind Greenwich Time. -# -# * O.I.C. 1980/02 INTERPRETATION ACT -# http://? - no online source found -# -# * Yukon Daylight Saving Time, YOIC 1987/56 -# https://www.canlii.org/en/yk/laws/regu/yoic-1987-56/latest/yoic-1987-56.html -# O.I.C. 1987/056 INTERPRETATION ACT ... -# -# In every year between -# (a) two o'clock in the morning in the first Sunday in April, and -# (b) two o'clock in the morning in the last Sunday in October, -# Standard Time shall be reckoned as seven hours behind Greenwich Time and -# called Yukon Daylight Saving Time. -# ... -# Dated ... 9th day of March, A.D., 1987. -# -# * Yukon Daylight Saving Time 2006, YOIC 2006/127 -# https://www.canlii.org/en/yk/laws/regu/yoic-2006-127/latest/yoic-2006-127.html -# O.I.C. 2006/127 INTERPRETATION ACT ... -# -# 1. In Yukon each year the time for general purposes shall be 7 hours -# behind Greenwich mean time during the period commencing at two o'clock -# in the forenoon on the second Sunday of March and ending at two o'clock -# in the forenoon on the first Sunday of November and shall be called -# Yukon Daylight Saving Time. -# -# 2. Order-in-Council 1987/56 is revoked. -# -# 3. This order comes into force January 1, 2007. -# -# * Interpretation Act, RSY 2002, c 125 -# https://www.canlii.org/en/yk/laws/stat/rsy-2002-c-125/latest/rsy-2002-c-125.html - -# From Rives McDow (1999-09-04): -# Nunavut ... moved ... to incorporate the whole territory into one time zone. -# Nunavut moves to single time zone Oct. 31 -# http://www.nunatsiaq.com/nunavut/nvt90903_13.html -# -# From Antoine Leca (1999-09-06): -# We then need to create a new timezone for the Kitikmeot region of Nunavut -# to differentiate it from the Yellowknife region. - -# From Paul Eggert (1999-09-20): -# Basic Facts: The New Territory -# http://www.nunavut.com/basicfacts/english/basicfacts_1territory.html -# (1999) reports that Pangnirtung operates on eastern time, -# and that Coral Harbour does not observe DST. We don't know when -# Pangnirtung switched to eastern time; we'll guess 1995. - -# From Rives McDow (1999-11-08): -# On October 31, when the rest of Nunavut went to Central time, -# Pangnirtung wobbled. Here is the result of their wobble: -# -# The following businesses and organizations in Pangnirtung use Central Time: -# -# First Air, Power Corp, Nunavut Construction, Health Center, RCMP, -# Eastern Arctic National Parks, A & D Specialist -# -# The following businesses and organizations in Pangnirtung use Eastern Time: -# -# Hamlet office, All other businesses, Both schools, Airport operator -# -# This has made for an interesting situation there, which warranted the news. -# No one there that I spoke with seems concerned, or has plans to -# change the local methods of keeping time, as it evidently does not -# really interfere with any activities or make things difficult locally. -# They plan to celebrate New Year's turn-over twice, one hour apart, -# so it appears that the situation will last at least that long. -# The Nunavut Intergovernmental Affairs hopes that they will "come to -# their senses", but the locals evidently don't see any problem with -# the current state of affairs. - -# From Michaela Rodrigue, writing in the -# Nunatsiaq News (1999-11-19): -# http://www.nunatsiaqonline.ca/archives/nunavut991130/nvt91119_17.html -# Clyde River, Pangnirtung and Sanikiluaq now operate with two time zones, -# central - or Nunavut time - for government offices, and eastern time -# for municipal offices and schools.... Igloolik [was similar but then] -# made the switch to central time on Saturday, Nov. 6. - -# From Paul Eggert (2000-10-02): -# Matthews and Vincent (1998) say the following, but we lack histories -# for these potential new Zones. -# -# The Canadian Forces station at Alert uses Eastern Time while the -# handful of residents at the Eureka weather station [in the Central -# zone] skip daylight savings. Baffin Island, which is crossed by the -# Central, Eastern and Atlantic Time zones only uses Eastern Time. -# Gjoa Haven, Taloyoak and Pelly Bay all use Mountain instead of -# Central Time and Southampton Island [in the Central zone] is not -# required to use daylight savings. - -# From -# Nunavut now has two time zones (2000-11-10): -# The Nunavut government would allow its employees in Kugluktuk and -# Cambridge Bay to operate on central time year-round, putting them -# one hour behind the rest of Nunavut for six months during the winter. -# At the end of October the two communities had rebelled against -# Nunavut's unified time zone, refusing to shift to eastern time with -# the rest of the territory for the winter. Cambridge Bay remained on -# central time, while Kugluktuk, even farther west, reverted to -# mountain time, which they had used before the advent of Nunavut's -# unified time zone in 1999. -# -# From Rives McDow (2001-01-20), quoting the Nunavut government: -# The preceding decision came into effect at midnight, Saturday Nov 4, 2000. - -# From Paul Eggert (2000-12-04): -# Let's just keep track of the official times for now. - -# From Rives McDow (2001-03-07): -# The premier of Nunavut has issued a ministerial statement advising -# that effective 2001-04-01, the territory of Nunavut will revert -# back to three time zones (mountain, central, and eastern). Of the -# cities in Nunavut, Coral Harbor is the only one that I know of that -# has said it will not observe dst, staying on EST year round. I'm -# checking for more info, and will get back to you if I come up with -# more. -# [Also see (2001-03-09).] - -# From Gwillim Law (2005-05-21): -# According to ... -# http://www.canadiangeographic.ca/Magazine/SO98/geomap.asp -# (from a 1998 Canadian Geographic article), the de facto and de jure time -# for Southampton Island (at the north end of Hudson Bay) is UTC-5 all year -# round. Using Google, it's easy to find other websites that confirm this. -# I wasn't able to find how far back this time regimen goes, but since it -# predates the creation of Nunavut, it probably goes back many years.... -# The Inuktitut name of Coral Harbour is Sallit, but it's rarely used. -# -# From Paul Eggert (2014-10-17): -# For lack of better information, assume that Southampton Island observed -# daylight saving only during wartime. Gwillim Law's email also -# mentioned maps now maintained by National Research Council Canada; -# see above for an up-to-date link. - -# From Chris Walton (2007-03-01): -# ... the community of Resolute (located on Cornwallis Island in -# Nunavut) moved from Central Time to Eastern Time last November. -# Basically the community did not change its clocks at the end of -# daylight saving.... -# http://www.nnsl.com/frames/newspapers/2006-11/nov13_06none.html - -# From Chris Walton (2011-03-21): -# Back in 2007 I initiated the creation of a new "zone file" for Resolute -# Bay. Resolute Bay is a small community located about 900km north of -# the Arctic Circle. The zone file was required because Resolute Bay had -# decided to use UTC-5 instead of UTC-6 for the winter of 2006-2007. -# -# According to new information which I received last week, Resolute Bay -# went back to using UTC-6 in the winter of 2007-2008... -# -# On March 11/2007 most of Canada went onto daylight saving. On March -# 14/2007 I phoned the Resolute Bay hamlet office to do a "time check." I -# talked to somebody that was both knowledgeable and helpful. I was able -# to confirm that Resolute Bay was still operating on UTC-5. It was -# explained to me that Resolute Bay had been on the Eastern Time zone -# (EST) in the winter, and was now back on the Central Time zone (CDT). -# i.e. the time zone had changed twice in the last year but the clocks -# had not moved. The residents had to know which time zone they were in -# so they could follow the correct TV schedule... -# -# On Nov 02/2008 most of Canada went onto standard time. On Nov 03/2008 I -# phoned the Resolute Bay hamlet office...[D]ue to the challenging nature -# of the phone call, I decided to seek out an alternate source of -# information. I found an e-mail address for somebody by the name of -# Stephanie Adams whose job was listed as "Inns North Support Officer for -# Arctic Co-operatives." I was under the impression that Stephanie lived -# and worked in Resolute Bay... -# -# On March 14/2011 I phoned the hamlet office again. I was told that -# Resolute Bay had been using Central Standard Time over the winter of -# 2010-2011 and that the clocks had therefore been moved one hour ahead -# on March 13/2011. The person I talked to was aware that Resolute Bay -# had previously experimented with Eastern Standard Time but he could not -# tell me when the practice had stopped. -# -# On March 17/2011 I searched the Web to find an e-mail address of -# somebody that might be able to tell me exactly when Resolute Bay went -# off Eastern Standard Time. I stumbled on the name "Aziz Kheraj." Aziz -# used to be the mayor of Resolute Bay and he apparently owns half the -# businesses including "South Camp Inn." This website has some info on -# Aziz: -# http://www.uphere.ca/node/493 -# -# I sent Aziz an e-mail asking when Resolute Bay had stopped using -# Eastern Standard Time. -# -# Aziz responded quickly with this: "hi, The time was not changed for the -# 1 year only, the following year, the community went back to the old way -# of "spring ahead-fall behind" currently we are zulu plus 5 hrs and in -# the winter Zulu plus 6 hrs" -# -# This of course conflicted with everything I had ascertained in November 2008. -# -# I sent Aziz a copy of my 2008 e-mail exchange with Stephanie. Aziz -# responded with this: "Hi, Stephanie lives in Winnipeg. I live here, You -# may want to check with the weather office in Resolute Bay or do a -# search on the weather through Env. Canada. web site" -# -# If I had realized the Stephanie did not live in Resolute Bay I would -# never have contacted her. I now believe that all the information I -# obtained in November 2008 should be ignored... -# I apologize for reporting incorrect information in 2008. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule NT_YK 1918 only - Apr 14 2:00 1:00 D -Rule NT_YK 1918 only - Oct 27 2:00 0 S -Rule NT_YK 1919 only - May 25 2:00 1:00 D -Rule NT_YK 1919 only - Nov 1 0:00 0 S -Rule NT_YK 1942 only - Feb 9 2:00 1:00 W # War -Rule NT_YK 1945 only - Aug 14 23:00u 1:00 P # Peace -Rule NT_YK 1945 only - Sep 30 2:00 0 S -Rule NT_YK 1965 only - Apr lastSun 0:00 2:00 DD -Rule NT_YK 1965 only - Oct lastSun 2:00 0 S -Rule NT_YK 1980 1986 - Apr lastSun 2:00 1:00 D -Rule NT_YK 1980 2006 - Oct lastSun 2:00 0 S -Rule NT_YK 1987 2006 - Apr Sun>=1 2:00 1:00 D -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -# aka Panniqtuuq -Zone America/Pangnirtung 0 - -00 1921 # trading post est. - -4:00 NT_YK A%sT 1995 Apr Sun>=1 2:00 - -5:00 Canada E%sT 1999 Oct 31 2:00 - -6:00 Canada C%sT 2000 Oct 29 2:00 - -5:00 Canada E%sT -# formerly Frobisher Bay -Zone America/Iqaluit 0 - -00 1942 Aug # Frobisher Bay est. - -5:00 NT_YK E%sT 1999 Oct 31 2:00 - -6:00 Canada C%sT 2000 Oct 29 2:00 - -5:00 Canada E%sT -# aka Qausuittuq -Zone America/Resolute 0 - -00 1947 Aug 31 # Resolute founded - -6:00 NT_YK C%sT 2000 Oct 29 2:00 - -5:00 - EST 2001 Apr 1 3:00 - -6:00 Canada C%sT 2006 Oct 29 2:00 - -5:00 - EST 2007 Mar 11 3:00 - -6:00 Canada C%sT -# aka Kangiqiniq -Zone America/Rankin_Inlet 0 - -00 1957 # Rankin Inlet founded - -6:00 NT_YK C%sT 2000 Oct 29 2:00 - -5:00 - EST 2001 Apr 1 3:00 - -6:00 Canada C%sT -# aka Iqaluktuuttiaq -Zone America/Cambridge_Bay 0 - -00 1920 # trading post est.? - -7:00 NT_YK M%sT 1999 Oct 31 2:00 - -6:00 Canada C%sT 2000 Oct 29 2:00 - -5:00 - EST 2000 Nov 5 0:00 - -6:00 - CST 2001 Apr 1 3:00 - -7:00 Canada M%sT -Zone America/Yellowknife 0 - -00 1935 # Yellowknife founded? - -7:00 NT_YK M%sT 1980 - -7:00 Canada M%sT -Zone America/Inuvik 0 - -00 1953 # Inuvik founded - -8:00 NT_YK P%sT 1979 Apr lastSun 2:00 - -7:00 NT_YK M%sT 1980 - -7:00 Canada M%sT -Zone America/Whitehorse -9:00:12 - LMT 1900 Aug 20 - -9:00 NT_YK Y%sT 1967 May 28 0:00 - -8:00 NT_YK P%sT 1980 - -8:00 Canada P%sT -Zone America/Dawson -9:17:40 - LMT 1900 Aug 20 - -9:00 NT_YK Y%sT 1973 Oct 28 0:00 - -8:00 NT_YK P%sT 1980 - -8:00 Canada P%sT - - -############################################################################### - -# Mexico - -# From Paul Eggert (2014-12-07): -# The Investigation and Analysis Service of the -# Mexican Library of Congress (MLoC) has published a -# history of Mexican local time (in Spanish) -# http://www.diputados.gob.mx/bibliot/publica/inveyana/polisoc/horver/index.htm -# -# Here are the discrepancies between Shanks & Pottenger (S&P) and the MLoC. -# (In all cases we go with the MLoC.) -# S&P report that Baja was at -8:00 in 1922/1923. -# S&P say the 1930 transition in Baja was 1930-11-16. -# S&P report no DST during summer 1931. -# S&P report a transition at 1932-03-30 23:00, not 1932-04-01. - -# From Gwillim Law (2001-02-20): -# There are some other discrepancies between the Decrees page and the -# tz database. I think they can best be explained by supposing that -# the researchers who prepared the Decrees page failed to find some of -# the relevant documents. - -# From Alan Perry (1996-02-15): -# A guy from our Mexico subsidiary finally found the Presidential Decree -# outlining the timezone changes in Mexico. -# -# ------------- Begin Forwarded Message ------------- -# -# I finally got my hands on the Official Presidential Decree that sets up the -# rules for the DST changes. The rules are: -# -# 1. The country is divided in 3 timezones: -# - Baja California Norte (the Mexico/BajaNorte TZ) -# - Baja California Sur, Nayarit, Sinaloa and Sonora (the Mexico/BajaSur TZ) -# - The rest of the country (the Mexico/General TZ) -# -# 2. From the first Sunday in April at 2:00 AM to the last Sunday in October -# at 2:00 AM, the times in each zone are as follows: -# BajaNorte: GMT+7 -# BajaSur: GMT+6 -# General: GMT+5 -# -# 3. The rest of the year, the times are as follows: -# BajaNorte: GMT+8 -# BajaSur: GMT+7 -# General: GMT+6 -# -# The Decree was published in Mexico's Official Newspaper on January 4th. -# -# -------------- End Forwarded Message -------------- -# From Paul Eggert (1996-06-12): -# For an English translation of the decree, see -# "Diario Oficial: Time Zone Changeover" (1996-01-04). -# http://mexico-travel.com/extra/timezone_eng.html - -# From Rives McDow (1998-10-08): -# The State of Quintana Roo has reverted back to central STD and DST times -# (i.e. UTC -0600 and -0500 as of 1998-08-02). - -# From Rives McDow (2000-01-10): -# Effective April 4, 1999 at 2:00 AM local time, Sonora changed to the time -# zone 5 hours from the International Date Line, and will not observe daylight -# savings time so as to stay on the same time zone as the southern part of -# Arizona year round. - -# From Jesper Nørgaard, translating -# (2001-01-17): -# In Oaxaca, the 55.000 teachers from the Section 22 of the National -# Syndicate of Education Workers, refuse to apply daylight saving each -# year, so that the more than 10,000 schools work at normal hour the -# whole year. - -# From Gwillim Law (2001-01-19): -# ... says -# (translated):... -# January 17, 2000 - The Energy Secretary, Ernesto Martens, announced -# that Summer Time will be reduced from seven to five months, starting -# this year.... -# http://www.publico.com.mx/scripts/texto3.asp?action=pagina&pag=21&pos=p&secc=naci&date=01/17/2001 -# [translated], says "summer time will ... take effect on the first Sunday -# in May, and end on the last Sunday of September. - -# From Arthur David Olson (2001-01-25): -# The 2001-01-24 traditional Washington Post contained the page one -# story "Timely Issue Divides Mexicans."... -# http://www.washingtonpost.com/wp-dyn/articles/A37383-2001Jan23.html -# ... Mexico City Mayor López Obrador "...is threatening to keep -# Mexico City and its 20 million residents on a different time than -# the rest of the country..." In particular, López Obrador would abolish -# observation of Daylight Saving Time. - -# Official statute published by the Energy Department -# http://www.conae.gob.mx/ahorro/decretohorver2001.html#decre -# (2001-02-01) shows Baja and Chihauhua as still using US DST rules, -# and Sonora with no DST. This was reported by Jesper Nørgaard (2001-02-03). - -# From Paul Eggert (2001-03-03): -# -# http://www.latimes.com/news/nation/20010303/t000018766.html -# James F. Smith writes in today's LA Times -# * Sonora will continue to observe standard time. -# * Last week Mexico City's mayor Andrés Manuel López Obrador decreed that -# the Federal District will not adopt DST. -# * 4 of 16 district leaders announced they'll ignore the decree. -# * The decree does not affect federal-controlled facilities including -# the airport, banks, hospitals, and schools. -# -# For now we'll assume that the Federal District will bow to federal rules. - -# From Jesper Nørgaard (2001-04-01): -# I found some references to the Mexican application of daylight -# saving, which modifies what I had already sent you, stating earlier -# that a number of northern Mexican states would go on daylight -# saving. The modification reverts this to only cover Baja California -# (Norte), while all other states (except Sonora, who has no daylight -# saving all year) will follow the original decree of president -# Vicente Fox, starting daylight saving May 6, 2001 and ending -# September 30, 2001. -# References: "Diario de Monterrey" -# Palabra (2001-03-31) - -# From Reuters (2001-09-04): -# Mexico's Supreme Court on Tuesday declared that daylight savings was -# unconstitutional in Mexico City, creating the possibility the -# capital will be in a different time zone from the rest of the nation -# next year.... The Supreme Court's ruling takes effect at 2:00 -# a.m. (0800 GMT) on Sept. 30, when Mexico is scheduled to revert to -# standard time. "This is so residents of the Federal District are not -# subject to unexpected time changes," a statement from the court said. - -# From Jesper Nørgaard Welen (2002-03-12): -# ... consulting my local grocery store(!) and my coworkers, they all insisted -# that a new decision had been made to reinstate US style DST in Mexico.... -# http://www.conae.gob.mx/ahorro/horaver2001_m1_2002.html (2002-02-20) -# confirms this. Sonora as usual is the only state where DST is not applied. - -# From Steffen Thorsen (2009-12-28): -# -# Steffen Thorsen wrote: -# > Mexico's House of Representatives has approved a proposal for northern -# > Mexico's border cities to share the same daylight saving schedule as -# > the United States. -# Now this has passed both the Congress and the Senate, so starting from -# 2010, some border regions will be the same: -# http://www.signonsandiego.com/news/2009/dec/28/clocks-will-match-both-sides-border/ -# http://www.elmananarey.com/diario/noticia/nacional/noticias/empatan_horario_de_frontera_con_eu/621939 -# (Spanish) -# -# Could not find the new law text, but the proposed law text changes are here: -# http://gaceta.diputados.gob.mx/Gaceta/61/2009/dic/20091210-V.pdf -# (Gaceta Parlamentaria) -# -# There is also a list of the votes here: -# http://gaceta.diputados.gob.mx/Gaceta/61/2009/dic/V2-101209.html -# -# Our page: -# https://www.timeanddate.com/news/time/north-mexico-dst-change.html - -# From Arthur David Olson (2010-01-20): -# The page -# http://dof.gob.mx/nota_detalle.php?codigo=5127480&fecha=06/01/2010 -# includes this text: -# En los municipios fronterizos de Tijuana y Mexicali en Baja California; -# Juárez y Ojinaga en Chihuahua; Acuña y Piedras Negras en Coahuila; -# Anáhuac en Nuevo León; y Nuevo Laredo, Reynosa y Matamoros en -# Tamaulipas, la aplicación de este horario estacional surtirá efecto -# desde las dos horas del segundo domingo de marzo y concluirá a las dos -# horas del primer domingo de noviembre. -# En los municipios fronterizos que se encuentren ubicados en la franja -# fronteriza norte en el territorio comprendido entre la línea -# internacional y la línea paralela ubicada a una distancia de veinte -# kilómetros, así como la Ciudad de Ensenada, Baja California, hacia el -# interior del país, la aplicación de este horario estacional surtirá -# efecto desde las dos horas del segundo domingo de marzo y concluirá a -# las dos horas del primer domingo de noviembre. - -# From Steffen Thorsen (2014-12-08), translated by Gwillim Law: -# The Mexican state of Quintana Roo will likely change to EST in 2015. -# -# http://www.unioncancun.mx/articulo/2014/12/04/medio-ambiente/congreso-aprueba-una-hora-mas-de-sol-en-qroo -# "With this change, the time conflict that has existed between the municipios -# of Quintana Roo and the municipio of Felipe Carrillo Puerto may come to an -# end. The latter declared itself in rebellion 15 years ago when a time change -# was initiated in Mexico, and since then it has refused to change its time -# zone along with the rest of the country." -# -# From Steffen Thorsen (2015-01-14), translated by Gwillim Law: -# http://sipse.com/novedades/confirman-aplicacion-de-nueva-zona-horaria-para-quintana-roo-132331.html -# "...the new time zone will come into effect at two o'clock on the first Sunday -# of February, when we will have to advance the clock one hour from its current -# time..." -# Also, the new zone will not use DST. -# -# From Carlos Raúl Perasso (2015-02-02): -# The decree that modifies the Mexican Hour System Law has finally -# been published at the Diario Oficial de la Federación -# http://www.dof.gob.mx/nota_detalle.php?codigo=5380123&fecha=31/01/2015 -# It establishes 5 zones for Mexico: -# 1- Zona Centro (Central Zone): Corresponds to longitude 90 W, -# includes most of Mexico, excluding what's mentioned below. -# 2- Zona Pacífico (Pacific Zone): Longitude 105 W, includes the -# states of Baja California Sur; Chihuahua; Nayarit (excluding Bahía -# de Banderas which lies in Central Zone); Sinaloa and Sonora. -# 3- Zona Noroeste (Northwest Zone): Longitude 120 W, includes the -# state of Baja California. -# 4- Zona Sureste (Southeast Zone): Longitude 75 W, includes the state -# of Quintana Roo. -# 5- The islands, reefs and keys shall take their timezone from the -# longitude they are located at. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Mexico 1939 only - Feb 5 0:00 1:00 D -Rule Mexico 1939 only - Jun 25 0:00 0 S -Rule Mexico 1940 only - Dec 9 0:00 1:00 D -Rule Mexico 1941 only - Apr 1 0:00 0 S -Rule Mexico 1943 only - Dec 16 0:00 1:00 W # War -Rule Mexico 1944 only - May 1 0:00 0 S -Rule Mexico 1950 only - Feb 12 0:00 1:00 D -Rule Mexico 1950 only - Jul 30 0:00 0 S -Rule Mexico 1996 2000 - Apr Sun>=1 2:00 1:00 D -Rule Mexico 1996 2000 - Oct lastSun 2:00 0 S -Rule Mexico 2001 only - May Sun>=1 2:00 1:00 D -Rule Mexico 2001 only - Sep lastSun 2:00 0 S -Rule Mexico 2002 max - Apr Sun>=1 2:00 1:00 D -Rule Mexico 2002 max - Oct lastSun 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -# Quintana Roo; represented by Cancún -Zone America/Cancun -5:47:04 - LMT 1922 Jan 1 0:12:56 - -6:00 - CST 1981 Dec 23 - -5:00 Mexico E%sT 1998 Aug 2 2:00 - -6:00 Mexico C%sT 2015 Feb 1 2:00 - -5:00 - EST -# Campeche, Yucatán; represented by Mérida -Zone America/Merida -5:58:28 - LMT 1922 Jan 1 0:01:32 - -6:00 - CST 1981 Dec 23 - -5:00 - EST 1982 Dec 2 - -6:00 Mexico C%sT -# Coahuila, Nuevo León, Tamaulipas (near US border) -# This includes the following municipalities: -# in Coahuila: Ocampo, Acuña, Zaragoza, Jiménez, Piedras Negras, Nava, -# Guerrero, Hidalgo. -# in Nuevo León: Anáhuac, Los Aldama. -# in Tamaulipas: Nuevo Laredo, Guerrero, Mier, Miguel Alemán, Camargo, -# Gustavo Díaz Ordaz, Reynosa, Río Bravo, Valle Hermoso, Matamoros. -# See: Inicia mañana Horario de Verano en zona fronteriza, El Universal, -# 2016-03-12 -# http://www.eluniversal.com.mx/articulo/estados/2016/03/12/inicia-manana-horario-de-verano-en-zona-fronteriza -Zone America/Matamoros -6:40:00 - LMT 1921 Dec 31 23:20:00 - -6:00 - CST 1988 - -6:00 US C%sT 1989 - -6:00 Mexico C%sT 2010 - -6:00 US C%sT -# Durango; Coahuila, Nuevo León, Tamaulipas (away from US border) -Zone America/Monterrey -6:41:16 - LMT 1921 Dec 31 23:18:44 - -6:00 - CST 1988 - -6:00 US C%sT 1989 - -6:00 Mexico C%sT -# Central Mexico -Zone America/Mexico_City -6:36:36 - LMT 1922 Jan 1 0:23:24 - -7:00 - MST 1927 Jun 10 23:00 - -6:00 - CST 1930 Nov 15 - -7:00 - MST 1931 May 1 23:00 - -6:00 - CST 1931 Oct - -7:00 - MST 1932 Apr 1 - -6:00 Mexico C%sT 2001 Sep 30 2:00 - -6:00 - CST 2002 Feb 20 - -6:00 Mexico C%sT -# Chihuahua (near US border) -# This includes the municipalities of Janos, Ascensión, Juárez, Guadalupe, -# Práxedis G Guerrero, Coyame del Sotol, Ojinaga, and Manuel Benavides. -# (See the 2016-03-12 El Universal source mentioned above.) -Zone America/Ojinaga -6:57:40 - LMT 1922 Jan 1 0:02:20 - -7:00 - MST 1927 Jun 10 23:00 - -6:00 - CST 1930 Nov 15 - -7:00 - MST 1931 May 1 23:00 - -6:00 - CST 1931 Oct - -7:00 - MST 1932 Apr 1 - -6:00 - CST 1996 - -6:00 Mexico C%sT 1998 - -6:00 - CST 1998 Apr Sun>=1 3:00 - -7:00 Mexico M%sT 2010 - -7:00 US M%sT -# Chihuahua (away from US border) -Zone America/Chihuahua -7:04:20 - LMT 1921 Dec 31 23:55:40 - -7:00 - MST 1927 Jun 10 23:00 - -6:00 - CST 1930 Nov 15 - -7:00 - MST 1931 May 1 23:00 - -6:00 - CST 1931 Oct - -7:00 - MST 1932 Apr 1 - -6:00 - CST 1996 - -6:00 Mexico C%sT 1998 - -6:00 - CST 1998 Apr Sun>=1 3:00 - -7:00 Mexico M%sT -# Sonora -Zone America/Hermosillo -7:23:52 - LMT 1921 Dec 31 23:36:08 - -7:00 - MST 1927 Jun 10 23:00 - -6:00 - CST 1930 Nov 15 - -7:00 - MST 1931 May 1 23:00 - -6:00 - CST 1931 Oct - -7:00 - MST 1932 Apr 1 - -6:00 - CST 1942 Apr 24 - -7:00 - MST 1949 Jan 14 - -8:00 - PST 1970 - -7:00 Mexico M%sT 1999 - -7:00 - MST - -# From Alexander Krivenyshev (2010-04-21): -# According to news, Bahía de Banderas (Mexican state of Nayarit) -# changed time zone UTC-7 to new time zone UTC-6 on April 4, 2010 (to -# share the same time zone as nearby city Puerto Vallarta, Jalisco). -# -# (Spanish) -# Bahía de Banderas homologa su horario al del centro del -# país, a partir de este domingo -# http://www.nayarit.gob.mx/notes.asp?id=20748 -# -# Bahía de Banderas homologa su horario con el del Centro del -# País -# http://www.bahiadebanderas.gob.mx/principal/index.php?option=com_content&view=article&id=261:bahia-de-banderas-homologa-su-horario-con-el-del-centro-del-pais&catid=42:comunicacion-social&Itemid=50 -# -# (English) -# Puerto Vallarta and Bahía de Banderas: One Time Zone -# http://virtualvallarta.com/puertovallarta/puertovallarta/localnews/2009-12-03-Puerto-Vallarta-and-Bahia-de-Banderas-One-Time-Zone.shtml -# http://www.worldtimezone.com/dst_news/dst_news_mexico08.html -# -# "Mexico's Senate approved the amendments to the Mexican Schedule System that -# will allow Bahía de Banderas and Puerto Vallarta to share the same time -# zone ..." -# Baja California Sur, Nayarit, Sinaloa - -# From Arthur David Olson (2010-05-01): -# Use "Bahia_Banderas" to keep the name to fourteen characters. - -# Mazatlán -Zone America/Mazatlan -7:05:40 - LMT 1921 Dec 31 23:54:20 - -7:00 - MST 1927 Jun 10 23:00 - -6:00 - CST 1930 Nov 15 - -7:00 - MST 1931 May 1 23:00 - -6:00 - CST 1931 Oct - -7:00 - MST 1932 Apr 1 - -6:00 - CST 1942 Apr 24 - -7:00 - MST 1949 Jan 14 - -8:00 - PST 1970 - -7:00 Mexico M%sT - -# Bahía de Banderas -Zone America/Bahia_Banderas -7:01:00 - LMT 1921 Dec 31 23:59:00 - -7:00 - MST 1927 Jun 10 23:00 - -6:00 - CST 1930 Nov 15 - -7:00 - MST 1931 May 1 23:00 - -6:00 - CST 1931 Oct - -7:00 - MST 1932 Apr 1 - -6:00 - CST 1942 Apr 24 - -7:00 - MST 1949 Jan 14 - -8:00 - PST 1970 - -7:00 Mexico M%sT 2010 Apr 4 2:00 - -6:00 Mexico C%sT - -# Baja California -Zone America/Tijuana -7:48:04 - LMT 1922 Jan 1 0:11:56 - -7:00 - MST 1924 - -8:00 - PST 1927 Jun 10 23:00 - -7:00 - MST 1930 Nov 15 - -8:00 - PST 1931 Apr 1 - -8:00 1:00 PDT 1931 Sep 30 - -8:00 - PST 1942 Apr 24 - -8:00 1:00 PWT 1945 Aug 14 23:00u - -8:00 1:00 PPT 1945 Nov 12 # Peace - -8:00 - PST 1948 Apr 5 - -8:00 1:00 PDT 1949 Jan 14 - -8:00 - PST 1954 - -8:00 CA P%sT 1961 - -8:00 - PST 1976 - -8:00 US P%sT 1996 - -8:00 Mexico P%sT 2001 - -8:00 US P%sT 2002 Feb 20 - -8:00 Mexico P%sT 2010 - -8:00 US P%sT -# From Paul Eggert (2006-03-22): -# Formerly there was an America/Ensenada zone, which differed from -# America/Tijuana only in that it did not observe DST from 1976 -# through 1995. This was as per Shanks (1999). But Shanks & Pottenger say -# Ensenada did not observe DST from 1948 through 1975. Guy Harris reports -# that the 1987 OAG says "Only Ensenada, Mexicali, San Felipe and -# Tijuana observe DST," which agrees with Shanks & Pottenger but implies that -# DST-observance was a town-by-town matter back then. This concerns -# data after 1970 so most likely there should be at least one Zone -# other than America/Tijuana for Baja, but it's not clear yet what its -# name or contents should be. -# -# From Paul Eggert (2015-10-08): -# Formerly there was an America/Santa_Isabel zone, but this appears to -# have come from a misreading of -# http://dof.gob.mx/nota_detalle.php?codigo=5127480&fecha=06/01/2010 -# It has been moved to the 'backward' file. -# -# -# Revillagigedo Is -# no information - -############################################################################### - -# Anguilla -# Antigua and Barbuda -# See America/Port_of_Spain. - -# Bahamas -# -# For 1899 Milne gives -5:09:29.5; round that. -# -# From Sue Williams (2006-12-07): -# The Bahamas announced about a month ago that they plan to change their DST -# rules to sync with the U.S. starting in 2007.... -# http://www.jonesbahamas.com/?c=45&a=10412 - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Bahamas 1964 1975 - Oct lastSun 2:00 0 S -Rule Bahamas 1964 1975 - Apr lastSun 2:00 1:00 D -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Nassau -5:09:30 - LMT 1912 Mar 2 - -5:00 Bahamas E%sT 1976 - -5:00 US E%sT - -# Barbados - -# For 1899 Milne gives -3:58:29.2; round that. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Barb 1977 only - Jun 12 2:00 1:00 D -Rule Barb 1977 1978 - Oct Sun>=1 2:00 0 S -Rule Barb 1978 1980 - Apr Sun>=15 2:00 1:00 D -Rule Barb 1979 only - Sep 30 2:00 0 S -Rule Barb 1980 only - Sep 25 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Barbados -3:58:29 - LMT 1924 # Bridgetown - -3:58:29 - BMT 1932 # Bridgetown Mean Time - -4:00 Barb A%sT - -# Belize -# Whitman entirely disagrees with Shanks; go with Shanks & Pottenger. -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Belize 1918 1942 - Oct Sun>=2 0:00 0:30 -0530 -Rule Belize 1919 1943 - Feb Sun>=9 0:00 0 CST -Rule Belize 1973 only - Dec 5 0:00 1:00 CDT -Rule Belize 1974 only - Feb 9 0:00 0 CST -Rule Belize 1982 only - Dec 18 0:00 1:00 CDT -Rule Belize 1983 only - Feb 12 0:00 0 CST -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Belize -5:52:48 - LMT 1912 Apr - -6:00 Belize %s - -# Bermuda - -# For 1899 Milne gives -4:19:18.3 as the meridian of the clock tower, -# Bermuda dockyard, Ireland I; round that. - -# From Dan Jones, reporting in The Royal Gazette (2006-06-26): - -# Next year, however, clocks in the US will go forward on the second Sunday -# in March, until the first Sunday in November. And, after the Time Zone -# (Seasonal Variation) Bill 2006 was passed in the House of Assembly on -# Friday, the same thing will happen in Bermuda. -# http://www.theroyalgazette.com/apps/pbcs.dll/article?AID=/20060529/NEWS/105290135 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Atlantic/Bermuda -4:19:18 - LMT 1930 Jan 1 2:00 # Hamilton - -4:00 - AST 1974 Apr 28 2:00 - -4:00 Canada A%sT 1976 - -4:00 US A%sT - -# Cayman Is -# See America/Panama. - -# Costa Rica - -# Milne gives -5:36:13.3 as San José mean time; round to nearest. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule CR 1979 1980 - Feb lastSun 0:00 1:00 D -Rule CR 1979 1980 - Jun Sun>=1 0:00 0 S -Rule CR 1991 1992 - Jan Sat>=15 0:00 1:00 D -# IATA SSIM (1991-09) says the following was at 1:00; -# go with Shanks & Pottenger. -Rule CR 1991 only - Jul 1 0:00 0 S -Rule CR 1992 only - Mar 15 0:00 0 S -# There are too many San Josés elsewhere, so we'll use 'Costa Rica'. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Costa_Rica -5:36:13 - LMT 1890 # San José - -5:36:13 - SJMT 1921 Jan 15 # San José Mean Time - -6:00 CR C%sT -# Coco -# no information; probably like America/Costa_Rica - -# Cuba - -# From Paul Eggert (2013-02-21): -# Milne gives -5:28:50.45 for the observatory at Havana, -5:29:23.57 -# for the port, and -5:30 for meteorological observations. -# For now, stick with Shanks & Pottenger. - -# From Arthur David Olson (1999-03-29): -# The 1999-03-28 exhibition baseball game held in Havana, Cuba, between -# the Cuban National Team and the Baltimore Orioles was carried live on -# the Orioles Radio Network, including affiliate WTOP in Washington, DC. -# During the game, play-by-play announcer Jim Hunter noted that -# "We'll be losing two hours of sleep...Cuba switched to Daylight Saving -# Time today." (The "two hour" remark referred to losing one hour of -# sleep on 1999-03-28 - when the announcers were in Cuba as it switched -# to DST - and one more hour on 1999-04-04 - when the announcers will have -# returned to Baltimore, which switches on that date.) - -# From Steffen Thorsen (2013-11-11): -# DST start in Cuba in 2004 ... does not follow the same rules as the -# years before. The correct date should be Sunday 2004-03-28 00:00 ... -# https://web.archive.org/web/20040402060750/http://www.granma.cu/espanol/2004/marzo/sab27/reloj.html - -# From Evert van der Veer via Steffen Thorsen (2004-10-28): -# Cuba is not going back to standard time this year. -# From Paul Eggert (2006-03-22): -# http://www.granma.cu/ingles/2004/septiembre/juev30/41medid-i.html -# says that it's due to a problem at the Antonio Guiteras -# thermoelectric plant, and says "This October there will be no return -# to normal hours (after daylight saving time)". -# For now, let's assume that it's a temporary measure. - -# From Carlos A. Carnero Delgado (2005-11-12): -# This year (just like in 2004-2005) there's no change in time zone -# adjustment in Cuba. We will stay in daylight saving time: -# http://www.granma.cu/espanol/2005/noviembre/mier9/horario.html - -# From Jesper Nørgaard Welen (2006-10-21): -# An article in GRANMA INTERNACIONAL claims that Cuba will end -# the 3 years of permanent DST next weekend, see -# http://www.granma.cu/ingles/2006/octubre/lun16/43horario.html -# "On Saturday night, October 28 going into Sunday, October 29, at 01:00, -# watches should be set back one hour - going back to 00:00 hours - returning -# to the normal schedule.... - -# From Paul Eggert (2007-03-02): -# , dated yesterday, -# says Cuban clocks will advance at midnight on March 10. -# For lack of better information, assume Cuba will use US rules, -# except that it switches at midnight standard time as usual. -# -# From Steffen Thorsen (2007-10-25): -# Carlos Alberto Fonseca Arauz informed me that Cuba will end DST one week -# earlier - on the last Sunday of October, just like in 2006. -# -# He supplied these references: -# -# http://www.prensalatina.com.mx/article.asp?ID=%7B4CC32C1B-A9F7-42FB-8A07-8631AFC923AF%7D&language=ES -# http://actualidad.terra.es/sociedad/articulo/cuba_llama_ahorrar_energia_cambio_1957044.htm -# -# From Alex Krivenyshev (2007-10-25): -# Here is also article from Granma (Cuba): -# -# Regirá el Horario Normal desde el próximo domingo 28 de octubre -# http://www.granma.cubaweb.cu/2007/10/24/nacional/artic07.html -# -# http://www.worldtimezone.com/dst_news/dst_news_cuba03.html - -# From Arthur David Olson (2008-03-09): -# I'm in Maryland which is now observing United States Eastern Daylight -# Time. At 9:44 local time I used RealPlayer to listen to -# http://media.enet.cu/radioreloj -# a Cuban information station, and heard -# the time announced as "ocho cuarenta y cuatro" ("eight forty-four"), -# indicating that Cuba is still on standard time. - -# From Steffen Thorsen (2008-03-12): -# It seems that Cuba will start DST on Sunday, 2007-03-16... -# It was announced yesterday, according to this source (in Spanish): -# http://www.nnc.cubaweb.cu/marzo-2008/cien-1-11-3-08.htm -# -# Some more background information is posted here: -# https://www.timeanddate.com/news/time/cuba-starts-dst-march-16.html -# -# The article also says that Cuba has been observing DST since 1963, -# while Shanks (and tzdata) has 1965 as the first date (except in the -# 1940's). Many other web pages in Cuba also claim that it has been -# observed since 1963, but with the exception of 1970 - an exception -# which is not present in tzdata/Shanks. So there is a chance we need to -# change some historic records as well. -# -# One example: -# http://www.radiohc.cu/espanol/noticias/mar07/11mar/hor.htm - -# From Jesper Nørgaard Welen (2008-03-13): -# The Cuban time change has just been confirmed on the most authoritative -# web site, the Granma. Please check out -# http://www.granma.cubaweb.cu/2008/03/13/nacional/artic10.html -# -# Basically as expected after Steffen Thorsen's information, the change -# will take place midnight between Saturday and Sunday. - -# From Arthur David Olson (2008-03-12): -# Assume Sun>=15 (third Sunday) going forward. - -# From Alexander Krivenyshev (2009-03-04) -# According to the Radio Reloj - Cuba will start Daylight Saving Time on -# midnight between Saturday, March 07, 2009 and Sunday, March 08, 2009- -# not on midnight March 14 / March 15 as previously thought. -# -# http://www.worldtimezone.com/dst_news/dst_news_cuba05.html -# (in Spanish) - -# From Arthur David Olson (2009-03-09) -# I listened over the Internet to -# http://media.enet.cu/readioreloj -# this morning; when it was 10:05 a. m. here in Bethesda, Maryland the -# the time was announced as "diez cinco" - the same time as here, indicating -# that has indeed switched to DST. Assume second Sunday from 2009 forward. - -# From Steffen Thorsen (2011-03-08): -# Granma announced that Cuba is going to start DST on 2011-03-20 00:00:00 -# this year. Nothing about the end date known so far (if that has -# changed at all). -# -# Source: -# http://granma.co.cu/2011/03/08/nacional/artic01.html -# -# Our info: -# https://www.timeanddate.com/news/time/cuba-starts-dst-2011.html -# -# From Steffen Thorsen (2011-10-30) -# Cuba will end DST two weeks later this year. Instead of going back -# tonight, it has been delayed to 2011-11-13 at 01:00. -# -# One source (Spanish) -# http://www.radioangulo.cu/noticias/cuba/17105-cuba-restablecera-el-horario-del-meridiano-de-greenwich.html -# -# Our page: -# https://www.timeanddate.com/news/time/cuba-time-changes-2011.html -# -# From Steffen Thorsen (2012-03-01) -# According to Radio Reloj, Cuba will start DST on Midnight between March -# 31 and April 1. -# -# Radio Reloj has the following info (Spanish): -# http://www.radioreloj.cu/index.php/noticias-radio-reloj/71-miscelaneas/7529-cuba-aplicara-el-horario-de-verano-desde-el-1-de-abril -# -# Our info on it: -# https://www.timeanddate.com/news/time/cuba-starts-dst-2012.html - -# From Steffen Thorsen (2012-11-03): -# Radio Reloj and many other sources report that Cuba is changing back -# to standard time on 2012-11-04: -# http://www.radioreloj.cu/index.php/noticias-radio-reloj/36-nacionales/9961-regira-horario-normal-en-cuba-desde-el-domingo-cuatro-de-noviembre -# From Paul Eggert (2012-11-03): -# For now, assume the future rule is first Sunday in November. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Cuba 1928 only - Jun 10 0:00 1:00 D -Rule Cuba 1928 only - Oct 10 0:00 0 S -Rule Cuba 1940 1942 - Jun Sun>=1 0:00 1:00 D -Rule Cuba 1940 1942 - Sep Sun>=1 0:00 0 S -Rule Cuba 1945 1946 - Jun Sun>=1 0:00 1:00 D -Rule Cuba 1945 1946 - Sep Sun>=1 0:00 0 S -Rule Cuba 1965 only - Jun 1 0:00 1:00 D -Rule Cuba 1965 only - Sep 30 0:00 0 S -Rule Cuba 1966 only - May 29 0:00 1:00 D -Rule Cuba 1966 only - Oct 2 0:00 0 S -Rule Cuba 1967 only - Apr 8 0:00 1:00 D -Rule Cuba 1967 1968 - Sep Sun>=8 0:00 0 S -Rule Cuba 1968 only - Apr 14 0:00 1:00 D -Rule Cuba 1969 1977 - Apr lastSun 0:00 1:00 D -Rule Cuba 1969 1971 - Oct lastSun 0:00 0 S -Rule Cuba 1972 1974 - Oct 8 0:00 0 S -Rule Cuba 1975 1977 - Oct lastSun 0:00 0 S -Rule Cuba 1978 only - May 7 0:00 1:00 D -Rule Cuba 1978 1990 - Oct Sun>=8 0:00 0 S -Rule Cuba 1979 1980 - Mar Sun>=15 0:00 1:00 D -Rule Cuba 1981 1985 - May Sun>=5 0:00 1:00 D -Rule Cuba 1986 1989 - Mar Sun>=14 0:00 1:00 D -Rule Cuba 1990 1997 - Apr Sun>=1 0:00 1:00 D -Rule Cuba 1991 1995 - Oct Sun>=8 0:00s 0 S -Rule Cuba 1996 only - Oct 6 0:00s 0 S -Rule Cuba 1997 only - Oct 12 0:00s 0 S -Rule Cuba 1998 1999 - Mar lastSun 0:00s 1:00 D -Rule Cuba 1998 2003 - Oct lastSun 0:00s 0 S -Rule Cuba 2000 2003 - Apr Sun>=1 0:00s 1:00 D -Rule Cuba 2004 only - Mar lastSun 0:00s 1:00 D -Rule Cuba 2006 2010 - Oct lastSun 0:00s 0 S -Rule Cuba 2007 only - Mar Sun>=8 0:00s 1:00 D -Rule Cuba 2008 only - Mar Sun>=15 0:00s 1:00 D -Rule Cuba 2009 2010 - Mar Sun>=8 0:00s 1:00 D -Rule Cuba 2011 only - Mar Sun>=15 0:00s 1:00 D -Rule Cuba 2011 only - Nov 13 0:00s 0 S -Rule Cuba 2012 only - Apr 1 0:00s 1:00 D -Rule Cuba 2012 max - Nov Sun>=1 0:00s 0 S -Rule Cuba 2013 max - Mar Sun>=8 0:00s 1:00 D - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Havana -5:29:28 - LMT 1890 - -5:29:36 - HMT 1925 Jul 19 12:00 # Havana MT - -5:00 Cuba C%sT - -# Dominica -# See America/Port_of_Spain. - -# Dominican Republic - -# From Steffen Thorsen (2000-10-30): -# Enrique Morales reported to me that the Dominican Republic has changed the -# time zone to Eastern Standard Time as of Sunday 29 at 2 am.... -# http://www.listin.com.do/antes/261000/republica/princi.html - -# From Paul Eggert (2000-12-04): -# That URL (2000-10-26, in Spanish) says they planned to use US-style DST. - -# From Rives McDow (2000-12-01): -# Dominican Republic changed its mind and presidential decree on Tuesday, -# November 28, 2000, with a new decree. On Sunday, December 3 at 1:00 AM the -# Dominican Republic will be reverting to 8 hours from the International Date -# Line, and will not be using DST in the foreseeable future. The reason they -# decided to use DST was to be in synch with Puerto Rico, who was also going -# to implement DST. When Puerto Rico didn't implement DST, the president -# decided to revert. - - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule DR 1966 only - Oct 30 0:00 1:00 EDT -Rule DR 1967 only - Feb 28 0:00 0 EST -Rule DR 1969 1973 - Oct lastSun 0:00 0:30 -0430 -Rule DR 1970 only - Feb 21 0:00 0 EST -Rule DR 1971 only - Jan 20 0:00 0 EST -Rule DR 1972 1974 - Jan 21 0:00 0 EST -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Santo_Domingo -4:39:36 - LMT 1890 - -4:40 - SDMT 1933 Apr 1 12:00 # S. Dom. MT - -5:00 DR %s 1974 Oct 27 - -4:00 - AST 2000 Oct 29 2:00 - -5:00 US E%sT 2000 Dec 3 1:00 - -4:00 - AST - -# El Salvador - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Salv 1987 1988 - May Sun>=1 0:00 1:00 D -Rule Salv 1987 1988 - Sep lastSun 0:00 0 S -# There are too many San Salvadors elsewhere, so use America/El_Salvador -# instead of America/San_Salvador. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/El_Salvador -5:56:48 - LMT 1921 # San Salvador - -6:00 Salv C%sT - -# Grenada -# Guadeloupe -# St Barthélemy -# St Martin (French part) -# See America/Port_of_Spain. - -# Guatemala -# -# From Gwillim Law (2006-04-22), after a heads-up from Oscar van Vlijmen: -# Diario Co Latino, at -# , -# says in an article dated 2006-04-19 that the Guatemalan government had -# decided on that date to advance official time by 60 minutes, to lessen the -# impact of the elevated cost of oil.... Daylight saving time will last from -# 2006-04-29 24:00 (Guatemalan standard time) to 2006-09-30 (time unspecified). -# From Paul Eggert (2006-06-22): -# The Ministry of Energy and Mines, press release CP-15/2006 -# (2006-04-19), says DST ends at 24:00. See -# http://www.sieca.org.gt/Sitio_publico/Energeticos/Doc/Medidas/Cambio_Horario_Nac_190406.pdf - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Guat 1973 only - Nov 25 0:00 1:00 D -Rule Guat 1974 only - Feb 24 0:00 0 S -Rule Guat 1983 only - May 21 0:00 1:00 D -Rule Guat 1983 only - Sep 22 0:00 0 S -Rule Guat 1991 only - Mar 23 0:00 1:00 D -Rule Guat 1991 only - Sep 7 0:00 0 S -Rule Guat 2006 only - Apr 30 0:00 1:00 D -Rule Guat 2006 only - Oct 1 0:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Guatemala -6:02:04 - LMT 1918 Oct 5 - -6:00 Guat C%sT - -# Haiti -# From Gwillim Law (2005-04-15): -# Risto O. Nykänen wrote me that Haiti is now on DST. -# I searched for confirmation, and I found a press release -# on the Web page of the Haitian Consulate in Chicago (2005-03-31), -# . Translated from French, it says: -# -# "The Prime Minister's Communication Office notifies the public in general -# and the press in particular that, following a decision of the Interior -# Ministry and the Territorial Collectivities [I suppose that means the -# provinces], Haiti will move to Eastern Daylight Time in the night from next -# Saturday the 2nd to Sunday the 3rd. -# -# "Consequently, the Prime Minister's Communication Office wishes to inform -# the population that the country's clocks will be set forward one hour -# starting at midnight. This provision will hold until the last Saturday in -# October 2005. -# -# "Port-au-Prince, March 31, 2005" -# -# From Steffen Thorsen (2006-04-04): -# I have been informed by users that Haiti observes DST this year like -# last year, so the current "only" rule for 2005 might be changed to a -# "max" rule or to last until 2006. (Who knows if they will observe DST -# next year or if they will extend their DST like US/Canada next year). -# -# I have found this article about it (in French): -# http://www.haitipressnetwork.com/news.cfm?articleID=7612 -# -# The reason seems to be an energy crisis. - -# From Stephen Colebourne (2007-02-22): -# Some IATA info: Haiti won't be having DST in 2007. - -# From Steffen Thorsen (2012-03-11): -# According to several news sources, Haiti will observe DST this year, -# apparently using the same start and end date as USA/Canada. -# So this means they have already changed their time. -# -# http://www.alterpresse.org/spip.php?article12510 -# http://radiovision2000haiti.net/home/?p=13253 -# -# From Arthur David Olson (2012-03-11): -# The alterpresse.org source seems to show a US-style leap from 2:00 a.m. to -# 3:00 a.m. rather than the traditional Haitian jump at midnight. -# Assume a US-style fall back as well. - -# From Steffen Thorsen (2013-03-10): -# It appears that Haiti is observing DST this year as well, same rules -# as US/Canada. They did it last year as well, and it looks like they -# are going to observe DST every year now... -# -# http://radiovision2000haiti.net/public/haiti-avis-changement-dheure-dimanche/ -# http://www.canalplushaiti.net/?p=6714 - -# From Steffen Thorsen (2016-03-12): -# Jean Antoine, editor of www.haiti-reference.com informed us that Haiti -# are not going on DST this year. Several other resources confirm this: ... -# https://www.radiotelevisioncaraibes.com/presse/heure_d_t_pas_de_changement_d_heure_pr_vu_pour_cet_ann_e.html -# https://www.vantbefinfo.com/changement-dheure-pas-pour-haiti/ -# http://news.anmwe.com/haiti-lheure-nationale-ne-sera-ni-avancee-ni-reculee-cette-annee/ - -# From Steffen Thorsen (2017-03-12): -# We have received 4 mails from different people telling that Haiti -# has started DST again today, and this source seems to confirm that, -# I have not been able to find a more authoritative source: -# https://www.haitilibre.com/en/news-20319-haiti-notices-time-change-in-haiti.html - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Haiti 1983 only - May 8 0:00 1:00 D -Rule Haiti 1984 1987 - Apr lastSun 0:00 1:00 D -Rule Haiti 1983 1987 - Oct lastSun 0:00 0 S -# Shanks & Pottenger say AT is 2:00, but IATA SSIM (1991/1997) says 1:00s. -# Go with IATA. -Rule Haiti 1988 1997 - Apr Sun>=1 1:00s 1:00 D -Rule Haiti 1988 1997 - Oct lastSun 1:00s 0 S -Rule Haiti 2005 2006 - Apr Sun>=1 0:00 1:00 D -Rule Haiti 2005 2006 - Oct lastSun 0:00 0 S -Rule Haiti 2012 2015 - Mar Sun>=8 2:00 1:00 D -Rule Haiti 2012 2015 - Nov Sun>=1 2:00 0 S -Rule Haiti 2017 max - Mar Sun>=8 2:00 1:00 D -Rule Haiti 2017 max - Nov Sun>=1 2:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Port-au-Prince -4:49:20 - LMT 1890 - -4:49 - PPMT 1917 Jan 24 12:00 # P-a-P MT - -5:00 Haiti E%sT - -# Honduras -# Shanks & Pottenger say 1921 Jan 1; go with Whitman's more precise Apr 1. - -# From Paul Eggert (2006-05-05): -# worldtimezone.com reports a 2006-05-02 Spanish-language AP article -# saying Honduras will start using DST midnight Saturday, effective 4 -# months until September. La Tribuna reported today -# that Manuel Zelaya, the president -# of Honduras, refused to back down on this. - -# From Jesper Nørgaard Welen (2006-08-08): -# It seems that Honduras has returned from DST to standard time this Monday at -# 00:00 hours (prolonging Sunday to 25 hours duration). -# http://www.worldtimezone.com/dst_news/dst_news_honduras04.html - -# From Paul Eggert (2006-08-08): -# Also see Diario El Heraldo, The country returns to standard time (2006-08-08). -# http://www.elheraldo.hn/nota.php?nid=54941&sec=12 -# It mentions executive decree 18-2006. - -# From Steffen Thorsen (2006-08-17): -# Honduras will observe DST from 2007 to 2009, exact dates are not -# published, I have located this authoritative source: -# http://www.presidencia.gob.hn/noticia.aspx?nId=47 - -# From Steffen Thorsen (2007-03-30): -# http://www.laprensahn.com/pais_nota.php?id04962=7386 -# So it seems that Honduras will not enter DST this year.... - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Hond 1987 1988 - May Sun>=1 0:00 1:00 D -Rule Hond 1987 1988 - Sep lastSun 0:00 0 S -Rule Hond 2006 only - May Sun>=1 0:00 1:00 D -Rule Hond 2006 only - Aug Mon>=1 0:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Tegucigalpa -5:48:52 - LMT 1921 Apr - -6:00 Hond C%sT -# -# Great Swan I ceded by US to Honduras in 1972 - -# Jamaica -# Shanks & Pottenger give -5:07:12, but Milne records -5:07:10.41 from an -# unspecified official document, and says "This time is used throughout the -# island". Go with Milne. Round to the nearest second as required by zic. -# -# Shanks & Pottenger give April 28 for the 1974 spring-forward transition, but -# Lance Neita writes that Prime Minister Michael Manley decreed it January 5. -# Assume Neita meant Jan 6 02:00, the same as the US. Neita also writes that -# Manley's supporters associated this act with Manley's nickname "Joshua" -# (recall that in the Bible the sun stood still at Joshua's request), -# and with the Rod of Correction which Manley said he had received from -# Haile Selassie, Emperor of Ethiopia. See: -# Neita L. The politician in all of us. Jamaica Observer 2014-09-20 -# http://www.jamaicaobserver.com/columns/The-politician-in-all-of-us_17573647 -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Jamaica -5:07:10 - LMT 1890 # Kingston - -5:07:10 - KMT 1912 Feb # Kingston Mean Time - -5:00 - EST 1974 - -5:00 US E%sT 1984 - -5:00 - EST - -# Martinique -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Martinique -4:04:20 - LMT 1890 # Fort-de-France - -4:04:20 - FFMT 1911 May # Fort-de-France MT - -4:00 - AST 1980 Apr 6 - -4:00 1:00 ADT 1980 Sep 28 - -4:00 - AST - -# Montserrat -# See America/Port_of_Spain. - -# Nicaragua -# -# This uses Shanks & Pottenger for times before 2005. -# -# From Steffen Thorsen (2005-04-12): -# I've got reports from 8 different people that Nicaragua just started -# DST on Sunday 2005-04-10, in order to save energy because of -# expensive petroleum. The exact end date for DST is not yet -# announced, only "September" but some sites also say "mid-September". -# Some background information is available on the President's official site: -# http://www.presidencia.gob.ni/Presidencia/Files_index/Secretaria/Notas%20de%20Prensa/Presidente/2005/ABRIL/Gobierno-de-nicaragua-adelanta-hora-oficial-06abril.htm -# The Decree, no 23-2005 is available here: -# http://www.presidencia.gob.ni/buscador_gaceta/BD/DECRETOS/2005/Decreto%2023-2005%20Se%20adelanta%20en%20una%20hora%20en%20todo%20el%20territorio%20nacional%20apartir%20de%20las%2024horas%20del%2009%20de%20Abril.pdf -# -# From Paul Eggert (2005-05-01): -# The decree doesn't say anything about daylight saving, but for now let's -# assume that it is daylight saving.... -# -# From Gwillim Law (2005-04-21): -# The Associated Press story on the time change, which can be found at -# http://www.lapalmainteractivo.com/guias/content/gen/ap/America_Latina/AMC_GEN_NICARAGUA_HORA.html -# and elsewhere, says (fifth paragraph, translated from Spanish): "The last -# time that a change of clocks was applied to save energy was in the year 2000 -# during the Arnoldo Alemán administration."... -# The northamerica file says that Nicaragua has been on UTC-6 continuously -# since December 1998. I wasn't able to find any details of Nicaraguan time -# changes in 2000. Perhaps a note could be added to the northamerica file, to -# the effect that we have indirect evidence that DST was observed in 2000. -# -# From Jesper Nørgaard Welen (2005-11-02): -# Nicaragua left DST the 2005-10-02 at 00:00 (local time). -# http://www.presidencia.gob.ni/presidencia/files_index/secretaria/comunicados/2005/septiembre/26septiembre-cambio-hora.htm -# (2005-09-26) -# -# From Jesper Nørgaard Welen (2006-05-05): -# http://www.elnuevodiario.com.ni/2006/05/01/nacionales/18410 -# (my informal translation) -# By order of the president of the republic, Enrique Bolaños, Nicaragua -# advanced by sixty minutes their official time, yesterday at 2 in the -# morning, and will stay that way until 30th of September. -# -# From Jesper Nørgaard Welen (2006-09-30): -# http://www.presidencia.gob.ni/buscador_gaceta/BD/DECRETOS/2006/D-063-2006P-PRN-Cambio-Hora.pdf -# My informal translation runs: -# The natural sun time is restored in all the national territory, in that the -# time is returned one hour at 01:00 am of October 1 of 2006. -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Nic 1979 1980 - Mar Sun>=16 0:00 1:00 D -Rule Nic 1979 1980 - Jun Mon>=23 0:00 0 S -Rule Nic 2005 only - Apr 10 0:00 1:00 D -Rule Nic 2005 only - Oct Sun>=1 0:00 0 S -Rule Nic 2006 only - Apr 30 2:00 1:00 D -Rule Nic 2006 only - Oct Sun>=1 1:00 0 S -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Managua -5:45:08 - LMT 1890 - -5:45:12 - MMT 1934 Jun 23 # Managua Mean Time? - -6:00 - CST 1973 May - -5:00 - EST 1975 Feb 16 - -6:00 Nic C%sT 1992 Jan 1 4:00 - -5:00 - EST 1992 Sep 24 - -6:00 - CST 1993 - -5:00 - EST 1997 - -6:00 Nic C%sT - -# Panama -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Panama -5:18:08 - LMT 1890 - -5:19:36 - CMT 1908 Apr 22 # Colón Mean Time - -5:00 - EST -Link America/Panama America/Cayman - -# Puerto Rico -# There are too many San Juans elsewhere, so we'll use 'Puerto_Rico'. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Puerto_Rico -4:24:25 - LMT 1899 Mar 28 12:00 # San Juan - -4:00 - AST 1942 May 3 - -4:00 US A%sT 1946 - -4:00 - AST - -# St Kitts-Nevis -# St Lucia -# See America/Port_of_Spain. - -# St Pierre and Miquelon -# There are too many St Pierres elsewhere, so we'll use 'Miquelon'. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Miquelon -3:44:40 - LMT 1911 May 15 # St Pierre - -4:00 - AST 1980 May - -3:00 - -03 1987 - -3:00 Canada -03/-02 - -# St Vincent and the Grenadines -# See America/Port_of_Spain. - -# Turks and Caicos -# -# From Chris Dunn in -# https://bugs.debian.org/415007 -# (2007-03-15): In the Turks & Caicos Islands (America/Grand_Turk) the -# daylight saving dates for time changes have been adjusted to match -# the recent U.S. change of dates. -# -# From Brian Inglis (2007-04-28): -# http://www.turksandcaicos.tc/calendar/index.htm [2007-04-26] -# there is an entry for Nov 4 "Daylight Savings Time Ends 2007" and three -# rows before that there is an out of date entry for Oct: -# "Eastern Standard Times Begins 2007 -# Clocks are set back one hour at 2:00 a.m. local Daylight Saving Time" -# indicating that the normal ET rules are followed. -# -# From Paul Eggert (2014-08-19): -# The 2014-08-13 Cabinet meeting decided to stay on UT -04 year-round. See: -# http://tcweeklynews.com/daylight-savings-time-to-be-maintained-p5353-127.htm -# Model this as a switch from EST/EDT to AST ... -# From Chris Walton (2014-11-04): -# ... the TCI government appears to have delayed the switch to -# "permanent daylight saving time" by one year.... -# http://tcweeklynews.com/time-change-to-go-ahead-this-november-p5437-127.htm -# -# From the Turks & Caicos Cabinet (2017-07-20), heads-up from Steffen Thorsen: -# ... agreed to the reintroduction in TCI of Daylight Saving Time (DST) -# during the summer months and Standard Time, also known as Local -# Time, during the winter months with effect from April 2018 ... -# https://www.gov.uk/government/news/turks-and-caicos-post-cabinet-meeting-statement--3 -# -# From Paul Eggert (2017-08-26): -# The date of effect of the spring 2018 change appears to be March 11, -# which makes more sense. See: Hamilton D. Time change back -# by March 2018 for TCI. Magnetic Media. 2017-08-25. -# http://magneticmediatv.com/2017/08/time-change-back-by-march-2018-for-tci/ -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Grand_Turk -4:44:32 - LMT 1890 - -5:07:10 - KMT 1912 Feb # Kingston Mean Time - -5:00 - EST 1979 - -5:00 US E%sT 2015 Nov Sun>=1 2:00 - -4:00 - AST 2018 Mar 11 3:00 - -5:00 US E%sT - -# British Virgin Is -# Virgin Is -# See America/Port_of_Spain. - - -# Local Variables: -# coding: utf-8 -# End: diff --git a/date_time/pacificnew b/date_time/pacificnew deleted file mode 100644 index 734943486b..0000000000 --- a/date_time/pacificnew +++ /dev/null @@ -1,27 +0,0 @@ -# This file is in the public domain, so clarified as of -# 2009-05-17 by Arthur David Olson. - -# From Arthur David Olson (1989-04-05): -# On 1989-04-05, the U. S. House of Representatives passed (238-154) a bill -# establishing "Pacific Presidential Election Time"; it was not acted on -# by the Senate or signed into law by the President. -# You might want to change the "PE" (Presidential Election) below to -# "Q" (Quadrennial) to maintain three-character zone abbreviations. -# If you're really conservative, you might want to change it to "D". -# Avoid "L" (Leap Year), which won't be true in 2100. - -# If Presidential Election Time is ever established, replace "XXXX" below -# with the year the law takes effect and uncomment the "##" lines. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -## Rule Twilite XXXX max - Apr Sun>=1 2:00 1:00 D -## Rule Twilite XXXX max uspres Oct lastSun 2:00 1:00 PE -## Rule Twilite XXXX max uspres Nov Sun>=7 2:00 0 S -## Rule Twilite XXXX max nonpres Oct lastSun 2:00 0 S - -# Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL] -## Zone America/Los_Angeles-PET -8:00 US P%sT XXXX -## -8:00 Twilite P%sT - -# For now... -Link America/Los_Angeles US/Pacific-New ## diff --git a/date_time/southamerica b/date_time/southamerica deleted file mode 100644 index 9784044ec8..0000000000 --- a/date_time/southamerica +++ /dev/null @@ -1,1936 +0,0 @@ -# This file is in the public domain, so clarified as of -# 2009-05-17 by Arthur David Olson. - -# This file is by no means authoritative; if you think you know better, -# go ahead and edit the file (and please send any changes to -# tz@iana.org for general use in the future). For more, please see -# the file CONTRIBUTING in the tz distribution. - -# From Paul Eggert (2016-12-05): -# -# Unless otherwise specified, the source for data through 1990 is: -# Thomas G. Shanks and Rique Pottenger, The International Atlas (6th edition), -# San Diego: ACS Publications, Inc. (2003). -# Unfortunately this book contains many errors and cites no sources. -# -# Many years ago Gwillim Law wrote that a good source -# for time zone data was the International Air Transport -# Association's Standard Schedules Information Manual (IATA SSIM), -# published semiannually. Law sent in several helpful summaries -# of the IATA's data after 1990. Except where otherwise noted, -# IATA SSIM is the source for entries after 1990. -# -# For data circa 1899, a common source is: -# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94. -# https://www.jstor.org/stable/1774359 -# -# These tables use numeric abbreviations like -03 and -0330 for -# integer hour and minute UT offsets. Although earlier editions used -# alphabetic time zone abbreviations, these abbreviations were -# invented and did not reflect common practice. - -############################################################################### - -############################################################################### - -# Argentina - -# From Bob Devine (1988-01-28): -# Argentina: first Sunday in October to first Sunday in April since 1976. -# Double Summer time from 1969 to 1974. Switches at midnight. - -# From U. S. Naval Observatory (1988-01-19): -# ARGENTINA 3 H BEHIND UTC - -# From Hernan G. Otero (1995-06-26): -# I am sending modifications to the Argentine time zone table... -# AR was chosen because they are the ISO letters that represent Argentina. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Arg 1930 only - Dec 1 0:00 1:00 - -Rule Arg 1931 only - Apr 1 0:00 0 - -Rule Arg 1931 only - Oct 15 0:00 1:00 - -Rule Arg 1932 1940 - Mar 1 0:00 0 - -Rule Arg 1932 1939 - Nov 1 0:00 1:00 - -Rule Arg 1940 only - Jul 1 0:00 1:00 - -Rule Arg 1941 only - Jun 15 0:00 0 - -Rule Arg 1941 only - Oct 15 0:00 1:00 - -Rule Arg 1943 only - Aug 1 0:00 0 - -Rule Arg 1943 only - Oct 15 0:00 1:00 - -Rule Arg 1946 only - Mar 1 0:00 0 - -Rule Arg 1946 only - Oct 1 0:00 1:00 - -Rule Arg 1963 only - Oct 1 0:00 0 - -Rule Arg 1963 only - Dec 15 0:00 1:00 - -Rule Arg 1964 1966 - Mar 1 0:00 0 - -Rule Arg 1964 1966 - Oct 15 0:00 1:00 - -Rule Arg 1967 only - Apr 2 0:00 0 - -Rule Arg 1967 1968 - Oct Sun>=1 0:00 1:00 - -Rule Arg 1968 1969 - Apr Sun>=1 0:00 0 - -Rule Arg 1974 only - Jan 23 0:00 1:00 - -Rule Arg 1974 only - May 1 0:00 0 - -Rule Arg 1988 only - Dec 1 0:00 1:00 - -# -# From Hernan G. Otero (1995-06-26): -# These corrections were contributed by InterSoft Argentina S.A., -# obtaining the data from the: -# Talleres de Hidrografía Naval Argentina -# (Argentine Naval Hydrography Institute) -Rule Arg 1989 1993 - Mar Sun>=1 0:00 0 - -Rule Arg 1989 1992 - Oct Sun>=15 0:00 1:00 - -# -# From Hernan G. Otero (1995-06-26): -# From this moment on, the law that mandated the daylight saving -# time corrections was derogated and no more modifications -# to the time zones (for daylight saving) are now made. -# -# From Rives McDow (2000-01-10): -# On October 3, 1999, 0:00 local, Argentina implemented daylight savings time, -# which did not result in the switch of a time zone, as they stayed 9 hours -# from the International Date Line. -Rule Arg 1999 only - Oct Sun>=1 0:00 1:00 - -# From Paul Eggert (2007-12-28): -# DST was set to expire on March 5, not March 3, but since it was converted -# to standard time on March 3 it's more convenient for us to pretend that -# it ended on March 3. -Rule Arg 2000 only - Mar 3 0:00 0 - -# -# From Peter Gradelski via Steffen Thorsen (2000-03-01): -# We just checked with our São Paulo office and they say the government of -# Argentina decided not to become one of the countries that go on or off DST. -# So Buenos Aires should be -3 hours from GMT at all times. -# -# From Fabián L. Arce Jofré (2000-04-04): -# The law that claimed DST for Argentina was derogated by President Fernando -# de la Rúa on March 2, 2000, because it would make people spend more energy -# in the winter time, rather than less. The change took effect on March 3. -# -# From Mariano Absatz (2001-06-06): -# one of the major newspapers here in Argentina said that the 1999 -# Timezone Law (which never was effectively applied) will (would?) be -# in effect.... The article is at -# http://ar.clarin.com/diario/2001-06-06/e-01701.htm -# ... The Law itself is "Ley No. 25155", sanctioned on 1999-08-25, enacted -# 1999-09-17, and published 1999-09-21. The official publication is at: -# http://www.boletin.jus.gov.ar/BON/Primera/1999/09-Septiembre/21/PDF/BO21-09-99LEG.PDF -# Regretfully, you have to subscribe (and pay) for the on-line version.... -# -# (2001-06-12): -# the timezone for Argentina will not change next Sunday. -# Apparently it will do so on Sunday 24th.... -# http://ar.clarin.com/diario/2001-06-12/s-03501.htm -# -# (2001-06-25): -# Last Friday (yes, the last working day before the date of the change), the -# Senate annulled the 1999 law that introduced the changes later postponed. -# http://www.clarin.com.ar/diario/2001-06-22/s-03601.htm -# It remains the vote of the Deputies..., but it will be the same.... -# This kind of things had always been done this way in Argentina. -# We are still -03:00 all year round in all of the country. -# -# From Steffen Thorsen (2007-12-21): -# A user (Leonardo Chaim) reported that Argentina will adopt DST.... -# all of the country (all Zone-entries) are affected. News reports like -# http://www.lanacion.com.ar/opinion/nota.asp?nota_id=973037 indicate -# that Argentina will use DST next year as well, from October to -# March, although exact rules are not given. -# -# From Jesper Nørgaard Welen (2007-12-26) -# The last hurdle of Argentina DST is over, the proposal was approved in -# the lower chamber too (Diputados) with a vote 192 for and 2 against. -# By the way thanks to Mariano Absatz and Daniel Mario Vega for the link to -# the original scanned proposal, where the dates and the zero hours are -# clear and unambiguous...This is the article about final approval: -# http://www.lanacion.com.ar/politica/nota.asp?nota_id=973996 -# -# From Paul Eggert (2007-12-22): -# For dates after mid-2008, the following rules are my guesses and -# are quite possibly wrong, but are more likely than no DST at all. - -# From Alexander Krivenyshev (2008-09-05): -# As per message from Carlos Alberto Fonseca Arauz (Nicaragua), -# Argentina will start DST on Sunday October 19, 2008. -# -# http://www.worldtimezone.com/dst_news/dst_news_argentina03.html -# http://www.impulsobaires.com.ar/nota.php?id=57832 (in spanish) - -# From Juan Manuel Docile in https://bugs.gentoo.org/240339 (2008-10-07) -# via Rodrigo Severo: -# Argentinian law No. 25.155 is no longer valid. -# http://www.infoleg.gov.ar/infolegInternet/anexos/60000-64999/60036/norma.htm -# The new one is law No. 26.350 -# http://www.infoleg.gov.ar/infolegInternet/anexos/135000-139999/136191/norma.htm -# So there is no summer time in Argentina for now. - -# From Mariano Absatz (2008-10-20): -# Decree 1693/2008 applies Law 26.350 for the summer 2008/2009 establishing DST -# in Argentina from 2008-10-19 until 2009-03-15. -# http://www.boletinoficial.gov.ar/Bora.Portal/CustomControls/PdfContent.aspx?fp=16102008&pi=3&pf=4&s=0&sec=01 -# - -# Decree 1705/2008 excepting 12 Provinces from applying DST in the summer -# 2008/2009: Catamarca, La Rioja, Mendoza, Salta, San Juan, San Luis, La -# Pampa, Neuquén, Rio Negro, Chubut, Santa Cruz and Tierra del Fuego -# http://www.boletinoficial.gov.ar/Bora.Portal/CustomControls/PdfContent.aspx?fp=17102008&pi=1&pf=1&s=0&sec=01 -# -# Press release 235 dated Saturday October 18th, from the Government of the -# Province of Jujuy saying it will not apply DST either (even when it was not -# included in Decree 1705/2008). -# http://www.jujuy.gov.ar/index2/partes_prensa/18_10_08/235-181008.doc - -# From fullinet (2009-10-18): -# As announced in -# http://www.argentina.gob.ar/argentina/portal/paginas.dhtml?pagina=356 -# (an official .gob.ar) under title: "Sin Cambio de Hora" -# (English: "No hour change"). -# -# "Por el momento, el Gobierno Nacional resolvió no modificar la hora -# oficial, decisión que estaba en estudio para su implementación el -# domingo 18 de octubre. Desde el Ministerio de Planificación se anunció -# que la Argentina hoy, en estas condiciones meteorológicas, no necesita -# la modificación del huso horario, ya que 2009 nos encuentra con -# crecimiento en la producción y distribución energética." - -Rule Arg 2007 only - Dec 30 0:00 1:00 - -Rule Arg 2008 2009 - Mar Sun>=15 0:00 0 - -Rule Arg 2008 only - Oct Sun>=15 0:00 1:00 - - -# From Mariano Absatz (2004-05-21): -# Today it was officially published that the Province of Mendoza is changing -# its timezone this winter... starting tomorrow night.... -# http://www.gobernac.mendoza.gov.ar/boletin/pdf/20040521-27158-normas.pdf -# From Paul Eggert (2004-05-24): -# It's Law No. 7,210. This change is due to a public power emergency, so for -# now we'll assume it's for this year only. -# -# From Paul Eggert (2018-01-31): -# Hora de verano para la República Argentina -# http://buenasiembra.com.ar/esoterismo/astrologia/hora-de-verano-de-la-republica-argentina-27.html -# says that standard time in Argentina from 1894-10-31 -# to 1920-05-01 was -4:16:48.25. Go with this more-precise value -# over Shanks & Pottenger. It is upward compatible with Milne, who -# says Córdoba time was -4:16:48.2. - -# -# From Mariano Absatz (2004-06-05): -# These media articles from a major newspaper mostly cover the current state: -# http://www.lanacion.com.ar/04/05/27/de_604825.asp -# http://www.lanacion.com.ar/04/05/28/de_605203.asp -# -# The following eight (8) provinces pulled clocks back to UTC-04:00 at -# midnight Monday May 31st. (that is, the night between 05/31 and 06/01). -# Apparently, all nine provinces would go back to UTC-03:00 at the same -# time in October 17th. -# -# Catamarca, Chubut, La Rioja, San Juan, San Luis, Santa Cruz, -# Tierra del Fuego, Tucumán. -# -# From Mariano Absatz (2004-06-14): -# ... this weekend, the Province of Tucumán decided it'd go back to UTC-03:00 -# yesterday midnight (that is, at 24:00 Saturday 12th), since the people's -# annoyance with the change is much higher than the power savings obtained.... -# -# From Gwillim Law (2004-06-14): -# http://www.lanacion.com.ar/04/06/10/de_609078.asp ... -# "The time change in Tierra del Fuego was a conflicted decision from -# the start. The government had decreed that the measure would take -# effect on June 1, but a normative error forced the new time to begin -# three days earlier, from a Saturday to a Sunday.... -# Our understanding was that the change was originally scheduled to take place -# on June 1 at 00:00 in Chubut, Santa Cruz, Tierra del Fuego (and some other -# provinces). Sunday was May 30, only two days earlier. So the article -# contains a contradiction. I would give more credence to the Saturday/Sunday -# date than the "three days earlier" phrase, and conclude that Tierra del -# Fuego set its clocks back at 2004-05-30 00:00. -# -# From Steffen Thorsen (2004-10-05): -# The previous law 7210 which changed the province of Mendoza's time zone -# back in May have been modified slightly in a new law 7277, which set the -# new end date to 2004-09-26 (original date was 2004-10-17). -# http://www.gobernac.mendoza.gov.ar/boletin/pdf/20040924-27244-normas.pdf -# -# From Mariano Absatz (2004-10-05): -# San Juan changed from UTC-03:00 to UTC-04:00 at midnight between -# Sunday, May 30th and Monday, May 31st. It changed back to UTC-03:00 -# at midnight between Saturday, July 24th and Sunday, July 25th.... -# http://www.sanjuan.gov.ar/prensa/archivo/000329.html -# http://www.sanjuan.gov.ar/prensa/archivo/000426.html -# http://www.sanjuan.gov.ar/prensa/archivo/000441.html - -# From Alex Krivenyshev (2008-01-17): -# Here are articles that Argentina Province San Luis is planning to end DST -# as earlier as upcoming Monday January 21, 2008 or February 2008: -# -# Provincia argentina retrasa reloj y marca diferencia con resto del país -# (Argentine Province delayed clock and mark difference with the rest of the -# country) -# http://cl.invertia.com/noticias/noticia.aspx?idNoticia=200801171849_EFE_ET4373&idtel -# -# Es inminente que en San Luis atrasen una hora los relojes -# (It is imminent in San Luis clocks one hour delay) -# https://www.lagaceta.com.ar/nota/253414/Economia/Es-inminente-que-en-San-Luis-atrasen-una-hora-los-relojes.html -# http://www.worldtimezone.com/dst_news/dst_news_argentina02.html - -# From Jesper Nørgaard Welen (2008-01-18): -# The page of the San Luis provincial government -# http://www.sanluis.gov.ar/notas.asp?idCanal=0&id=22812 -# confirms what Alex Krivenyshev has earlier sent to the tz -# emailing list about that San Luis plans to return to standard -# time much earlier than the rest of the country. It also -# confirms that upon request the provinces San Juan and Mendoza -# refused to follow San Luis in this change. -# -# The change is supposed to take place Monday the 21st at 0:00 -# hours. As far as I understand it if this goes ahead, we need -# a new timezone for San Luis (although there are also documented -# independent changes in the southamerica file of San Luis in -# 1990 and 1991 which has not been confirmed). - -# From Jesper Nørgaard Welen (2008-01-25): -# Unfortunately the below page has become defunct, about the San Luis -# time change. Perhaps because it now is part of a group of pages "Most -# important pages of 2008." -# -# You can use -# http://www.sanluis.gov.ar/notas.asp?idCanal=8141&id=22834 -# instead it seems. Or use "Buscador" from the main page of the San Luis -# government, and fill in "huso" and click OK, and you will get 3 pages -# from which the first one is identical to the above. - -# From Mariano Absatz (2008-01-28): -# I can confirm that the Province of San Luis (and so far only that -# province) decided to go back to UTC-3 effective midnight Jan 20th 2008 -# (that is, Monday 21st at 0:00 is the time the clocks were delayed back -# 1 hour), and they intend to keep UTC-3 as their timezone all year round -# (that is, unless they change their mind any minute now). -# -# So we'll have to add yet another city to 'southamerica' (I think San -# Luis city is the mos populated city in the Province, so it'd be -# America/Argentina/San_Luis... of course I can't remember if San Luis's -# history of particular changes goes along with Mendoza or San Juan :-( -# (I only remember not being able to collect hard facts about San Luis -# back in 2004, when these provinces changed to UTC-4 for a few days, I -# mailed them personally and never got an answer). - -# From Paul Eggert (2014-08-12): -# Unless otherwise specified, data entries are from Shanks & Pottenger through -# 1992, from the IATA otherwise. As noted below, Shanks & Pottenger say that -# America/Cordoba split into 6 subregions during 1991/1992, one of which -# was America/San_Luis, but we haven't verified this yet so for now we'll -# keep America/Cordoba a single region rather than splitting it into the -# other 5 subregions. - -# From Mariano Absatz (2009-03-13): -# Yesterday (with our usual 2-day notice) the Province of San Luis -# decided that next Sunday instead of "staying" @utc-03:00 they will go -# to utc-04:00 until the second Saturday in October... -# -# The press release is at -# http://www.sanluis.gov.ar/SL/Paginas/NoticiaDetalle.asp?TemaId=1&InfoPrensaId=3102 -# (I couldn't find the decree, but www.sanluis.gov.ar -# is the official page for the Province Government.) -# -# There's also a note in only one of the major national papers ... -# http://www.lanacion.com.ar/nota.asp?nota_id=1107912 -# -# The press release says [quick and dirty translation]: -# ... announced that next Sunday, at 00:00, Puntanos (the San Luis -# inhabitants) will have to turn back one hour their clocks -# -# Since then, San Luis will establish its own Province timezone. Thus, -# during 2009, this timezone change will run from 00:00 the third Sunday -# in March until 24:00 of the second Saturday in October. - -# From Mariano Absatz (2009-10-16): -# ...the Province of San Luis is a case in itself. -# -# The Law at -# http://www.diputadossanluis.gov.ar/diputadosasp/paginas/verNorma.asp?NormaID=276 -# is ambiguous because establishes a calendar from the 2nd Sunday in -# October at 0:00 thru the 2nd Saturday in March at 24:00 and the -# complement of that starting on the 2nd Sunday of March at 0:00 and -# ending on the 2nd Saturday of March at 24:00. -# -# This clearly breaks every time the 1st of March or October is a Sunday. -# -# IMHO, the "spirit of the Law" is to make the changes at 0:00 on the 2nd -# Sunday of October and March. -# -# The problem is that the changes in the rest of the Provinces that did -# change in 2007/2008, were made according to the Federal Law and Decrees -# that did so on the 3rd Sunday of October and March. -# -# In fact, San Luis actually switched from UTC-4 to UTC-3 last Sunday -# (October 11th) at 0:00. -# -# So I guess a new set of rules, besides "Arg", must be made and the last -# America/Argentina/San_Luis entries should change to use these... -# ... - -# From Alexander Krivenyshev (2010-04-09): -# According to news reports from El Diario de la República Province San -# Luis, Argentina (standard time UTC-04) will keep Daylight Saving Time -# after April 11, 2010 - will continue to have same time as rest of -# Argentina (UTC-3) (no DST). -# -# Confirmaron la prórroga del huso horario de verano (Spanish) -# http://www.eldiariodelarepublica.com/index.php?option=com_content&task=view&id=29383&Itemid=9 -# or (some English translation): -# http://www.worldtimezone.com/dst_news/dst_news_argentina08.html - -# From Mariano Absatz (2010-04-12): -# yes...I can confirm this...and given that San Luis keeps calling -# UTC-03:00 "summer time", we should't just let San Luis go back to "Arg" -# rules...San Luis is still using "Western ARgentina Time" and it got -# stuck on Summer daylight savings time even though the summer is over. - -# From Paul Eggert (2018-01-23): -# Perhaps San Luis operates on the legal fiction that it is at -04 -# with perpetual daylight saving time, but ordinary usage typically seems to -# just say it's at -03; see, for example, -# https://es.wikipedia.org/wiki/Hora_oficial_argentina -# We've documented similar situations as being plain changes to -# standard time, so let's do that here too. This does not change UTC -# offsets, only tm_isdst and the time zone abbreviations. One minor -# plus is that this silences a zic complaint that there's no POSIX TZ -# setting for time stamps past 2038. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -# -# Buenos Aires (BA), Capital Federal (CF), -Zone America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 Oct 31 - -4:16:48 - CMT 1920 May # Córdoba Mean Time - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 Arg -03/-02 -# -# Córdoba (CB), Santa Fe (SF), Entre Ríos (ER), Corrientes (CN), Misiones (MN), -# Chaco (CC), Formosa (FM), Santiago del Estero (SE) -# -# Shanks & Pottenger also make the following claims, which we haven't verified: -# - Formosa switched to -3:00 on 1991-01-07. -# - Misiones switched to -3:00 on 1990-12-29. -# - Chaco switched to -3:00 on 1991-01-04. -# - Santiago del Estero switched to -4:00 on 1991-04-01, -# then to -3:00 on 1991-04-26. -# -Zone America/Argentina/Cordoba -4:16:48 - LMT 1894 Oct 31 - -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 3 - -4:00 - -04 1991 Oct 20 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 Arg -03/-02 -# -# Salta (SA), La Pampa (LP), Neuquén (NQ), Rio Negro (RN) -Zone America/Argentina/Salta -4:21:40 - LMT 1894 Oct 31 - -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 3 - -4:00 - -04 1991 Oct 20 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 -# -# Tucumán (TM) -Zone America/Argentina/Tucuman -4:20:52 - LMT 1894 Oct 31 - -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 3 - -4:00 - -04 1991 Oct 20 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 Jun 1 - -4:00 - -04 2004 Jun 13 - -3:00 Arg -03/-02 -# -# La Rioja (LR) -Zone America/Argentina/La_Rioja -4:27:24 - LMT 1894 Oct 31 - -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 1 - -4:00 - -04 1991 May 7 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 Jun 1 - -4:00 - -04 2004 Jun 20 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 -# -# San Juan (SJ) -Zone America/Argentina/San_Juan -4:34:04 - LMT 1894 Oct 31 - -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 1 - -4:00 - -04 1991 May 7 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 May 31 - -4:00 - -04 2004 Jul 25 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 -# -# Jujuy (JY) -Zone America/Argentina/Jujuy -4:21:12 - LMT 1894 Oct 31 - -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1990 Mar 4 - -4:00 - -04 1990 Oct 28 - -4:00 1:00 -03 1991 Mar 17 - -4:00 - -04 1991 Oct 6 - -3:00 1:00 -02 1992 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 -# -# Catamarca (CT), Chubut (CH) -Zone America/Argentina/Catamarca -4:23:08 - LMT 1894 Oct 31 - -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1991 Mar 3 - -4:00 - -04 1991 Oct 20 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 Jun 1 - -4:00 - -04 2004 Jun 20 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 -# -# Mendoza (MZ) -Zone America/Argentina/Mendoza -4:35:16 - LMT 1894 Oct 31 - -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1990 Mar 4 - -4:00 - -04 1990 Oct 15 - -4:00 1:00 -03 1991 Mar 1 - -4:00 - -04 1991 Oct 15 - -4:00 1:00 -03 1992 Mar 1 - -4:00 - -04 1992 Oct 18 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 May 23 - -4:00 - -04 2004 Sep 26 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 -# -# San Luis (SL) - -Rule SanLuis 2008 2009 - Mar Sun>=8 0:00 0 - -Rule SanLuis 2007 2008 - Oct Sun>=8 0:00 1:00 - - -Zone America/Argentina/San_Luis -4:25:24 - LMT 1894 Oct 31 - -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1990 - -3:00 1:00 -02 1990 Mar 14 - -4:00 - -04 1990 Oct 15 - -4:00 1:00 -03 1991 Mar 1 - -4:00 - -04 1991 Jun 1 - -3:00 - -03 1999 Oct 3 - -4:00 1:00 -03 2000 Mar 3 - -3:00 - -03 2004 May 31 - -4:00 - -04 2004 Jul 25 - -3:00 Arg -03/-02 2008 Jan 21 - -4:00 SanLuis -04/-03 2009 Oct 11 - -3:00 - -03 -# -# Santa Cruz (SC) -Zone America/Argentina/Rio_Gallegos -4:36:52 - LMT 1894 Oct 31 - -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 Jun 1 - -4:00 - -04 2004 Jun 20 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 -# -# Tierra del Fuego, Antártida e Islas del Atlántico Sur (TF) -Zone America/Argentina/Ushuaia -4:33:12 - LMT 1894 Oct 31 - -4:16:48 - CMT 1920 May - -4:00 - -04 1930 Dec - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1999 Oct 3 - -4:00 Arg -04/-03 2000 Mar 3 - -3:00 - -03 2004 May 30 - -4:00 - -04 2004 Jun 20 - -3:00 Arg -03/-02 2008 Oct 18 - -3:00 - -03 - -# Aruba -Link America/Curacao America/Aruba - -# Bolivia -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/La_Paz -4:32:36 - LMT 1890 - -4:32:36 - CMT 1931 Oct 15 # Calamarca MT - -4:32:36 1:00 BST 1932 Mar 21 # Bolivia ST - -4:00 - -04 - -# Brazil - -# From Paul Eggert (1993-11-18): -# The mayor of Rio recently attempted to change the time zone rules -# just in his city, in order to leave more summer time for the tourist trade. -# The rule change lasted only part of the day; -# the federal government refused to follow the city's rules, and business -# was in a chaos, so the mayor backed down that afternoon. - -# From IATA SSIM (1996-02): -# _Only_ the following states in BR1 observe DST: Rio Grande do Sul (RS), -# Santa Catarina (SC), Paraná (PR), São Paulo (SP), Rio de Janeiro (RJ), -# Espírito Santo (ES), Minas Gerais (MG), Bahia (BA), Goiás (GO), -# Distrito Federal (DF), Tocantins (TO), Sergipe [SE] and Alagoas [AL]. -# [The last three states are new to this issue of the IATA SSIM.] - -# From Gwillim Law (1996-10-07): -# Geography, history (Tocantins was part of Goiás until 1989), and other -# sources of time zone information lead me to believe that AL, SE, and TO were -# always in BR1, and so the only change was whether or not they observed DST.... -# The earliest issue of the SSIM I have is 2/91. Each issue from then until -# 9/95 says that DST is observed only in the ten states I quoted from 9/95, -# along with Mato Grosso (MT) and Mato Grosso do Sul (MS), which are in BR2 -# (UTC-4).... The other two time zones given for Brazil are BR3, which is -# UTC-5, no DST, and applies only in the state of Acre (AC); and BR4, which is -# UTC-2, and applies to Fernando de Noronha (formerly FN, but I believe it's -# become part of the state of Pernambuco). The boundary between BR1 and BR2 -# has never been clearly stated. They've simply been called East and West. -# However, some conclusions can be drawn from another IATA manual: the Airline -# Coding Directory, which lists close to 400 airports in Brazil. For each -# airport it gives a time zone which is coded to the SSIM. From that -# information, I'm led to conclude that the states of Amapá (AP), Ceará (CE), -# Maranhão (MA), Paraíba (PR), Pernambuco (PE), Piauí (PI), and Rio Grande do -# Norte (RN), and the eastern part of Pará (PA) are all in BR1 without DST. - -# From Marcos Tadeu (1998-09-27): -# Brazilian official page - -# From Jesper Nørgaard (2000-11-03): -# [For an official list of which regions in Brazil use which time zones, see:] -# http://pcdsh01.on.br/Fusbr.htm -# http://pcdsh01.on.br/Fusbrhv.htm - -# From Celso Doria via David Madeo (2002-10-09): -# The reason for the delay this year has to do with elections in Brazil. -# -# Unlike in the United States, elections in Brazil are 100% computerized and -# the results are known almost immediately. Yesterday, it was the first -# round of the elections when 115 million Brazilians voted for President, -# Governor, Senators, Federal Deputies, and State Deputies. Nobody is -# counting (or re-counting) votes anymore and we know there will be a second -# round for the Presidency and also for some Governors. The 2nd round will -# take place on October 27th. -# -# The reason why the DST will only begin November 3rd is that the thousands -# of electoral machines used cannot have their time changed, and since the -# Constitution says the elections must begin at 8:00 AM and end at 5:00 PM, -# the Government decided to postpone DST, instead of changing the Constitution -# (maybe, for the next elections, it will be possible to change the clock)... - -# From Rodrigo Severo (2004-10-04): -# It's just the biannual change made necessary by the much hyped, supposedly -# modern Brazilian eletronic voting machines which, apparently, can't deal -# with a time change between the first and the second rounds of the elections. - -# From Steffen Thorsen (2007-09-20): -# Brazil will start DST on 2007-10-14 00:00 and end on 2008-02-17 00:00: -# http://www.mme.gov.br/site/news/detail.do;jsessionid=BBA06811AFCAAC28F0285210913513DA?newsId=13975 - -# From Paul Schulze (2008-06-24): -# ...by law number 11.662 of April 24, 2008 (published in the "Diario -# Oficial da União"...) in Brazil there are changes in the timezones, -# effective today (00:00am at June 24, 2008) as follows: -# -# a) The timezone UTC+5 is extinguished, with all the Acre state and the -# part of the Amazonas state that had this timezone now being put to the -# timezone UTC+4 -# b) The whole Pará state now is put at timezone UTC+3, instead of just -# part of it, as was before. -# -# This change follows a proposal of senator Tiao Viana of Acre state, that -# proposed it due to concerns about open television channels displaying -# programs inappropriate to youths in the states that had the timezone -# UTC+5 too early in the night. In the occasion, some more corrections -# were proposed, trying to unify the timezones of any given state. This -# change modifies timezone rules defined in decree 2.784 of 18 June, -# 1913. - -# From Rodrigo Severo (2008-06-24): -# Just correcting the URL: -# https://www.in.gov.br/imprensa/visualiza/index.jsp?jornal=do&secao=1&pagina=1&data=25/04/2008 -# -# As a result of the above Decree I believe the America/Rio_Branco -# timezone shall be modified from UTC-5 to UTC-4 and a new timezone shall -# be created to represent the...west side of the Pará State. I -# suggest this new timezone be called Santarem as the most -# important/populated city in the affected area. -# -# This new timezone would be the same as the Rio_Branco timezone up to -# the 2008/06/24 change which would be to UTC-3 instead of UTC-4. - -# From Alex Krivenyshev (2008-06-24): -# This is a quick reference page for New and Old Brazil Time Zones map. -# http://www.worldtimezone.com/brazil-time-new-old.php -# -# - 4 time zones replaced by 3 time zones - eliminating time zone UTC-05 -# (state Acre and the part of the Amazonas will be UTC/GMT-04) - western -# part of Par state is moving to one timezone UTC-03 (from UTC-04). - -# From Paul Eggert (2002-10-10): -# The official decrees referenced below are mostly taken from -# Decretos sobre o Horário de Verão no Brasil. -# http://pcdsh01.on.br/DecHV.html - -# From Steffen Thorsen (2008-08-29): -# As announced by the government and many newspapers in Brazil late -# yesterday, Brazil will start DST on 2008-10-19 (need to change rule) and -# it will end on 2009-02-15 (current rule for Brazil is fine). Based on -# past years experience with the elections, there was a good chance that -# the start was postponed to November, but it did not happen this year. -# -# It has not yet been posted to http://pcdsh01.on.br/DecHV.html -# -# An official page about it: -# http://www.mme.gov.br/site/news/detail.do?newsId=16722 -# Note that this link does not always work directly, but must be accessed -# by going to -# http://www.mme.gov.br/first -# -# One example link that works directly: -# http://jornale.com.br/index.php?option=com_content&task=view&id=13530&Itemid=54 -# (Portuguese) -# -# We have a written a short article about it as well: -# https://www.timeanddate.com/news/time/brazil-dst-2008-2009.html -# -# From Alexander Krivenyshev (2011-10-04): -# State Bahia will return to Daylight savings time this year after 8 years off. -# The announcement was made by Governor Jaques Wagner in an interview to a -# television station in Salvador. - -# In Portuguese: -# http://g1.globo.com/bahia/noticia/2011/10/governador-jaques-wagner-confirma-horario-de-verao-na-bahia.html -# https://noticias.terra.com.br/brasil/noticias/0,,OI5390887-EI8139,00-Bahia+volta+a+ter+horario+de+verao+apos+oito+anos.html - -# From Guilherme Bernardes Rodrigues (2011-10-07): -# There is news in the media, however there is still no decree about it. -# I just send a e-mail to Zulmira Brandao at http://pcdsh01.on.br/ the -# official agency about time in Brazil, and she confirmed that the old rule is -# still in force. - -# From Guilherme Bernardes Rodrigues (2011-10-14) -# It's official, the President signed a decree that includes Bahia in summer -# time. -# [ and in a second message (same day): ] -# I found the decree. -# -# DECRETO No. 7.584, DE 13 DE OUTUBRO DE 2011 -# Link : -# http://www.in.gov.br/visualiza/index.jsp?data=13/10/2011&jornal=1000&pagina=6&totalArquivos=6 - -# From Kelley Cook (2012-10-16): -# The governor of state of Bahia in Brazil announced on Thursday that -# due to public pressure, he is reversing the DST policy they implemented -# last year and will not be going to Summer Time on October 21st.... -# http://www.correio24horas.com.br/r/artigo/apos-pressoes-wagner-suspende-horario-de-verao-na-bahia - -# From Rodrigo Severo (2012-10-16): -# Tocantins state will have DST. -# https://noticias.terra.com.br/brasil/noticias/0,,OI6232536-EI306.html - -# From Steffen Thorsen (2013-09-20): -# Tocantins in Brazil is very likely not to observe DST from October.... -# http://conexaoto.com.br/2013/09/18/ministerio-confirma-que-tocantins-esta-fora-do-horario-de-verao-em-2013-mas-falta-publicacao-de-decreto -# We will keep this article updated when this is confirmed: -# https://www.timeanddate.com/news/time/brazil-starts-dst-2013.html - -# From Steffen Thorsen (2013-10-17): -# https://www.timeanddate.com/news/time/acre-amazonas-change-time-zone.html -# Senator Jorge Viana announced that Acre will change time zone on November 10. -# He did not specify the time of the change, nor if western parts of Amazonas -# will change as well. -# -# From Paul Eggert (2013-10-17): -# For now, assume western Amazonas will change as well. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -# Decree 20,466 (1931-10-01) -# Decree 21,896 (1932-01-10) -Rule Brazil 1931 only - Oct 3 11:00 1:00 - -Rule Brazil 1932 1933 - Apr 1 0:00 0 - -Rule Brazil 1932 only - Oct 3 0:00 1:00 - -# Decree 23,195 (1933-10-10) -# revoked DST. -# Decree 27,496 (1949-11-24) -# Decree 27,998 (1950-04-13) -Rule Brazil 1949 1952 - Dec 1 0:00 1:00 - -Rule Brazil 1950 only - Apr 16 1:00 0 - -Rule Brazil 1951 1952 - Apr 1 0:00 0 - -# Decree 32,308 (1953-02-24) -Rule Brazil 1953 only - Mar 1 0:00 0 - -# Decree 34,724 (1953-11-30) -# revoked DST. -# Decree 52,700 (1963-10-18) -# established DST from 1963-10-23 00:00 to 1964-02-29 00:00 -# in SP, RJ, GB, MG, ES, due to the prolongation of the drought. -# Decree 53,071 (1963-12-03) -# extended the above decree to all of the national territory on 12-09. -Rule Brazil 1963 only - Dec 9 0:00 1:00 - -# Decree 53,604 (1964-02-25) -# extended summer time by one day to 1964-03-01 00:00 (start of school). -Rule Brazil 1964 only - Mar 1 0:00 0 - -# Decree 55,639 (1965-01-27) -Rule Brazil 1965 only - Jan 31 0:00 1:00 - -Rule Brazil 1965 only - Mar 31 0:00 0 - -# Decree 57,303 (1965-11-22) -Rule Brazil 1965 only - Dec 1 0:00 1:00 - -# Decree 57,843 (1966-02-18) -Rule Brazil 1966 1968 - Mar 1 0:00 0 - -Rule Brazil 1966 1967 - Nov 1 0:00 1:00 - -# Decree 63,429 (1968-10-15) -# revoked DST. -# Decree 91,698 (1985-09-27) -Rule Brazil 1985 only - Nov 2 0:00 1:00 - -# Decree 92,310 (1986-01-21) -# Decree 92,463 (1986-03-13) -Rule Brazil 1986 only - Mar 15 0:00 0 - -# Decree 93,316 (1986-10-01) -Rule Brazil 1986 only - Oct 25 0:00 1:00 - -Rule Brazil 1987 only - Feb 14 0:00 0 - -# Decree 94,922 (1987-09-22) -Rule Brazil 1987 only - Oct 25 0:00 1:00 - -Rule Brazil 1988 only - Feb 7 0:00 0 - -# Decree 96,676 (1988-09-12) -# except for the states of AC, AM, PA, RR, RO, and AP (then a territory) -Rule Brazil 1988 only - Oct 16 0:00 1:00 - -Rule Brazil 1989 only - Jan 29 0:00 0 - -# Decree 98,077 (1989-08-21) -# with the same exceptions -Rule Brazil 1989 only - Oct 15 0:00 1:00 - -Rule Brazil 1990 only - Feb 11 0:00 0 - -# Decree 99,530 (1990-09-17) -# adopted by RS, SC, PR, SP, RJ, ES, MG, GO, MS, DF. -# Decree 99,629 (1990-10-19) adds BA, MT. -Rule Brazil 1990 only - Oct 21 0:00 1:00 - -Rule Brazil 1991 only - Feb 17 0:00 0 - -# Unnumbered decree (1991-09-25) -# adopted by RS, SC, PR, SP, RJ, ES, MG, BA, GO, MT, MS, DF. -Rule Brazil 1991 only - Oct 20 0:00 1:00 - -Rule Brazil 1992 only - Feb 9 0:00 0 - -# Unnumbered decree (1992-10-16) -# adopted by same states. -Rule Brazil 1992 only - Oct 25 0:00 1:00 - -Rule Brazil 1993 only - Jan 31 0:00 0 - -# Decree 942 (1993-09-28) -# adopted by same states, plus AM. -# Decree 1,252 (1994-09-22; -# web page corrected 2004-01-07) adopted by same states, minus AM. -# Decree 1,636 (1995-09-14) -# adopted by same states, plus MT and TO. -# Decree 1,674 (1995-10-13) -# adds AL, SE. -Rule Brazil 1993 1995 - Oct Sun>=11 0:00 1:00 - -Rule Brazil 1994 1995 - Feb Sun>=15 0:00 0 - -Rule Brazil 1996 only - Feb 11 0:00 0 - -# Decree 2,000 (1996-09-04) -# adopted by same states, minus AL, SE. -Rule Brazil 1996 only - Oct 6 0:00 1:00 - -Rule Brazil 1997 only - Feb 16 0:00 0 - -# From Daniel C. Sobral (1998-02-12): -# In 1997, the DS began on October 6. The stated reason was that -# because international television networks ignored Brazil's policy on DS, -# they bought the wrong times on satellite for coverage of Pope's visit. -# This year, the ending date of DS was postponed to March 1 -# to help dealing with the shortages of electric power. -# -# Decree 2,317 (1997-09-04), adopted by same states. -Rule Brazil 1997 only - Oct 6 0:00 1:00 - -# Decree 2,495 -# (1998-02-10) -Rule Brazil 1998 only - Mar 1 0:00 0 - -# Decree 2,780 (1998-09-11) -# adopted by the same states as before. -Rule Brazil 1998 only - Oct 11 0:00 1:00 - -Rule Brazil 1999 only - Feb 21 0:00 0 - -# Decree 3,150 -# (1999-08-23) adopted by same states. -# Decree 3,188 (1999-09-30) -# adds SE, AL, PB, PE, RN, CE, PI, MA and RR. -Rule Brazil 1999 only - Oct 3 0:00 1:00 - -Rule Brazil 2000 only - Feb 27 0:00 0 - -# Decree 3,592 (2000-09-06) -# adopted by the same states as before. -# Decree 3,630 (2000-10-13) -# repeals DST in PE and RR, effective 2000-10-15 00:00. -# Decree 3,632 (2000-10-17) -# repeals DST in SE, AL, PB, RN, CE, PI and MA, effective 2000-10-22 00:00. -# Decree 3,916 -# (2001-09-13) reestablishes DST in AL, CE, MA, PB, PE, PI, RN, SE. -Rule Brazil 2000 2001 - Oct Sun>=8 0:00 1:00 - -Rule Brazil 2001 2006 - Feb Sun>=15 0:00 0 - -# Decree 4,399 (2002-10-01) repeals DST in AL, CE, MA, PB, PE, PI, RN, SE. -# 4,399 -Rule Brazil 2002 only - Nov 3 0:00 1:00 - -# Decree 4,844 (2003-09-24; corrected 2003-09-26) repeals DST in BA, MT, TO. -# 4,844 -Rule Brazil 2003 only - Oct 19 0:00 1:00 - -# Decree 5,223 (2004-10-01) reestablishes DST in MT. -# 5,223 -Rule Brazil 2004 only - Nov 2 0:00 1:00 - -# Decree 5,539 (2005-09-19), -# adopted by the same states as before. -Rule Brazil 2005 only - Oct 16 0:00 1:00 - -# Decree 5,920 (2006-10-03), -# adopted by the same states as before. -Rule Brazil 2006 only - Nov 5 0:00 1:00 - -Rule Brazil 2007 only - Feb 25 0:00 0 - -# Decree 6,212 (2007-09-26), -# adopted by the same states as before. -Rule Brazil 2007 only - Oct Sun>=8 0:00 1:00 - -# From Frederico A. C. Neves (2008-09-10): -# According to this decree -# http://www.planalto.gov.br/ccivil_03/_Ato2007-2010/2008/Decreto/D6558.htm -# [t]he DST period in Brazil now on will be from the 3rd Oct Sunday to the -# 3rd Feb Sunday. There is an exception on the return date when this is -# the Carnival Sunday then the return date will be the next Sunday... -Rule Brazil 2008 2017 - Oct Sun>=15 0:00 1:00 - -Rule Brazil 2008 2011 - Feb Sun>=15 0:00 0 - -# Decree 7,584 (2011-10-13) -# added Bahia. -Rule Brazil 2012 only - Feb Sun>=22 0:00 0 - -# Decree 7,826 (2012-10-15) -# removed Bahia and added Tocantins. -# Decree 8,112 (2013-09-30) -# removed Tocantins. -Rule Brazil 2013 2014 - Feb Sun>=15 0:00 0 - -Rule Brazil 2015 only - Feb Sun>=22 0:00 0 - -Rule Brazil 2016 2022 - Feb Sun>=15 0:00 0 - -# From Steffen Thorsen (2017-12-18): -# According to many media sources, next year's DST start in Brazil will move to -# the first Sunday of November, and it will stay like that for the years after. -# ... https://www.timeanddate.com/news/time/brazil-delays-dst-2018.html -# From Steffen Thorsen (2017-12-20): -# http://www.planalto.gov.br/ccivil_03/_ato2015-2018/2017/decreto/D9242.htm -Rule Brazil 2018 max - Nov Sun>=1 0:00 1:00 - -Rule Brazil 2023 only - Feb Sun>=22 0:00 0 - -Rule Brazil 2024 2025 - Feb Sun>=15 0:00 0 - -Rule Brazil 2026 only - Feb Sun>=22 0:00 0 - -Rule Brazil 2027 2033 - Feb Sun>=15 0:00 0 - -Rule Brazil 2034 only - Feb Sun>=22 0:00 0 - -Rule Brazil 2035 2036 - Feb Sun>=15 0:00 0 - -Rule Brazil 2037 only - Feb Sun>=22 0:00 0 - -# From Arthur David Olson (2008-09-29): -# The next is wrong in some years but is better than nothing. -Rule Brazil 2038 max - Feb Sun>=15 0:00 0 - - -# The latest ruleset listed above says that the following states observe DST: -# DF, ES, GO, MG, MS, MT, PR, RJ, RS, SC, SP. - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -# -# Fernando de Noronha (administratively part of PE) -Zone America/Noronha -2:09:40 - LMT 1914 - -2:00 Brazil -02/-01 1990 Sep 17 - -2:00 - -02 1999 Sep 30 - -2:00 Brazil -02/-01 2000 Oct 15 - -2:00 - -02 2001 Sep 13 - -2:00 Brazil -02/-01 2002 Oct 1 - -2:00 - -02 -# Other Atlantic islands have no permanent settlement. -# These include Trindade and Martim Vaz (administratively part of ES), -# Rocas Atoll (RN), and the St Peter and St Paul Archipelago (PE). -# Fernando de Noronha was a separate territory from 1942-09-02 to 1989-01-01; -# it also included the Penedos. -# -# Amapá (AP), east Pará (PA) -# East Pará includes Belém, Marabá, Serra Norte, and São Félix do Xingu. -# The division between east and west Pará is the river Xingu. -# In the north a very small part from the river Javary (now Jari I guess, -# the border with Amapá) to the Amazon, then to the Xingu. -Zone America/Belem -3:13:56 - LMT 1914 - -3:00 Brazil -03/-02 1988 Sep 12 - -3:00 - -03 -# -# west Pará (PA) -# West Pará includes Altamira, Óbidos, Prainha, Oriximiná, and Santarém. -Zone America/Santarem -3:38:48 - LMT 1914 - -4:00 Brazil -04/-03 1988 Sep 12 - -4:00 - -04 2008 Jun 24 0:00 - -3:00 - -03 -# -# Maranhão (MA), Piauí (PI), Ceará (CE), Rio Grande do Norte (RN), -# Paraíba (PB) -Zone America/Fortaleza -2:34:00 - LMT 1914 - -3:00 Brazil -03/-02 1990 Sep 17 - -3:00 - -03 1999 Sep 30 - -3:00 Brazil -03/-02 2000 Oct 22 - -3:00 - -03 2001 Sep 13 - -3:00 Brazil -03/-02 2002 Oct 1 - -3:00 - -03 -# -# Pernambuco (PE) (except Atlantic islands) -Zone America/Recife -2:19:36 - LMT 1914 - -3:00 Brazil -03/-02 1990 Sep 17 - -3:00 - -03 1999 Sep 30 - -3:00 Brazil -03/-02 2000 Oct 15 - -3:00 - -03 2001 Sep 13 - -3:00 Brazil -03/-02 2002 Oct 1 - -3:00 - -03 -# -# Tocantins (TO) -Zone America/Araguaina -3:12:48 - LMT 1914 - -3:00 Brazil -03/-02 1990 Sep 17 - -3:00 - -03 1995 Sep 14 - -3:00 Brazil -03/-02 2003 Sep 24 - -3:00 - -03 2012 Oct 21 - -3:00 Brazil -03/-02 2013 Sep - -3:00 - -03 -# -# Alagoas (AL), Sergipe (SE) -Zone America/Maceio -2:22:52 - LMT 1914 - -3:00 Brazil -03/-02 1990 Sep 17 - -3:00 - -03 1995 Oct 13 - -3:00 Brazil -03/-02 1996 Sep 4 - -3:00 - -03 1999 Sep 30 - -3:00 Brazil -03/-02 2000 Oct 22 - -3:00 - -03 2001 Sep 13 - -3:00 Brazil -03/-02 2002 Oct 1 - -3:00 - -03 -# -# Bahia (BA) -# There are too many Salvadors elsewhere, so use America/Bahia instead -# of America/Salvador. -Zone America/Bahia -2:34:04 - LMT 1914 - -3:00 Brazil -03/-02 2003 Sep 24 - -3:00 - -03 2011 Oct 16 - -3:00 Brazil -03/-02 2012 Oct 21 - -3:00 - -03 -# -# Goiás (GO), Distrito Federal (DF), Minas Gerais (MG), -# Espírito Santo (ES), Rio de Janeiro (RJ), São Paulo (SP), Paraná (PR), -# Santa Catarina (SC), Rio Grande do Sul (RS) -Zone America/Sao_Paulo -3:06:28 - LMT 1914 - -3:00 Brazil -03/-02 1963 Oct 23 0:00 - -3:00 1:00 -02 1964 - -3:00 Brazil -03/-02 -# -# Mato Grosso do Sul (MS) -Zone America/Campo_Grande -3:38:28 - LMT 1914 - -4:00 Brazil -04/-03 -# -# Mato Grosso (MT) -Zone America/Cuiaba -3:44:20 - LMT 1914 - -4:00 Brazil -04/-03 2003 Sep 24 - -4:00 - -04 2004 Oct 1 - -4:00 Brazil -04/-03 -# -# Rondônia (RO) -Zone America/Porto_Velho -4:15:36 - LMT 1914 - -4:00 Brazil -04/-03 1988 Sep 12 - -4:00 - -04 -# -# Roraima (RR) -Zone America/Boa_Vista -4:02:40 - LMT 1914 - -4:00 Brazil -04/-03 1988 Sep 12 - -4:00 - -04 1999 Sep 30 - -4:00 Brazil -04/-03 2000 Oct 15 - -4:00 - -04 -# -# east Amazonas (AM): Boca do Acre, Jutaí, Manaus, Floriano Peixoto -# The great circle line from Tabatinga to Porto Acre divides -# east from west Amazonas. -Zone America/Manaus -4:00:04 - LMT 1914 - -4:00 Brazil -04/-03 1988 Sep 12 - -4:00 - -04 1993 Sep 28 - -4:00 Brazil -04/-03 1994 Sep 22 - -4:00 - -04 -# -# west Amazonas (AM): Atalaia do Norte, Boca do Maoco, Benjamin Constant, -# Eirunepé, Envira, Ipixuna -Zone America/Eirunepe -4:39:28 - LMT 1914 - -5:00 Brazil -05/-04 1988 Sep 12 - -5:00 - -05 1993 Sep 28 - -5:00 Brazil -05/-04 1994 Sep 22 - -5:00 - -05 2008 Jun 24 0:00 - -4:00 - -04 2013 Nov 10 - -5:00 - -05 -# -# Acre (AC) -Zone America/Rio_Branco -4:31:12 - LMT 1914 - -5:00 Brazil -05/-04 1988 Sep 12 - -5:00 - -05 2008 Jun 24 0:00 - -4:00 - -04 2013 Nov 10 - -5:00 - -05 - -# Chile - -# From Paul Eggert (2015-04-03): -# Shanks & Pottenger says America/Santiago introduced standard time in -# 1890 and rounds its UT offset to 70W40; guess that in practice this -# was the same offset as in 1916-1919. It also says Pacific/Easter -# standardized on 109W22 in 1890; assume this didn't change the clocks. -# -# Dates for America/Santiago from 1910 to 2004 are primarily from -# the following source, cited by Oscar van Vlijmen (2006-10-08): -# [1] Chile Law -# http://www.webexhibits.org/daylightsaving/chile.html -# This contains a copy of this official table: -# Cambios en la hora oficial de Chile desde 1900 (retrieved 2008-03-30) -# https://web.archive.org/web/20080330200901/http://www.horaoficial.cl/cambio.htm -# [1] needs several corrections, though. -# -# The first set of corrections is from: -# [2] History of the Official Time of Chile -# http://www.horaoficial.cl/ing/horaof_ing.html (retrieved 2012-03-06). See: -# https://web.archive.org/web/20120306042032/http://www.horaoficial.cl/ing/horaof_ing.html -# This is an English translation of: -# Historia de la hora oficial de Chile (retrieved 2012-10-24). See: -# https://web.archive.org/web/20121024234627/http://www.horaoficial.cl/horaof.htm -# A fancier Spanish version (requiring mouse-clicking) is at: -# http://www.horaoficial.cl/historia_hora.html -# Conflicts between [1] and [2] were resolved as follows: -# -# - [1] says the 1910 transition was Jan 1, [2] says Jan 10 and cites -# Boletín No. 1, Aviso No. 1 (1910). Go with [2]. -# -# - [1] says SMT was -4:42:45, [2] says Chile's official time from -# 1916 to 1919 was -4:42:46.3, the meridian of Chile's National -# Astronomical Observatory (OAN), then located in what is now -# Quinta Normal in Santiago. Go with [2], rounding it to -4:42:46. -# -# - [1] says the 1918 transition was Sep 1, [2] says Sep 10 and cites -# Boletín No. 22, Aviso No. 129/1918 (1918-08-23). Go with [2]. -# -# - [1] does not give times for transitions; assume they occur -# at midnight mainland time, the current common practice. However, -# go with [2]'s specification of 23:00 for the 1947-05-21 transition. -# -# Another correction to [1] is from Jesper Nørgaard Welen, who -# wrote (2006-10-08), "I think that there are some obvious mistakes in -# the suggested link from Oscar van Vlijmen,... for instance entry 66 -# says that GMT-4 ended 1990-09-12 while entry 67 only begins GMT-3 at -# 1990-09-15 (they should have been 1990-09-15 and 1990-09-16 -# respectively), but anyhow it clears up some doubts too." -# -# Data for Pacific/Easter from 1910 through 1967 come from Shanks & -# Pottenger. After that, for lack of better info assume -# Pacific/Easter is always two hours behind America/Santiago; -# this is known to work for DST transitions starting in 2008 and -# may well be true for earlier transitions. - -# From Eduardo Krell (1995-10-19): -# The law says to switch to DST at midnight [24:00] on the second SATURDAY -# of October.... The law is the same for March and October. -# (1998-09-29): -# Because of the drought this year, the government decided to go into -# DST earlier (saturday 9/26 at 24:00). This is a one-time change only ... -# (unless there's another dry season next year, I guess). - -# From Julio I. Pacheco Troncoso (1999-03-18): -# Because of the same drought, the government decided to end DST later, -# on April 3, (one-time change). - -# From Germán Poo-Caamaño (2008-03-03): -# Due to drought, Chile extends Daylight Time in three weeks. This -# is one-time change (Saturday 3/29 at 24:00 for America/Santiago -# and Saturday 3/29 at 22:00 for Pacific/Easter) -# The Supreme Decree is located at -# http://www.shoa.cl/servicios/supremo316.pdf -# -# From José Miguel Garrido (2008-03-05): -# http://www.shoa.cl/noticias/2008/04hora/hora.htm - -# From Angel Chiang (2010-03-04): -# Subject: DST in Chile exceptionally extended to 3 April due to earthquake -# http://www.gobiernodechile.cl/viewNoticia.aspx?idArticulo=30098 -# -# From Arthur David Olson (2010-03-06): -# Angel Chiang's message confirmed by Julio Pacheco; Julio provided a patch. - -# From Glenn Eychaner (2011-03-28): -# http://diario.elmercurio.com/2011/03/28/_portada/_portada/noticias/7565897A-CA86-49E6-9E03-660B21A4883E.htm?id=3D{7565897A-CA86-49E6-9E03-660B21A4883E} -# In English: -# Chile's clocks will go back an hour this year on the 7th of May instead -# of this Saturday. They will go forward again the 3rd Saturday in -# August, not in October as they have since 1968. - -# From Mauricio Parada (2012-02-22), translated by Glenn Eychaner (2012-02-23): -# As stated in the website of the Chilean Energy Ministry -# http://www.minenergia.cl/ministerio/noticias/generales/gobierno-anuncia-fechas-de-cambio-de.html -# The Chilean Government has decided to postpone the entrance into winter time -# (to leave DST) from March 11 2012 to April 28th 2012.... -# Quote from the website communication: -# -# 6. For the year 2012, the dates of entry into winter time will be as follows: -# a. Saturday April 28, 2012, clocks should go back 60 minutes; that is, at -# 23:59:59, instead of passing to 0:00, the time should be adjusted to be 23:00 -# of the same day. -# b. Saturday, September 1, 2012, clocks should go forward 60 minutes; that is, -# at 23:59:59, instead of passing to 0:00, the time should be adjusted to be -# 01:00 on September 2. - -# From Steffen Thorsen (2013-02-15): -# According to several news sources, Chile has extended DST this year, -# they will end DST later and start DST earlier than planned. They -# hope to save energy. The new end date is 2013-04-28 00:00 and new -# start date is 2013-09-08 00:00.... -# http://www.gob.cl/informa/2013/02/15/gobierno-anuncia-fechas-de-cambio-de-hora-para-el-ano-2013.htm - -# From José Miguel Garrido (2014-02-19): -# Today appeared in the Diario Oficial a decree amending the time change -# dates to 2014. -# DST End: last Saturday of April 2014 (Sun 27 Apr 2014 03:00 UTC) -# DST Start: first Saturday of September 2014 (Sun 07 Sep 2014 04:00 UTC) -# http://www.diariooficial.interior.gob.cl//media/2014/02/19/do-20140219.pdf - -# From Eduardo Romero Urra (2015-03-03): -# Today has been published officially that Chile will use the DST time -# permanently until March 25 of 2017 -# http://www.diariooficial.interior.gob.cl/media/2015/03/03/1-large.jpg -# -# From Paul Eggert (2015-03-03): -# For now, assume that the extension will persist indefinitely. - -# From Juan Correa (2016-03-18): -# The decree regarding DST has been published in today's Official Gazette: -# http://www.diariooficial.interior.gob.cl/versiones-anteriores/do/20160318/ -# http://www.leychile.cl/Navegar?idNorma=1088502 -# It does consider the second Saturday of May and August as the dates -# for the transition; and it lists DST dates until 2019, but I think -# this scheme will stick. -# -# From Paul Eggert (2016-03-18): -# For now, assume the pattern holds for the indefinite future. -# The decree says transitions occur at 24:00; in practice this appears -# to mean 24:00 mainland time, not 24:00 local time, so that Easter -# Island is always two hours behind the mainland. - -# From Juan Correa (2016-12-04): -# Magallanes region ... will keep DST (UTC -3) all year round.... -# http://www.soychile.cl/Santiago/Sociedad/2016/12/04/433428/Bachelet-firmo-el-decreto-para-establecer-un-horario-unico-para-la-Region-de-Magallanes.aspx -# -# From Deborah Goldsmith (2017-01-19): -# http://www.diariooficial.interior.gob.cl/publicaciones/2017/01/17/41660/01/1169626.pdf -# From Paul Eggert (2017-01-19): -# The above says the Magallanes change expires 2019-05-11 at 24:00, -# so in theory, they will revert to -04/-03 after that, which means -# they will switch from -03 to -04 one hour after Santiago does that day. -# For now, assume that they will not revert. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Chile 1927 1931 - Sep 1 0:00 1:00 - -Rule Chile 1928 1932 - Apr 1 0:00 0 - -Rule Chile 1968 only - Nov 3 4:00u 1:00 - -Rule Chile 1969 only - Mar 30 3:00u 0 - -Rule Chile 1969 only - Nov 23 4:00u 1:00 - -Rule Chile 1970 only - Mar 29 3:00u 0 - -Rule Chile 1971 only - Mar 14 3:00u 0 - -Rule Chile 1970 1972 - Oct Sun>=9 4:00u 1:00 - -Rule Chile 1972 1986 - Mar Sun>=9 3:00u 0 - -Rule Chile 1973 only - Sep 30 4:00u 1:00 - -Rule Chile 1974 1987 - Oct Sun>=9 4:00u 1:00 - -Rule Chile 1987 only - Apr 12 3:00u 0 - -Rule Chile 1988 1990 - Mar Sun>=9 3:00u 0 - -Rule Chile 1988 1989 - Oct Sun>=9 4:00u 1:00 - -Rule Chile 1990 only - Sep 16 4:00u 1:00 - -Rule Chile 1991 1996 - Mar Sun>=9 3:00u 0 - -Rule Chile 1991 1997 - Oct Sun>=9 4:00u 1:00 - -Rule Chile 1997 only - Mar 30 3:00u 0 - -Rule Chile 1998 only - Mar Sun>=9 3:00u 0 - -Rule Chile 1998 only - Sep 27 4:00u 1:00 - -Rule Chile 1999 only - Apr 4 3:00u 0 - -Rule Chile 1999 2010 - Oct Sun>=9 4:00u 1:00 - -Rule Chile 2000 2007 - Mar Sun>=9 3:00u 0 - -# N.B.: the end of March 29 in Chile is March 30 in Universal time, -# which is used below in specifying the transition. -Rule Chile 2008 only - Mar 30 3:00u 0 - -Rule Chile 2009 only - Mar Sun>=9 3:00u 0 - -Rule Chile 2010 only - Apr Sun>=1 3:00u 0 - -Rule Chile 2011 only - May Sun>=2 3:00u 0 - -Rule Chile 2011 only - Aug Sun>=16 4:00u 1:00 - -Rule Chile 2012 2014 - Apr Sun>=23 3:00u 0 - -Rule Chile 2012 2014 - Sep Sun>=2 4:00u 1:00 - -Rule Chile 2016 max - May Sun>=9 3:00u 0 - -Rule Chile 2016 max - Aug Sun>=9 4:00u 1:00 - -# IATA SSIM anomalies: (1992-02) says 1992-03-14; -# (1996-09) says 1998-03-08. Ignore these. -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Santiago -4:42:46 - LMT 1890 - -4:42:46 - SMT 1910 Jan 10 # Santiago Mean Time - -5:00 - -05 1916 Jul 1 - -4:42:46 - SMT 1918 Sep 10 - -4:00 - -04 1919 Jul 1 - -4:42:46 - SMT 1927 Sep 1 - -5:00 Chile -05/-04 1932 Sep 1 - -4:00 - -04 1942 Jun 1 - -5:00 - -05 1942 Aug 1 - -4:00 - -04 1946 Jul 15 - -4:00 1:00 -03 1946 Sep 1 # central Chile - -4:00 - -04 1947 Apr 1 - -5:00 - -05 1947 May 21 23:00 - -4:00 Chile -04/-03 -Zone America/Punta_Arenas -4:43:40 - LMT 1890 - -4:42:46 - SMT 1910 Jan 10 - -5:00 - -05 1916 Jul 1 - -4:42:46 - SMT 1918 Sep 10 - -4:00 - -04 1919 Jul 1 - -4:42:46 - SMT 1927 Sep 1 - -5:00 Chile -05/-04 1932 Sep 1 - -4:00 - -04 1942 Jun 1 - -5:00 - -05 1942 Aug 1 - -4:00 - -04 1947 Apr 1 - -5:00 - -05 1947 May 21 23:00 - -4:00 Chile -04/-03 2016 Dec 4 - -3:00 - -03 -Zone Pacific/Easter -7:17:28 - LMT 1890 - -7:17:28 - EMT 1932 Sep # Easter Mean Time - -7:00 Chile -07/-06 1982 Mar 14 3:00u # Easter Time - -6:00 Chile -06/-05 -# -# Salas y Gómez Island is uninhabited. -# Other Chilean locations, including Juan Fernández Is, Desventuradas Is, -# and Antarctic bases, are like America/Santiago. - -# Antarctic base using South American rules -# (See the file 'antarctica' for more.) -# -# Palmer, Anvers Island, since 1965 (moved 2 miles in 1968) -# -# From Ethan Dicks (1996-10-06): -# It keeps the same time as Punta Arenas, Chile, because, just like us -# and the South Pole, that's the other end of their supply line.... -# I verified with someone who was there that since 1980, -# Palmer has followed Chile. Prior to that, before the Falklands War, -# Palmer used to be supplied from Argentina. -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Antarctica/Palmer 0 - -00 1965 - -4:00 Arg -04/-03 1969 Oct 5 - -3:00 Arg -03/-02 1982 May - -4:00 Chile -04/-03 2016 Dec 4 - -3:00 - -03 - -# Colombia - -# Milne gives 4:56:16.4 for Bogotá time in 1899; round to nearest. He writes, -# "A variation of fifteen minutes in the public clocks of Bogota is not rare." - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule CO 1992 only - May 3 0:00 1:00 - -Rule CO 1993 only - Apr 4 0:00 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Bogota -4:56:16 - LMT 1884 Mar 13 - -4:56:16 - BMT 1914 Nov 23 # Bogotá Mean Time - -5:00 CO -05/-04 -# Malpelo, Providencia, San Andres -# no information; probably like America/Bogota - -# Curaçao - -# Milne gives 4:35:46.9 for Curaçao mean time; round to nearest. -# -# From Paul Eggert (2006-03-22): -# Shanks & Pottenger say that The Bottom and Philipsburg have been at -# -4:00 since standard time was introduced on 1912-03-02; and that -# Kralendijk and Rincon used Kralendijk Mean Time (-4:33:08) from -# 1912-02-02 to 1965-01-01. The former is dubious, since S&P also say -# Saba Island has been like Curaçao. -# This all predates our 1970 cutoff, though. -# -# By July 2007 Curaçao and St Maarten are planned to become -# associated states within the Netherlands, much like Aruba; -# Bonaire, Saba and St Eustatius would become directly part of the -# Netherlands as Kingdom Islands. This won't affect their time zones -# though, as far as we know. -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Curacao -4:35:47 - LMT 1912 Feb 12 # Willemstad - -4:30 - -0430 1965 - -4:00 - AST - -# From Arthur David Olson (2011-06-15): -# use links for places with new iso3166 codes. -# The name "Lower Prince's Quarter" is both longer than fourteen characters -# and contains an apostrophe; use "Lower_Princes" below. - -Link America/Curacao America/Lower_Princes # Sint Maarten -Link America/Curacao America/Kralendijk # Caribbean Netherlands - -# Ecuador -# -# Milne says the Central and South American Telegraph Company used -5:24:15. -# -# From Alois Treindl (2016-12-15): -# https://www.elcomercio.com/actualidad/hora-sixto-1993.html -# ... Whether the law applied also to Galápagos, I do not know. -# From Paul Eggert (2016-12-15): -# https://www.elcomercio.com/afull/modificacion-husohorario-ecuador-presidentes-decreto.html -# This says President Sixto Durán Ballén signed decree No. 285, which -# established DST from 1992-11-28 to 1993-02-05; it does not give transition -# times. The people called it "hora de Sixto" ("Sixto hour"). The change did -# not go over well; a popular song "Qué hora es" by Jaime Guevara had lyrics -# that included "Amanecía en mitad de la noche, los guaguas iban a clase sin -# sol" ("It was dawning in the middle of the night, the buses went to class -# without sun"). Although Ballén's campaign slogan was "Ni un paso atrás" -# (Not one step back), the clocks went back in 1993 and the experiment was not -# repeated. For now, assume transitions were at 00:00 local time country-wide. -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Ecuador 1992 only - Nov 28 0:00 1:00 - -Rule Ecuador 1993 only - Feb 5 0:00 0 - -# -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Guayaquil -5:19:20 - LMT 1890 - -5:14:00 - QMT 1931 # Quito Mean Time - -5:00 Ecuador -05/-04 -Zone Pacific/Galapagos -5:58:24 - LMT 1931 # Puerto Baquerizo Moreno - -5:00 - -05 1986 - -6:00 Ecuador -06/-05 - -# Falklands - -# From Paul Eggert (2006-03-22): -# Between 1990 and 2000 inclusive, Shanks & Pottenger and the IATA agree except -# the IATA gives 1996-09-08. Go with Shanks & Pottenger. - -# From Falkland Islands Government Office, London (2001-01-22) -# via Jesper Nørgaard: -# ... the clocks revert back to Local Mean Time at 2 am on Sunday 15 -# April 2001 and advance one hour to summer time at 2 am on Sunday 2 -# September. It is anticipated that the clocks will revert back at 2 -# am on Sunday 21 April 2002 and advance to summer time at 2 am on -# Sunday 1 September. - -# From Rives McDow (2001-02-13): -# -# I have communicated several times with people there, and the last -# time I had communications that was helpful was in 1998. Here is -# what was said then: -# -# "The general rule was that Stanley used daylight saving and the Camp -# did not. However for various reasons many people in the Camp have -# started to use daylight saving (known locally as 'Stanley Time') -# There is no rule as to who uses daylight saving - it is a matter of -# personal choice and so it is impossible to draw a map showing who -# uses it and who does not. Any list would be out of date as soon as -# it was produced. This year daylight saving ended on April 18/19th -# and started again on September 12/13th. I do not know what the rule -# is, but can find out if you like. We do not change at the same time -# as UK or Chile." -# -# I did have in my notes that the rule was "Second Saturday in Sep at -# 0:00 until third Saturday in Apr at 0:00". I think that this does -# not agree in some cases with Shanks; is this true? -# -# Also, there is no mention in the list that some areas in the -# Falklands do not use DST. I have found in my communications there -# that these areas are on the western half of East Falkland and all of -# West Falkland. Stanley is the only place that consistently observes -# DST. Again, as in other places in the world, the farmers don't like -# it. West Falkland is almost entirely sheep farmers. -# -# I know one lady there that keeps a list of which farm keeps DST and -# which doesn't each year. She runs a shop in Stanley, and says that -# the list changes each year. She uses it to communicate to her -# customers, catching them when they are home for lunch or dinner. - -# From Paul Eggert (2001-03-05): -# For now, we'll just record the time in Stanley, since we have no -# better info. - -# From Steffen Thorsen (2011-04-01): -# The Falkland Islands will not turn back clocks this winter, but stay on -# daylight saving time. -# -# One source: -# http://www.falklandnews.com/public/story.cfm?get=5914&source=3 -# -# We have gotten this confirmed by a clerk of the legislative assembly: -# Normally the clocks revert to Local Mean Time (UTC/GMT -4 hours) on the -# third Sunday of April at 0200hrs and advance to Summer Time (UTC/GMT -3 -# hours) on the first Sunday of September at 0200hrs. -# -# IMPORTANT NOTE: During 2011, on a trial basis, the Falkland Islands -# will not revert to local mean time, but clocks will remain on Summer -# time (UTC/GMT - 3 hours) throughout the whole of 2011. Any long term -# change to local time following the trial period will be notified. -# -# From Andrew Newman (2012-02-24) -# A letter from Justin McPhee, Chief Executive, -# Cable & Wireless Falkland Islands (dated 2012-02-22) -# states... -# The current Atlantic/Stanley entry under South America expects the -# clocks to go back to standard Falklands Time (FKT) on the 15th April. -# The database entry states that in 2011 Stanley was staying on fixed -# summer time on a trial basis only. FIG need to contact IANA and/or -# the maintainers of the database to inform them we're adopting -# the same policy this year and suggest recommendations for future years. -# -# For now we will assume permanent -03 for the Falklands -# until advised differently (to apply for 2012 and beyond, after the 2011 -# experiment was apparently successful.) -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Falk 1937 1938 - Sep lastSun 0:00 1:00 - -Rule Falk 1938 1942 - Mar Sun>=19 0:00 0 - -Rule Falk 1939 only - Oct 1 0:00 1:00 - -Rule Falk 1940 1942 - Sep lastSun 0:00 1:00 - -Rule Falk 1943 only - Jan 1 0:00 0 - -Rule Falk 1983 only - Sep lastSun 0:00 1:00 - -Rule Falk 1984 1985 - Apr lastSun 0:00 0 - -Rule Falk 1984 only - Sep 16 0:00 1:00 - -Rule Falk 1985 2000 - Sep Sun>=9 0:00 1:00 - -Rule Falk 1986 2000 - Apr Sun>=16 0:00 0 - -Rule Falk 2001 2010 - Apr Sun>=15 2:00 0 - -Rule Falk 2001 2010 - Sep Sun>=1 2:00 1:00 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Atlantic/Stanley -3:51:24 - LMT 1890 - -3:51:24 - SMT 1912 Mar 12 # Stanley Mean Time - -4:00 Falk -04/-03 1983 May - -3:00 Falk -03/-02 1985 Sep 15 - -4:00 Falk -04/-03 2010 Sep 5 2:00 - -3:00 - -03 - -# French Guiana -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Cayenne -3:29:20 - LMT 1911 Jul - -4:00 - -04 1967 Oct - -3:00 - -03 - -# Guyana -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Guyana -3:52:40 - LMT 1915 Mar # Georgetown - -3:45 - -0345 1975 Jul 31 - -3:00 - -03 1991 -# IATA SSIM (1996-06) says -4:00. Assume a 1991 switch. - -4:00 - -04 - -# Paraguay -# -# From Paul Eggert (2006-03-22): -# Shanks & Pottenger say that spring transitions are 01:00 -> 02:00, -# and autumn transitions are 00:00 -> 23:00. Go with pre-1999 -# editions of Shanks, and with the IATA, who say transitions occur at 00:00. -# -# From Waldemar Villamayor-Venialbo (2013-09-20): -# No time of the day is established for the adjustment, so people normally -# adjust their clocks at 0 hour of the given dates. -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Para 1975 1988 - Oct 1 0:00 1:00 - -Rule Para 1975 1978 - Mar 1 0:00 0 - -Rule Para 1979 1991 - Apr 1 0:00 0 - -Rule Para 1989 only - Oct 22 0:00 1:00 - -Rule Para 1990 only - Oct 1 0:00 1:00 - -Rule Para 1991 only - Oct 6 0:00 1:00 - -Rule Para 1992 only - Mar 1 0:00 0 - -Rule Para 1992 only - Oct 5 0:00 1:00 - -Rule Para 1993 only - Mar 31 0:00 0 - -Rule Para 1993 1995 - Oct 1 0:00 1:00 - -Rule Para 1994 1995 - Feb lastSun 0:00 0 - -Rule Para 1996 only - Mar 1 0:00 0 - -# IATA SSIM (2000-02) says 1999-10-10; ignore this for now. -# From Steffen Thorsen (2000-10-02): -# I have three independent reports that Paraguay changed to DST this Sunday -# (10-01). -# -# Translated by Gwillim Law (2001-02-27) from -# Noticias, a daily paper in Asunción, Paraguay (2000-10-01): -# http://www.diarionoticias.com.py/011000/nacional/naciona1.htm -# Starting at 0:00 today, the clock will be set forward 60 minutes, in -# fulfillment of Decree No. 7,273 of the Executive Power.... The time change -# system has been operating for several years. Formerly there was a separate -# decree each year; the new law has the same effect, but permanently. Every -# year, the time will change on the first Sunday of October; likewise, the -# clock will be set back on the first Sunday of March. -# -Rule Para 1996 2001 - Oct Sun>=1 0:00 1:00 - -# IATA SSIM (1997-09) says Mar 1; go with Shanks & Pottenger. -Rule Para 1997 only - Feb lastSun 0:00 0 - -# Shanks & Pottenger say 1999-02-28; IATA SSIM (1999-02) says 1999-02-27, but -# (1999-09) reports no date; go with above sources and Gerd Knops (2001-02-27). -Rule Para 1998 2001 - Mar Sun>=1 0:00 0 - -# From Rives McDow (2002-02-28): -# A decree was issued in Paraguay (No. 16350) on 2002-02-26 that changed the -# dst method to be from the first Sunday in September to the first Sunday in -# April. -Rule Para 2002 2004 - Apr Sun>=1 0:00 0 - -Rule Para 2002 2003 - Sep Sun>=1 0:00 1:00 - -# -# From Jesper Nørgaard Welen (2005-01-02): -# There are several sources that claim that Paraguay made -# a timezone rule change in autumn 2004. -# From Steffen Thorsen (2005-01-05): -# Decree 1,867 (2004-03-05) -# From Carlos Raúl Perasso via Jesper Nørgaard Welen (2006-10-13) -# http://www.presidencia.gov.py/decretos/D1867.pdf -Rule Para 2004 2009 - Oct Sun>=15 0:00 1:00 - -Rule Para 2005 2009 - Mar Sun>=8 0:00 0 - -# From Carlos Raúl Perasso (2010-02-18): -# By decree number 3958 issued yesterday -# http://www.presidencia.gov.py/v1/wp-content/uploads/2010/02/decreto3958.pdf -# Paraguay changes its DST schedule, postponing the March rule to April and -# modifying the October date. The decree reads: -# ... -# Art. 1. It is hereby established that from the second Sunday of the month of -# April of this year (2010), the official time is to be set back 60 minutes, -# and that on the first Sunday of the month of October, it is to be set -# forward 60 minutes, in all the territory of the Paraguayan Republic. -# ... -Rule Para 2010 max - Oct Sun>=1 0:00 1:00 - -Rule Para 2010 2012 - Apr Sun>=8 0:00 0 - -# -# From Steffen Thorsen (2013-03-07): -# Paraguay will end DST on 2013-03-24 00:00.... -# http://www.ande.gov.py/interna.php?id=1075 -# -# From Carlos Raúl Perasso (2013-03-15): -# The change in Paraguay is now final. Decree number 10780 -# http://www.presidencia.gov.py/uploads/pdf/presidencia-3b86ff4b691c79d4f5927ca964922ec74772ce857c02ca054a52a37b49afc7fb.pdf -# From Carlos Raúl Perasso (2014-02-28): -# Decree 1264 can be found at: -# http://www.presidencia.gov.py/archivos/documentos/DECRETO1264_ey9r8zai.pdf -Rule Para 2013 max - Mar Sun>=22 0:00 0 - - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Asuncion -3:50:40 - LMT 1890 - -3:50:40 - AMT 1931 Oct 10 # Asunción Mean Time - -4:00 - -04 1972 Oct - -3:00 - -03 1974 Apr - -4:00 Para -04/-03 - -# Peru -# -# From Evelyn C. Leeper via Mark Brader (2003-10-26) -# : -# When we were in Peru in 1985-1986, they apparently switched over -# sometime between December 29 and January 3 while we were on the Amazon. -# -# From Paul Eggert (2006-03-22): -# Shanks & Pottenger don't have this transition. Assume 1986 was like 1987. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Peru 1938 only - Jan 1 0:00 1:00 - -Rule Peru 1938 only - Apr 1 0:00 0 - -Rule Peru 1938 1939 - Sep lastSun 0:00 1:00 - -Rule Peru 1939 1940 - Mar Sun>=24 0:00 0 - -Rule Peru 1986 1987 - Jan 1 0:00 1:00 - -Rule Peru 1986 1987 - Apr 1 0:00 0 - -Rule Peru 1990 only - Jan 1 0:00 1:00 - -Rule Peru 1990 only - Apr 1 0:00 0 - -# IATA is ambiguous for 1993/1995; go with Shanks & Pottenger. -Rule Peru 1994 only - Jan 1 0:00 1:00 - -Rule Peru 1994 only - Apr 1 0:00 0 - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Lima -5:08:12 - LMT 1890 - -5:08:36 - LMT 1908 Jul 28 # Lima Mean Time? - -5:00 Peru -05/-04 - -# South Georgia -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Atlantic/South_Georgia -2:26:08 - LMT 1890 # Grytviken - -2:00 - -02 - -# South Sandwich Is -# uninhabited; scientific personnel have wintered - -# Suriname -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Paramaribo -3:40:40 - LMT 1911 - -3:40:52 - PMT 1935 # Paramaribo Mean Time - -3:40:36 - PMT 1945 Oct # The capital moved? - -3:30 - -0330 1984 Oct - -3:00 - -03 - -# Trinidad and Tobago -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Port_of_Spain -4:06:04 - LMT 1912 Mar 2 - -4:00 - AST - -# These all agree with Trinidad and Tobago since 1970. -Link America/Port_of_Spain America/Anguilla -Link America/Port_of_Spain America/Antigua -Link America/Port_of_Spain America/Dominica -Link America/Port_of_Spain America/Grenada -Link America/Port_of_Spain America/Guadeloupe -Link America/Port_of_Spain America/Marigot # St Martin (French part) -Link America/Port_of_Spain America/Montserrat -Link America/Port_of_Spain America/St_Barthelemy # St Barthélemy -Link America/Port_of_Spain America/St_Kitts # St Kitts & Nevis -Link America/Port_of_Spain America/St_Lucia -Link America/Port_of_Spain America/St_Thomas # Virgin Islands (US) -Link America/Port_of_Spain America/St_Vincent -Link America/Port_of_Spain America/Tortola # Virgin Islands (UK) - -# Uruguay -# From Paul Eggert (1993-11-18): -# Uruguay wins the prize for the strangest peacetime manipulation of the rules. -# -# From Tim Parenti (2018-02-20), per Jeremie Bonjour (2018-01-31) and Michael -# Deckers (2018-02-20): -# ... At least they kept good records... -# -# http://www.armada.mil.uy/ContenidosPDFs/sohma/web/almanaque/almanaque_2018.pdf#page=36 -# Page 36 of Almanaque 2018, published by the Oceanography, Hydrography, and -# Meteorology Service of the Uruguayan Navy, seems to give many transitions -# with greater clarity than we've had before. It directly references many laws -# and decrees which are, in turn, referenced below. They can be viewed in the -# public archives of the Diario Oficial (in Spanish) at -# http://www.impo.com.uy/diariooficial/ -# -# Ley No. 3920 of 1908-06-10 placed the determination of legal time under the -# auspices of the National Institute for the Prediction of Time. It is unclear -# exactly what offset was used during this period, though Ley No. 7200 of -# 1920-04-23 used the Observatory of the National Meteorological Institute in -# Montevideo (34° 54' 33" S, 56° 12' 45" W) as its reference meridian, -# retarding legal time by 15 minutes 9 seconds from 1920-04-30 24:00, -# resulting in UT-04. Assume the corresponding LMT of UT-03:44:51 (given on -# page 725 of the Proceedings of the Second Pan-American Scientific Congress, -# 1915-1916) was in use, and merely became official from 1908-06-10. -# https://www.impo.com.uy/diariooficial/1908/06/18/12 -# https://www.impo.com.uy/diariooficial/1920/04/27/9 -# -# Ley No. 7594 of 1923-06-28 specified legal time as Observatory time advanced -# by 44 minutes 51 seconds (UT-03) "from 30 September to 31 March", and by 14 -# minutes 51 seconds (UT-03:30) "the rest of the year"; a message from the -# National Council of Administration the same day, published directly below the -# law in the Diario Oficial, specified the first transition to be 1923-09-30 -# 24:00. This effectively established standard time at UT-03:30 with 30 -# minutes DST. Assume transitions at 24:00 on the specified days until Ley No. -# 7919 of 1926-03-05 ended this arrangement, repealing all "laws and other -# provisions which oppose" it, resulting in year-round UT-03:30; a Resolución -# of 1926-03-11 puts the final transition at 1926-03-31 24:00, the same as it -# would have been under the previous law. -# https://www.impo.com.uy/diariooficial/1923/07/02/2 -# https://www.impo.com.uy/diariooficial/1926/03/10/2 -# https://www.impo.com.uy/diariooficial/1926/03/18/2 -# -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule Uruguay 1923 1925 - Oct 1 0:00 0:30 - -Rule Uruguay 1924 1926 - Apr 1 0:00 0 - -# From Tim Parenti (2018-02-15): -# http://www.impo.com.uy/diariooficial/1933/10/27/6 -# -# It appears Ley No. 9122 of 1933 was never published as such in the Diario -# Oficial, but instead appeared as Document 26 in the Diario on Friday -# 1933-10-27 as a decree made Monday 1933-10-23 and filed under the Ministry of -# National Defense. It reinstituted a DST of 30 minutes (to UT-03) "from the -# last Sunday of October...until the last Saturday of March." In accordance -# with this provision, the first transition was explicitly specified in Article -# 2 of the decree as Saturday 1933-10-28 at 24:00; that is, Sunday 1933-10-29 -# at 00:00. Assume transitions at 00:00 Sunday throughout. -# -# Departing from the matter-of-fact nature of previous timekeeping laws, the -# 1933 decree "consider[s] the advantages of...the advance of legal time": -# -# "Whereas: The measure adopted by almost all nations at the time of the last -# World War still persists in North America and Europe, precisely because of -# the economic, hygienic, and social advantages derived from such an -# emergency measure... -# -# Whereas: The advance of the legal time during the summer seasons, by -# displacing social activity near sunrise, favors the citizen populations -# and especially the society that creates and works..." -# -# It further specified that "necessary measures" be taken to ensure that -# "public spectacles finish, in general, before [01:00]." -Rule Uruguay 1933 1938 - Oct lastSun 0:00 0:30 - -Rule Uruguay 1934 1941 - Mar lastSat 24:00 0 - -# From Tim Parenti (2018-02-15): -# Most of the Rules below, and their contemporaneous Zone lines, have been -# updated simply to match the Almanaque 2018. Although the document does not -# list exact transition times, midnight transitions were already present in our -# data here for all transitions through 2004-09, and this is both consistent -# with prior transitions and verified in several decrees marked below between -# 1939-09 and 2004-09, wherein the relevant text was typically of the form: -# -# "From 0 hours on [date], the legal time of the entire Republic will be... -# -# In accordance with [the preceding], on [previous date] at 24 hours, all -# clocks throughout the Republic will be [advanced/retarded] by..." -# -# It is possible that there is greater specificity to be found for the Rules -# below, but it is buried in no fewer than 40 different decrees individually -# referenced by the Almanaque for the period from 1939-09 to 2014-09. -# Four-fifths of these were promulgated less than two weeks before taking -# effect; more than half within a week and none more than 5 weeks. Only the -# handful with comments below have been checked with any thoroughness. -Rule Uruguay 1939 only - Oct 1 0:00 0:30 - -Rule Uruguay 1940 only - Oct 27 0:00 0:30 - -# From Tim Parenti (2018-02-15): -# Decreto 1145 of the Ministry of National Defense, dated 1941-07-26, specified -# UT-03 from Friday 1941-08-01 00:00, citing an "urgent...need to save fuel". -# http://www.impo.com.uy/diariooficial/1941/08/04/1 -Rule Uruguay 1941 only - Aug 1 0:00 0:30 - -# From Tim Parenti (2018-02-15): -# Decreto 1866 of the Ministry of National Defense, dated 1942-12-09, specified -# further advancement (to UT-02:30) from Sunday 1942-12-13 24:00. Since clocks -# never went back to UT-03:30 thereafter, this is modeled as advancing standard -# time by 30 minutes to UT-03, while retaining 30 minutes of DST. -# http://www.impo.com.uy/diariooficial/1942/12/16/3 -Rule Uruguay 1942 only - Dec 14 0:00 0:30 - -Rule Uruguay 1943 only - Mar 14 0:00 0 - -Rule Uruguay 1959 only - May 24 0:00 0:30 - -Rule Uruguay 1959 only - Nov 15 0:00 0 - -Rule Uruguay 1960 only - Jan 17 0:00 1:00 - -Rule Uruguay 1960 only - Mar 6 0:00 0 - -Rule Uruguay 1965 only - Apr 4 0:00 1:00 - -Rule Uruguay 1965 only - Sep 26 0:00 0 - -# From Tim Parenti (2018-02-15): -# Decreto 321/968 of 1968-05-25, citing emergency drought measures decreed the -# day before, brought clocks forward 30 minutes from Monday 1968-05-27 00:00. -# http://www.impo.com.uy/diariooficial/1968/05/30/5 -Rule Uruguay 1968 only - May 27 0:00 0:30 - -Rule Uruguay 1968 only - Dec 1 0:00 0 - -# From Tim Parenti (2018-02-15): -# Decreto 188/970 of 1970-04-23 instituted restrictions on electricity -# consumption "as a consequence of the current rainfall regime in the country". -# Articles 13 and 14 advanced clocks by an hour from Saturday 1970-04-25 00:00. -# http://www.impo.com.uy/diariooficial/1970/04/29/4 -Rule Uruguay 1970 only - Apr 25 0:00 1:00 - -Rule Uruguay 1970 only - Jun 14 0:00 0 - -Rule Uruguay 1972 only - Apr 23 0:00 1:00 - -Rule Uruguay 1972 only - Jul 16 0:00 0 - -# From Tim Parenti (2018-02-15): -# Decreto 29/974 of 1974-01-11, citing "the international rise in the price of -# oil", advanced clocks by 90 minutes (to UT-01:30). Decreto 163/974 of -# 1974-03-04 returned 60 of those minutes (to UT-02:30), and the remaining 30 -# minutes followed in Decreto 679/974 of 1974-08-29. -# http://www.impo.com.uy/diariooficial/1974/01/22/11 -# http://www.impo.com.uy/diariooficial/1974/03/14/3 -# http://www.impo.com.uy/diariooficial/1974/09/04/6 -Rule Uruguay 1974 only - Jan 13 0:00 1:30 - -Rule Uruguay 1974 only - Mar 10 0:00 0:30 - -Rule Uruguay 1974 only - Sep 1 0:00 0 - -Rule Uruguay 1974 only - Dec 22 0:00 1:00 - -Rule Uruguay 1975 only - Mar 30 0:00 0 - -Rule Uruguay 1976 only - Dec 19 0:00 1:00 - -Rule Uruguay 1977 only - Mar 6 0:00 0 - -Rule Uruguay 1977 only - Dec 4 0:00 1:00 - -Rule Uruguay 1978 1979 - Mar Sun>=1 0:00 0 - -Rule Uruguay 1978 only - Dec 17 0:00 1:00 - -Rule Uruguay 1979 only - Apr 29 0:00 1:00 - -Rule Uruguay 1980 only - Mar 16 0:00 0 - -# From Tim Parenti (2018-02-15): -# Decreto 725/987 of 1987-12-04 cited "better use of national tourist -# attractions" to advance clocks one hour from Monday 1987-12-14 00:00. -# http://www.impo.com.uy/diariooficial/1988/01/25/1 -Rule Uruguay 1987 only - Dec 14 0:00 1:00 - -Rule Uruguay 1988 only - Feb 28 0:00 0 - -Rule Uruguay 1988 only - Dec 11 0:00 1:00 - -Rule Uruguay 1989 only - Mar 5 0:00 0 - -Rule Uruguay 1989 only - Oct 29 0:00 1:00 - -Rule Uruguay 1990 only - Feb 25 0:00 0 - -# From Tim Parenti (2018-02-15), per Paul Eggert (1999-11-04): -# IATA agrees as below for 1990-10 through 1993-02. Per Almanaque 2018, the -# 1992/1993 season appears to be the first in over half a century where DST -# both began and ended pursuant to the same decree. -Rule Uruguay 1990 1991 - Oct Sun>=21 0:00 1:00 - -Rule Uruguay 1991 1992 - Mar Sun>=1 0:00 0 - -Rule Uruguay 1992 only - Oct 18 0:00 1:00 - -Rule Uruguay 1993 only - Feb 28 0:00 0 - -# From Eduardo Cota (2004-09-20): -# The Uruguayan government has decreed a change in the local time.... -# From Tim Parenti (2018-02-15): -# Decreto 328/004 of 2004-09-15. -# http://www.impo.com.uy/diariooficial/2004/09/23/documentos.pdf#page=1 -Rule Uruguay 2004 only - Sep 19 0:00 1:00 - -# From Steffen Thorsen (2005-03-11): -# Uruguay's DST was scheduled to end on Sunday, 2005-03-13, but in order to -# save energy ... it was postponed two weeks.... -# From Tim Parenti (2018-02-15): -# This 2005 postponement is not in Almanaque 2018. Go with the contemporaneous -# reporting, which is confirmed by Decreto 107/005 of 2005-03-10 amending -# Decreto 328/004: -# http://www.impo.com.uy/diariooficial/2005/03/15/documentos.pdf#page=1 -# The original decree specified a transition of 2005-03-12 24:00, but the new -# one specified 2005-03-27 02:00. -Rule Uruguay 2005 only - Mar 27 2:00 0 - -# From Eduardo Cota (2005-09-27): -# ...from 2005-10-09 at 02:00 local time, until 2006-03-12 at 02:00 local time, -# official time in Uruguay will be at GMT -2. -# From Tim Parenti (2018-02-15): -# Decreto 318/005 of 2005-09-19. -# http://www.impo.com.uy/diariooficial/2005/09/23/documentos.pdf#page=1 -Rule Uruguay 2005 only - Oct 9 2:00 1:00 - -Rule Uruguay 2006 2015 - Mar Sun>=8 2:00 0 - -# From Tim Parenti (2018-02-15), per Jesper Nørgaard Welen (2006-09-06): -# Decreto 311/006 of 2006-09-04 established regular DST from the first Sunday -# of October at 02:00 through the second Sunday of March at 02:00. Almanaque -# 2018 appears to have a few typoed dates through this period; ignore them. -# http://www.impo.com.uy/diariooficial/2006/09/08/documentos.pdf#page=1 -Rule Uruguay 2006 2014 - Oct Sun>=1 2:00 1:00 - -# From Steffen Thorsen (2015-06-30): -# ... it looks like they will not be using DST the coming summer: -# http://www.elobservador.com.uy/gobierno-resolvio-que-no-habra-cambio-horario-verano-n656787 -# http://www.republica.com.uy/este-ano-no-se-modificara-el-huso-horario-en-uruguay/523760/ -# From Paul Eggert (2015-06-30): -# Apparently restaurateurs complained that DST caused people to go to the beach -# instead of out to dinner. -# From Pablo Camargo (2015-07-13): -# http://archivo.presidencia.gub.uy/sci/decretos/2015/06/cons_min_201.pdf -# From Tim Parenti (2018-02-15): -# Decreto 178/015 of 2015-06-29; repeals Decreto 311/006. - -# This Zone can be simplified once we assume zic %z. -Zone America/Montevideo -3:44:51 - LMT 1908 Jun 10 - -3:44:51 - MMT 1920 May 1 # Montevideo MT - -4:00 - -04 1923 Oct 1 - -3:30 Uruguay -0330/-03 1942 Dec 14 - -3:00 Uruguay -03/-0230 1960 - -3:00 Uruguay -03/-02 1968 - -3:00 Uruguay -03/-0230 1970 - -3:00 Uruguay -03/-02 1974 - -3:00 Uruguay -03/-0130 1974 Mar 10 - -3:00 Uruguay -03/-0230 1974 Dec 22 - -3:00 Uruguay -03/-02 - -# Venezuela -# -# From Paul Eggert (2015-07-28): -# For the 1965 transition see Gaceta Oficial No. 27.619 (1964-12-15), p 205.533 -# http://www.pgr.gob.ve/dmdocuments/1964/27619.pdf -# -# From John Stainforth (2007-11-28): -# ... the change for Venezuela originally expected for 2007-12-31 has -# been brought forward to 2007-12-09. The official announcement was -# published today in the "Gaceta Oficial de la República Bolivariana -# de Venezuela, número 38.819" (official document for all laws or -# resolution publication) -# http://www.globovision.com/news.php?nid=72208 - -# From Alexander Krivenyshev (2016-04-15): -# https://actualidad.rt.com/actualidad/204758-venezuela-modificar-huso-horario-sequia-elnino -# -# From Paul Eggert (2016-04-15): -# Clocks advance 30 minutes on 2016-05-01 at 02:30.... -# "'Venezuela's new time-zone: hours without light, hours without water, -# hours of presidential broadcasts, hours of lines,' quipped comedian -# Jean Mary Curró ...". See: Cawthorne A, Kai D. Venezuela scraps -# half-hour time difference set by Chavez. Reuters 2016-04-15 14:50 -0400 -# https://www.reuters.com/article/us-venezuela-timezone-idUSKCN0XC2BE -# -# From Matt Johnson (2016-04-20): -# ... published in the official Gazette [2016-04-18], here: -# http://historico.tsj.gob.ve/gaceta_ext/abril/1842016/E-1842016-4551.pdf - -# Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Caracas -4:27:44 - LMT 1890 - -4:27:40 - CMT 1912 Feb 12 # Caracas Mean Time? - -4:30 - -0430 1965 Jan 1 0:00 - -4:00 - -04 2007 Dec 9 3:00 - -4:30 - -0430 2016 May 1 2:30 - -4:00 - -04 diff --git a/date_time/systemv b/date_time/systemv deleted file mode 100644 index d9e2995756..0000000000 --- a/date_time/systemv +++ /dev/null @@ -1,37 +0,0 @@ -# This file is in the public domain, so clarified as of -# 2009-05-17 by Arthur David Olson. - -# Old rules, should the need arise. -# No attempt is made to handle Newfoundland, since it cannot be expressed -# using the System V "TZ" scheme (half-hour offset), or anything outside -# North America (no support for non-standard DST start/end dates), nor -# the changes in the DST rules in the US after 1976 (which occurred after -# the old rules were written). -# -# If you need the old rules, uncomment ## lines. -# Compile this *without* leap second correction for true conformance. - -# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S -Rule SystemV min 1973 - Apr lastSun 2:00 1:00 D -Rule SystemV min 1973 - Oct lastSun 2:00 0 S -Rule SystemV 1974 only - Jan 6 2:00 1:00 D -Rule SystemV 1974 only - Nov lastSun 2:00 0 S -Rule SystemV 1975 only - Feb 23 2:00 1:00 D -Rule SystemV 1975 only - Oct lastSun 2:00 0 S -Rule SystemV 1976 max - Apr lastSun 2:00 1:00 D -Rule SystemV 1976 max - Oct lastSun 2:00 0 S - -# Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL] -## Zone SystemV/AST4ADT -4:00 SystemV A%sT -## Zone SystemV/EST5EDT -5:00 SystemV E%sT -## Zone SystemV/CST6CDT -6:00 SystemV C%sT -## Zone SystemV/MST7MDT -7:00 SystemV M%sT -## Zone SystemV/PST8PDT -8:00 SystemV P%sT -## Zone SystemV/YST9YDT -9:00 SystemV Y%sT -## Zone SystemV/AST4 -4:00 - AST -## Zone SystemV/EST5 -5:00 - EST -## Zone SystemV/CST6 -6:00 - CST -## Zone SystemV/MST7 -7:00 - MST -## Zone SystemV/PST8 -8:00 - PST -## Zone SystemV/YST9 -9:00 - YST -## Zone SystemV/HST10 -10:00 - HST diff --git a/date_time/windowsZones.xml b/date_time/windowsZones.xml index 88d38dc345..aa18dffe19 100644 --- a/date_time/windowsZones.xml +++ b/date_time/windowsZones.xml @@ -233,7 +233,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -336,7 +336,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -427,11 +427,11 @@ For terms of use, see http://www.unicode.org/copyright.html - + - + @@ -508,7 +508,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -762,7 +762,7 @@ For terms of use, see http://www.unicode.org/copyright.html - + @@ -783,4 +783,4 @@ For terms of use, see http://www.unicode.org/copyright.html - + \ No newline at end of file diff --git a/date_time/zonespec.csv b/date_time/zonespec.csv deleted file mode 100644 index 22a18cf991..0000000000 --- a/date_time/zonespec.csv +++ /dev/null @@ -1,433 +0,0 @@ -"ID","STD ABBR","STD NAME","DST ABBR","DST NAME","GMT offset","DST adjustment","DST Start Date rule","Start time","DST End date rule","End time" -"Africa/Abidjan","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"Africa/Accra","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"Africa/Addis_Ababa","EAT","EAT","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Africa/Algiers","CET","CET","","","+01:00:00","+00:00:00","","","","+00:00:00" -"Africa/Asmara","EAT","EAT","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Africa/Bamako","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"Africa/Bangui","WAT","WAT","","","+01:00:00","+00:00:00","","","","+00:00:00" -"Africa/Banjul","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"Africa/Bissau","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"Africa/Blantyre","CAT","CAT","","","+02:00:00","+00:00:00","","","","+00:00:00" -"Africa/Brazzaville","WAT","WAT","","","+01:00:00","+00:00:00","","","","+00:00:00" -"Africa/Bujumbura","CAT","CAT","","","+02:00:00","+00:00:00","","","","+00:00:00" -"Africa/Cairo","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;5;4","+00:00:00","-1;5;9","+00:00:00" -"Africa/Casablanca","WET","WET","WEST","WEST","+00:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Africa/Ceuta","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Africa/Conakry","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"Africa/Dakar","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"Africa/Dar_es_Salaam","EAT","EAT","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Africa/Djibouti","EAT","EAT","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Africa/Douala","WAT","WAT","","","+01:00:00","+00:00:00","","","","+00:00:00" -"Africa/El_Aaiun","WET","WET","WEST","WEST","+00:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Africa/Freetown","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"Africa/Gaborone","CAT","CAT","","","+02:00:00","+00:00:00","","","","+00:00:00" -"Africa/Harare","CAT","CAT","","","+02:00:00","+00:00:00","","","","+00:00:00" -"Africa/Johannesburg","SAST","SAST","","","+02:00:00","+00:00:00","","","","+00:00:00" -"Africa/Juba","EAT","EAT","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Africa/Kampala","EAT","EAT","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Africa/Khartoum","EAT","EAT","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Africa/Kigali","CAT","CAT","","","+02:00:00","+00:00:00","","","","+00:00:00" -"Africa/Kinshasa","WAT","WAT","","","+01:00:00","+00:00:00","","","","+00:00:00" -"Africa/Lagos","WAT","WAT","","","+01:00:00","+00:00:00","","","","+00:00:00" -"Africa/Libreville","WAT","WAT","","","+01:00:00","+00:00:00","","","","+00:00:00" -"Africa/Lome","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"Africa/Luanda","WAT","WAT","","","+01:00:00","+00:00:00","","","","+00:00:00" -"Africa/Lubumbashi","CAT","CAT","","","+02:00:00","+00:00:00","","","","+00:00:00" -"Africa/Lusaka","CAT","CAT","","","+02:00:00","+00:00:00","","","","+00:00:00" -"Africa/Malabo","WAT","WAT","","","+01:00:00","+00:00:00","","","","+00:00:00" -"Africa/Maputo","CAT","CAT","","","+02:00:00","+00:00:00","","","","+00:00:00" -"Africa/Maseru","SAST","SAST","","","+02:00:00","+00:00:00","","","","+00:00:00" -"Africa/Mbabane","SAST","SAST","","","+02:00:00","+00:00:00","","","","+00:00:00" -"Africa/Mogadishu","EAT","EAT","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Africa/Monrovia","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"Africa/Nairobi","EAT","EAT","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Africa/Ndjamena","WAT","WAT","","","+01:00:00","+00:00:00","","","","+00:00:00" -"Africa/Niamey","WAT","WAT","","","+01:00:00","+00:00:00","","","","+00:00:00" -"Africa/Nouakchott","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"Africa/Ouagadougou","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"Africa/Porto-Novo","WAT","WAT","","","+01:00:00","+00:00:00","","","","+00:00:00" -"Africa/Sao_Tome","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"Africa/Tripoli","EET","EET","","","+02:00:00","+00:00:00","","","","+00:00:00" -"Africa/Tunis","CET","CET","","","+01:00:00","+00:00:00","","","","+00:00:00" -"Africa/Windhoek","WAT","WAT","WAST","WAST","+02:00:00","+01:00:00","1;0;9","+02:00:00","1;0;4","+02:00:00" -"America/Adak","HAST","HAST","HADT","HADT","-10:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Anchorage","AKST","AKST","AKDT","AKDT","-09:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Anguilla","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Antigua","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Araguaina","BRT","BRT","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Argentina/Buenos_Aires","ART","ART","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Argentina/Catamarca","ART","ART","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Argentina/Cordoba","ART","ART","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Argentina/Jujuy","ART","ART","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Argentina/La_Rioja","ART","ART","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Argentina/Mendoza","ART","ART","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Argentina/Rio_Gallegos","ART","ART","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Argentina/Salta","ART","ART","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Argentina/San_Juan","ART","ART","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Argentina/San_Luis","ART","ART","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Argentina/Tucuman","ART","ART","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Argentina/Ushuaia","ART","ART","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Aruba","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Asuncion","PYT","PYT","PYST","PYST","-03:00:00","+01:00:00","1;0;10","+00:00:00","4;0;3","+00:00:00" -"America/Atikokan","EST","EST","","","-05:00:00","+00:00:00","","","","+00:00:00" -"America/Bahia","BRT","BRT","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Bahia_Banderas","CST","CST","CDT","CDT","-06:00:00","+01:00:00","1;0;4","+02:00:00","-1;0;10","+02:00:00" -"America/Barbados","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Belem","BRT","BRT","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Belize","CST","CST","","","-06:00:00","+00:00:00","","","","+00:00:00" -"America/Blanc-Sablon","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Boa_Vista","AMT","AMT","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Bogota","COT","COT","","","-05:00:00","+00:00:00","","","","+00:00:00" -"America/Boise","MST","MST","MDT","MDT","-07:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Cambridge_Bay","MST","MST","MDT","MDT","-07:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Campo_Grande","AMT","AMT","AMST","AMST","-03:00:00","+01:00:00","3;0;10","+00:00:00","-1;0;2","+00:00:00" -"America/Cancun","CST","CST","","","-06:00:00","+00:00:00","","","","+00:00:00" -"America/Caracas","VET","VET","","","-04:30:00","+00:00:00","","","","+00:00:00" -"America/Cayenne","GFT","GFT","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Cayman","EST","EST","","","-05:00:00","+00:00:00","","","","+00:00:00" -"America/Chicago","CST","CST","CDT","CDT","-06:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Chihuahua","MST","MST","MDT","MDT","-07:00:00","+01:00:00","1;0;4","+02:00:00","-1;0;10","+02:00:00" -"America/Costa_Rica","CST","CST","","","-06:00:00","+00:00:00","","","","+00:00:00" -"America/Creston","MST","MST","","","-07:00:00","+00:00:00","","","","+00:00:00" -"America/Cuiaba","AMT","AMT","AMST","AMST","-03:00:00","+01:00:00","3;0;10","+00:00:00","-1;0;2","+00:00:00" -"America/Curacao","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Danmarkshavn","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"America/Dawson","PST","PST","PDT","PDT","-08:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Dawson_Creek","MST","MST","","","-07:00:00","+00:00:00","","","","+00:00:00" -"America/Denver","MST","MST","MDT","MDT","-07:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Detroit","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Dominica","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Edmonton","MST","MST","MDT","MDT","-07:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Eirunepe","ACT","ACT","","","-05:00:00","+00:00:00","","","","+00:00:00" -"America/El_Salvador","CST","CST","","","-06:00:00","+00:00:00","","","","+00:00:00" -"America/Fortaleza","BRT","BRT","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Glace_Bay","AST","AST","ADT","ADT","-04:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Godthab","WGT","WGT","WGST","WGST","-03:00:00","+01:00:00","-1;6;3","+22:00:00","4;6;10","+23:00:00" -"America/Goose_Bay","AST","AST","ADT","ADT","-04:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Grand_Turk","AST","AST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+03:00:00" -"America/Grenada","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Guadeloupe","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Guatemala","CST","CST","","","-06:00:00","+00:00:00","","","","+00:00:00" -"America/Guayaquil","ECT","ECT","","","-05:00:00","+00:00:00","","","","+00:00:00" -"America/Guyana","GYT","GYT","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Halifax","AST","AST","ADT","ADT","-04:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Havana","CST","CST","CDT","CDT","-05:00:00","+01:00:00","2;0;3","+00:00:00","1;0;11","+01:00:00" -"America/Hermosillo","MST","MST","","","-07:00:00","+00:00:00","","","","+00:00:00" -"America/Indiana/Indianapolis","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Indiana/Knox","CST","CST","CDT","CDT","-06:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Indiana/Marengo","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Indiana/Petersburg","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Indiana/Tell_City","CST","CST","CDT","CDT","-06:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Indiana/Vevay","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Indiana/Vincennes","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Indiana/Winamac","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Inuvik","MST","MST","MDT","MDT","-07:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Iqaluit","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Jamaica","EST","EST","","","-05:00:00","+00:00:00","","","","+00:00:00" -"America/Juneau","AKST","AKST","AKDT","AKDT","-09:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Kentucky/Louisville","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Kentucky/Monticello","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Kralendijk","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/La_Paz","BOT","BOT","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Lima","PET","PET","","","-05:00:00","+00:00:00","","","","+00:00:00" -"America/Los_Angeles","PST","PST","PDT","PDT","-08:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Lower_Princes","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Maceio","BRT","BRT","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Managua","CST","CST","","","-06:00:00","+00:00:00","","","","+00:00:00" -"America/Manaus","AMT","AMT","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Marigot","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Martinique","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Matamoros","CST","CST","CDT","CDT","-06:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Mazatlan","MST","MST","MDT","MDT","-07:00:00","+01:00:00","1;0;4","+02:00:00","-1;0;10","+02:00:00" -"America/Menominee","CST","CST","CDT","CDT","-06:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Merida","CST","CST","CDT","CDT","-06:00:00","+01:00:00","1;0;4","+02:00:00","-1;0;10","+02:00:00" -"America/Metlakatla","PST","PST","","","-08:00:00","+00:00:00","","","","+00:00:00" -"America/Mexico_City","CST","CST","CDT","CDT","-06:00:00","+01:00:00","1;0;4","+02:00:00","-1;0;10","+02:00:00" -"America/Miquelon","PMST","PMST","PMDT","PMDT","-03:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Moncton","AST","AST","ADT","ADT","-04:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Monterrey","CST","CST","CDT","CDT","-06:00:00","+01:00:00","1;0;4","+02:00:00","-1;0;10","+02:00:00" -"America/Montevideo","UYT","UYT","UYST","UYST","-02:00:00","+01:00:00","1;0;10","+02:00:00","2;0;3","+02:00:00" -"America/Montreal","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Montserrat","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Nassau","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/New_York","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Nipigon","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Nome","AKST","AKST","AKDT","AKDT","-09:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Noronha","FNT","FNT","","","-02:00:00","+00:00:00","","","","+00:00:00" -"America/North_Dakota/Beulah","CST","CST","CDT","CDT","-06:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/North_Dakota/Center","CST","CST","CDT","CDT","-06:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/North_Dakota/New_Salem","CST","CST","CDT","CDT","-06:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Ojinaga","MST","MST","MDT","MDT","-07:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Panama","EST","EST","","","-05:00:00","+00:00:00","","","","+00:00:00" -"America/Pangnirtung","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Paramaribo","SRT","SRT","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Phoenix","MST","MST","","","-07:00:00","+00:00:00","","","","+00:00:00" -"America/Port-au-Prince","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Port_of_Spain","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Porto_Velho","AMT","AMT","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Puerto_Rico","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Rainy_River","CST","CST","CDT","CDT","-06:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Rankin_Inlet","CST","CST","CDT","CDT","-06:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Recife","BRT","BRT","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Regina","CST","CST","","","-06:00:00","+00:00:00","","","","+00:00:00" -"America/Resolute","CST","CST","CDT","CDT","-06:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Rio_Branco","ACT","ACT","","","-05:00:00","+00:00:00","","","","+00:00:00" -"America/Santa_Isabel","PST","PST","PDT","PDT","-08:00:00","+01:00:00","1;0;4","+02:00:00","-1;0;10","+02:00:00" -"America/Santarem","BRT","BRT","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Santiago","CLST","CLST","","","-03:00:00","+00:00:00","","","","+00:00:00" -"America/Santo_Domingo","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Sao_Paulo","BRT","BRT","BRST","BRST","-02:00:00","+01:00:00","3;0;10","+00:00:00","-1;0;2","+00:00:00" -"America/Scoresbysund","EGT","EGT","EGST","EGST","-01:00:00","+01:00:00","-1;0;3","+00:00:00","-1;0;10","+01:00:00" -"America/Sitka","AKST","AKST","AKDT","AKDT","-09:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/St_Barthelemy","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/St_Johns","NST","NST","NDT","NDT","-03:30:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/St_Kitts","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/St_Lucia","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/St_Thomas","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/St_Vincent","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Swift_Current","CST","CST","","","-06:00:00","+00:00:00","","","","+00:00:00" -"America/Tegucigalpa","CST","CST","","","-06:00:00","+00:00:00","","","","+00:00:00" -"America/Thule","AST","AST","ADT","ADT","-04:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Thunder_Bay","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Tijuana","PST","PST","PDT","PDT","-08:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Toronto","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Tortola","AST","AST","","","-04:00:00","+00:00:00","","","","+00:00:00" -"America/Vancouver","PST","PST","PDT","PDT","-08:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Whitehorse","PST","PST","PDT","PDT","-08:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Winnipeg","CST","CST","CDT","CDT","-06:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Yakutat","AKST","AKST","AKDT","AKDT","-09:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"America/Yellowknife","MST","MST","MDT","MDT","-07:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"Antarctica/Casey","AWST","AWST","","","+08:00:00","+00:00:00","","","","+00:00:00" -"Antarctica/Davis","DAVT","DAVT","","","+07:00:00","+00:00:00","","","","+00:00:00" -"Antarctica/DumontDUrville","DDUT","DDUT","","","+10:00:00","+00:00:00","","","","+00:00:00" -"Antarctica/Macquarie","MIST","MIST","","","+11:00:00","+00:00:00","","","","+00:00:00" -"Antarctica/Mawson","MAWT","MAWT","","","+05:00:00","+00:00:00","","","","+00:00:00" -"Antarctica/McMurdo","NZST","NZST","NZDT","NZDT","+13:00:00","+01:00:00","-1;0;9","+02:00:00","1;0;4","+03:00:00" -"Antarctica/Palmer","CLST","CLST","","","-03:00:00","+00:00:00","","","","+00:00:00" -"Antarctica/Rothera","ROTT","ROTT","","","-03:00:00","+00:00:00","","","","+00:00:00" -"Antarctica/Syowa","SYOT","SYOT","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Antarctica/Troll","UTC","UTC","CEST","CEST","+00:00:00","+02:00:00","-1;0;3","+01:00:00","-1;0;10","+03:00:00" -"Antarctica/Vostok","VOST","VOST","","","+06:00:00","+00:00:00","","","","+00:00:00" -"Arctic/Longyearbyen","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Asia/Aden","AST","AST","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Asia/Almaty","ALMT","ALMT","","","+06:00:00","+00:00:00","","","","+00:00:00" -"Asia/Amman","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;5;3","+00:00:00","-1;5;10","+01:00:00" -"Asia/Anadyr","ANAT","ANAT","","","+12:00:00","+00:00:00","","","","+00:00:00" -"Asia/Aqtau","AQTT","AQTT","","","+05:00:00","+00:00:00","","","","+00:00:00" -"Asia/Aqtobe","AQTT","AQTT","","","+05:00:00","+00:00:00","","","","+00:00:00" -"Asia/Ashgabat","TMT","TMT","","","+05:00:00","+00:00:00","","","","+00:00:00" -"Asia/Baghdad","AST","AST","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Asia/Bahrain","AST","AST","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Asia/Baku","AZT","AZT","AZST","AZST","+04:00:00","+01:00:00","-1;0;3","+04:00:00","-1;0;10","+05:00:00" -"Asia/Bangkok","ICT","ICT","","","+07:00:00","+00:00:00","","","","+00:00:00" -"Asia/Beirut","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;0;3","+00:00:00","-1;0;10","+00:00:00" -"Asia/Bishkek","KGT","KGT","","","+06:00:00","+00:00:00","","","","+00:00:00" -"Asia/Brunei","BNT","BNT","","","+08:00:00","+00:00:00","","","","+00:00:00" -"Asia/Chita","IRKT","IRKT","","","+08:00:00","+00:00:00","","","","+00:00:00" -"Asia/Choibalsan","CHOT","CHOT","CHOST","CHOST","+08:00:00","+01:00:00","-1;6;3","+02:00:00","-1;6;9","+00:00:00" -"Asia/Colombo","IST","IST","","","+05:30:00","+00:00:00","","","","+00:00:00" -"Asia/Damascus","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;5;3","+00:00:00","-1;5;10","+00:00:00" -"Asia/Dhaka","BDT","BDT","","","+06:00:00","+00:00:00","","","","+00:00:00" -"Asia/Dili","TLT","TLT","","","+09:00:00","+00:00:00","","","","+00:00:00" -"Asia/Dubai","GST","GST","","","+04:00:00","+00:00:00","","","","+00:00:00" -"Asia/Dushanbe","TJT","TJT","","","+05:00:00","+00:00:00","","","","+00:00:00" -"Asia/Gaza","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;6;3","+00:00:00","4;5;10","+00:00:00" -"Asia/Hebron","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;6;3","+00:00:00","4;5;10","+00:00:00" -"Asia/Ho_Chi_Minh","ICT","ICT","","","+07:00:00","+00:00:00","","","","+00:00:00" -"Asia/Hong_Kong","HKT","HKT","","","+08:00:00","+00:00:00","","","","+00:00:00" -"Asia/Hovd","HOVT","HOVT","HOVST","HOVST","+07:00:00","+01:00:00","-1;6;3","+02:00:00","-1;6;9","+00:00:00" -"Asia/Irkutsk","IRKT","IRKT","","","+08:00:00","+00:00:00","","","","+00:00:00" -"Asia/Jakarta","WIB","WIB","","","+07:00:00","+00:00:00","","","","+00:00:00" -"Asia/Jayapura","WIT","WIT","","","+09:00:00","+00:00:00","","","","+00:00:00" -"Asia/Jerusalem","IST","IST","IDT","IDT","+02:00:00","+01:00:00","-1;5;3","+02:00:00","-1;0;10","+02:00:00" -"Asia/Kabul","AFT","AFT","","","+04:30:00","+00:00:00","","","","+00:00:00" -"Asia/Kamchatka","PETT","PETT","","","+12:00:00","+00:00:00","","","","+00:00:00" -"Asia/Karachi","PKT","PKT","","","+05:00:00","+00:00:00","","","","+00:00:00" -"Asia/Kathmandu","NPT","NPT","","","+05:45:00","+00:00:00","","","","+00:00:00" -"Asia/Khandyga","YAKT","YAKT","","","+09:00:00","+00:00:00","","","","+00:00:00" -"Asia/Kolkata","IST","IST","","","+05:30:00","+00:00:00","","","","+00:00:00" -"Asia/Krasnoyarsk","KRAT","KRAT","","","+07:00:00","+00:00:00","","","","+00:00:00" -"Asia/Kuala_Lumpur","MYT","MYT","","","+08:00:00","+00:00:00","","","","+00:00:00" -"Asia/Kuching","MYT","MYT","","","+08:00:00","+00:00:00","","","","+00:00:00" -"Asia/Kuwait","AST","AST","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Asia/Macau","CST","CST","","","+08:00:00","+00:00:00","","","","+00:00:00" -"Asia/Magadan","MAGT","MAGT","","","+10:00:00","+00:00:00","","","","+00:00:00" -"Asia/Makassar","WITA","WITA","","","+08:00:00","+00:00:00","","","","+00:00:00" -"Asia/Manila","PHT","PHT","","","+08:00:00","+00:00:00","","","","+00:00:00" -"Asia/Muscat","GST","GST","","","+04:00:00","+00:00:00","","","","+00:00:00" -"Asia/Nicosia","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;0;3","+03:00:00","-1;0;10","+04:00:00" -"Asia/Novokuznetsk","KRAT","KRAT","","","+07:00:00","+00:00:00","","","","+00:00:00" -"Asia/Novosibirsk","NOVT","NOVT","","","+06:00:00","+00:00:00","","","","+00:00:00" -"Asia/Omsk","OMST","OMST","","","+06:00:00","+00:00:00","","","","+00:00:00" -"Asia/Oral","ORAT","ORAT","","","+05:00:00","+00:00:00","","","","+00:00:00" -"Asia/Phnom_Penh","ICT","ICT","","","+07:00:00","+00:00:00","","","","+00:00:00" -"Asia/Pontianak","WIB","WIB","","","+07:00:00","+00:00:00","","","","+00:00:00" -"Asia/Pyongyang","KST","KST","","","+09:00:00","+00:00:00","","","","+00:00:00" -"Asia/Qatar","AST","AST","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Asia/Qyzylorda","QYZT","QYZT","","","+06:00:00","+00:00:00","","","","+00:00:00" -"Asia/Rangoon","MMT","MMT","","","+06:30:00","+00:00:00","","","","+00:00:00" -"Asia/Riyadh","AST","AST","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Asia/Sakhalin","SAKT","SAKT","","","+10:00:00","+00:00:00","","","","+00:00:00" -"Asia/Samarkand","UZT","UZT","","","+05:00:00","+00:00:00","","","","+00:00:00" -"Asia/Seoul","KST","KST","","","+09:00:00","+00:00:00","","","","+00:00:00" -"Asia/Shanghai","CST","CST","","","+08:00:00","+00:00:00","","","","+00:00:00" -"Asia/Singapore","SGT","SGT","","","+08:00:00","+00:00:00","","","","+00:00:00" -"Asia/Srednekolymsk","SRET","SRET","","","+11:00:00","+00:00:00","","","","+00:00:00" -"Asia/Taipei","CST","CST","","","+08:00:00","+00:00:00","","","","+00:00:00" -"Asia/Tashkent","UZT","UZT","","","+05:00:00","+00:00:00","","","","+00:00:00" -"Asia/Tbilisi","GET","GET","","","+04:00:00","+00:00:00","","","","+00:00:00" -"Asia/Tehran","IRST","IRST","IRDT","IRDT","+03:30:00","+01:00:00","4;0;3","+00:00:00","4;2;9","+00:00:00" -"Asia/Thimphu","BTT","BTT","","","+06:00:00","+00:00:00","","","","+00:00:00" -"Asia/Tokyo","JST","JST","","","+09:00:00","+00:00:00","","","","+00:00:00" -"Asia/Ulaanbaatar","ULAT","ULAT","ULAST","ULAST","+08:00:00","+01:00:00","-1;6;3","+02:00:00","-1;6;9","+00:00:00" -"Asia/Urumqi","XJT","XJT","","","+06:00:00","+00:00:00","","","","+00:00:00" -"Asia/Ust-Nera","VLAT","VLAT","","","+10:00:00","+00:00:00","","","","+00:00:00" -"Asia/Vientiane","ICT","ICT","","","+07:00:00","+00:00:00","","","","+00:00:00" -"Asia/Vladivostok","VLAT","VLAT","","","+10:00:00","+00:00:00","","","","+00:00:00" -"Asia/Yakutsk","YAKT","YAKT","","","+09:00:00","+00:00:00","","","","+00:00:00" -"Asia/Yekaterinburg","YEKT","YEKT","","","+05:00:00","+00:00:00","","","","+00:00:00" -"Asia/Yerevan","AMT","AMT","","","+04:00:00","+00:00:00","","","","+00:00:00" -"Atlantic/Azores","AZOT","AZOT","AZOST","AZOST","-01:00:00","+01:00:00","-1;0;3","+00:00:00","-1;0;10","+01:00:00" -"Atlantic/Bermuda","AST","AST","ADT","ADT","-04:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"Atlantic/Canary","WET","WET","WEST","WEST","+00:00:00","+01:00:00","-1;0;3","+01:00:00","-1;0;10","+02:00:00" -"Atlantic/Cape_Verde","CVT","CVT","","","-01:00:00","+00:00:00","","","","+00:00:00" -"Atlantic/Faroe","WET","WET","WEST","WEST","+00:00:00","+01:00:00","-1;0;3","+01:00:00","-1;0;10","+02:00:00" -"Atlantic/Madeira","WET","WET","WEST","WEST","+00:00:00","+01:00:00","-1;0;3","+01:00:00","-1;0;10","+02:00:00" -"Atlantic/Reykjavik","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"Atlantic/South_Georgia","GST","GST","","","-02:00:00","+00:00:00","","","","+00:00:00" -"Atlantic/St_Helena","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"Atlantic/Stanley","FKST","FKST","","","-03:00:00","+00:00:00","","","","+00:00:00" -"Australia/Adelaide","ACST","ACST","ACDT","ACDT","+10:30:00","+01:00:00","1;0;10","+02:00:00","1;0;4","+03:00:00" -"Australia/Brisbane","AEST","AEST","","","+10:00:00","+00:00:00","","","","+00:00:00" -"Australia/Broken_Hill","ACST","ACST","ACDT","ACDT","+10:30:00","+01:00:00","1;0;10","+02:00:00","1;0;4","+03:00:00" -"Australia/Currie","AEST","AEST","AEDT","AEDT","+11:00:00","+01:00:00","1;0;10","+02:00:00","1;0;4","+03:00:00" -"Australia/Darwin","ACST","ACST","","","+09:30:00","+00:00:00","","","","+00:00:00" -"Australia/Eucla","ACWST","ACWST","","","+08:45:00","+00:00:00","","","","+00:00:00" -"Australia/Hobart","AEST","AEST","AEDT","AEDT","+11:00:00","+01:00:00","1;0;10","+02:00:00","1;0;4","+03:00:00" -"Australia/Lindeman","AEST","AEST","","","+10:00:00","+00:00:00","","","","+00:00:00" -"Australia/Lord_Howe","LHST","LHST","LHDT","LHDT","+11:00:00","+00:30:00","1;0;10","+02:00:00","1;0;4","+02:00:00" -"Australia/Melbourne","AEST","AEST","AEDT","AEDT","+11:00:00","+01:00:00","1;0;10","+02:00:00","1;0;4","+03:00:00" -"Australia/Perth","AWST","AWST","","","+08:00:00","+00:00:00","","","","+00:00:00" -"Australia/Sydney","AEST","AEST","AEDT","AEDT","+11:00:00","+01:00:00","1;0;10","+02:00:00","1;0;4","+03:00:00" -"Canada/Atlantic","AST","AST","ADT","ADT","-04:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"Canada/Central","CST","CST","CDT","CDT","-06:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"Canada/Eastern","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"Canada/Mountain","MST","MST","MDT","MDT","-07:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"Canada/Newfoundland","NST","NST","NDT","NDT","-03:30:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"Canada/Pacific","PST","PST","PDT","PDT","-08:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"Europe/Amsterdam","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Andorra","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Athens","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;0;3","+03:00:00","-1;0;10","+04:00:00" -"Europe/Belgrade","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Berlin","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Bratislava","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Brussels","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Bucharest","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;0;3","+03:00:00","-1;0;10","+04:00:00" -"Europe/Budapest","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Busingen","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Chisinau","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;0;3","+03:00:00","-1;0;10","+04:00:00" -"Europe/Copenhagen","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Dublin","GMT","GMT","IST","IST","+00:00:00","+01:00:00","-1;0;3","+01:00:00","-1;0;10","+02:00:00" -"Europe/Gibraltar","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Guernsey","GMT","GMT","BST","BST","+00:00:00","+01:00:00","-1;0;3","+01:00:00","-1;0;10","+02:00:00" -"Europe/Helsinki","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;0;3","+03:00:00","-1;0;10","+04:00:00" -"Europe/Isle_of_Man","GMT","GMT","BST","BST","+00:00:00","+01:00:00","-1;0;3","+01:00:00","-1;0;10","+02:00:00" -"Europe/Istanbul","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;0;3","+03:00:00","-1;0;10","+04:00:00" -"Europe/Jersey","GMT","GMT","BST","BST","+00:00:00","+01:00:00","-1;0;3","+01:00:00","-1;0;10","+02:00:00" -"Europe/Kaliningrad","EET","EET","","","+02:00:00","+00:00:00","","","","+00:00:00" -"Europe/Kiev","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;0;3","+03:00:00","-1;0;10","+04:00:00" -"Europe/Lisbon","WET","WET","WEST","WEST","+00:00:00","+01:00:00","-1;0;3","+01:00:00","-1;0;10","+02:00:00" -"Europe/Ljubljana","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/London","GMT","GMT","BST","BST","+00:00:00","+01:00:00","-1;0;3","+01:00:00","-1;0;10","+02:00:00" -"Europe/Luxembourg","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Madrid","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Malta","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Mariehamn","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;0;3","+03:00:00","-1;0;10","+04:00:00" -"Europe/Minsk","MSK","MSK","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Europe/Monaco","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Moscow","MSK","MSK","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Europe/Oslo","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Paris","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Podgorica","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Prague","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Riga","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;0;3","+03:00:00","-1;0;10","+04:00:00" -"Europe/Rome","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Samara","SAMT","SAMT","","","+04:00:00","+00:00:00","","","","+00:00:00" -"Europe/San_Marino","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Sarajevo","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Simferopol","MSK","MSK","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Europe/Skopje","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Sofia","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;0;3","+03:00:00","-1;0;10","+04:00:00" -"Europe/Stockholm","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Tallinn","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;0;3","+03:00:00","-1;0;10","+04:00:00" -"Europe/Tirane","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Uzhgorod","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;0;3","+03:00:00","-1;0;10","+04:00:00" -"Europe/Vaduz","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Vatican","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Vienna","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Vilnius","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;0;3","+03:00:00","-1;0;10","+04:00:00" -"Europe/Volgograd","MSK","MSK","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Europe/Warsaw","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Zagreb","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"Europe/Zaporozhye","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;0;3","+03:00:00","-1;0;10","+04:00:00" -"Europe/Zurich","CET","CET","CEST","CEST","+01:00:00","+01:00:00","-1;0;3","+02:00:00","-1;0;10","+03:00:00" -"GMT","GMT","GMT","","","+00:00:00","+00:00:00","","","","+00:00:00" -"Indian/Antananarivo","EAT","EAT","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Indian/Chagos","IOT","IOT","","","+06:00:00","+00:00:00","","","","+00:00:00" -"Indian/Christmas","CXT","CXT","","","+07:00:00","+00:00:00","","","","+00:00:00" -"Indian/Cocos","CCT","CCT","","","+06:30:00","+00:00:00","","","","+00:00:00" -"Indian/Comoro","EAT","EAT","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Indian/Kerguelen","TFT","TFT","","","+05:00:00","+00:00:00","","","","+00:00:00" -"Indian/Mahe","SCT","SCT","","","+04:00:00","+00:00:00","","","","+00:00:00" -"Indian/Maldives","MVT","MVT","","","+05:00:00","+00:00:00","","","","+00:00:00" -"Indian/Mauritius","MUT","MUT","","","+04:00:00","+00:00:00","","","","+00:00:00" -"Indian/Mayotte","EAT","EAT","","","+03:00:00","+00:00:00","","","","+00:00:00" -"Indian/Reunion","RET","RET","","","+04:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Apia","WSST","WSST","WSDT","WSDT","+14:00:00","+01:00:00","-1;0;9","+03:00:00","1;0;4","+04:00:00" -"Pacific/Auckland","NZST","NZST","NZDT","NZDT","+13:00:00","+01:00:00","-1;0;9","+02:00:00","1;0;4","+03:00:00" -"Pacific/Bougainville","BST","BST","","","+11:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Chatham","CHAST","CHAST","CHADT","CHADT","+13:45:00","+01:00:00","-1;0;9","+02:45:00","1;0;4","+03:45:00" -"Pacific/Chuuk","CHUT","CHUT","","","+10:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Easter","EASST","EASST","","","-05:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Efate","VUT","VUT","","","+11:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Enderbury","PHOT","PHOT","","","+13:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Fakaofo","TKT","TKT","","","+13:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Fiji","FJT","FJT","FJST","FJST","+13:00:00","+01:00:00","1;0;11","+02:00:00","3;0;1","+03:00:00" -"Pacific/Funafuti","TVT","TVT","","","+12:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Galapagos","GALT","GALT","","","-06:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Gambier","GAMT","GAMT","","","-09:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Guadalcanal","SBT","SBT","","","+11:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Guam","ChST","ChST","","","+10:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Honolulu","HST","HST","","","-10:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Johnston","HST","HST","","","-10:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Kiritimati","LINT","LINT","","","+14:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Kosrae","KOST","KOST","","","+11:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Kwajalein","MHT","MHT","","","+12:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Majuro","MHT","MHT","","","+12:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Marquesas","MART","MART","","","-09:30:00","+00:00:00","","","","+00:00:00" -"Pacific/Midway","SST","SST","","","-11:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Nauru","NRT","NRT","","","+12:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Niue","NUT","NUT","","","-11:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Norfolk","NFT","NFT","","","+11:30:00","+00:00:00","","","","+00:00:00" -"Pacific/Noumea","NCT","NCT","","","+11:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Pago_Pago","SST","SST","","","-11:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Palau","PWT","PWT","","","+09:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Pitcairn","PST","PST","","","-08:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Pohnpei","PONT","PONT","","","+11:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Port_Moresby","PGT","PGT","","","+10:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Rarotonga","CKT","CKT","","","-10:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Saipan","ChST","ChST","","","+10:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Tahiti","TAHT","TAHT","","","-10:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Tarawa","GILT","GILT","","","+12:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Tongatapu","TOT","TOT","","","+13:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Wake","WAKT","WAKT","","","+12:00:00","+00:00:00","","","","+00:00:00" -"Pacific/Wallis","WFT","WFT","","","+12:00:00","+00:00:00","","","","+00:00:00" -"US/Alaska","AKST","AKST","AKDT","AKDT","-09:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"US/Arizona","MST","MST","","","-07:00:00","+00:00:00","","","","+00:00:00" -"US/Central","CST","CST","CDT","CDT","-06:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"US/Eastern","EST","EST","EDT","EDT","-05:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"US/Hawaii","HST","HST","","","-10:00:00","+00:00:00","","","","+00:00:00" -"US/Mountain","MST","MST","MDT","MDT","-07:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"US/Pacific","PST","PST","PDT","PDT","-08:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00" -"UTC","UTC","UTC","","","+00:00:00","+00:00:00","","","","+00:00:00" diff --git a/docs/docs/api/elevation/api-reference.md b/docs/docs/api/elevation/api-reference.md index a9c9a0dc13..c79a7f13b9 100644 --- a/docs/docs/api/elevation/api-reference.md +++ b/docs/docs/api/elevation/api-reference.md @@ -21,7 +21,7 @@ By default, all height values are returned as integer values. This works fine fo | Height precision | Description | | :--------- | :----------- | -| `height_precision` | Specifies the precision (number of decimal places) of all returned height values. Values of `0`, `1`, or `2` are admissable. Defaults to 0 (integer precision). Any other value will result in integer precision (0 decimal places). +| `height_precision` | Specifies the precision (number of decimal places) of all returned height values. Values of `0`, `1`, or `2` are admissible. Defaults to 0 (integer precision). Any other value will result in integer precision (0 decimal places). ### Use a shape list for input locations diff --git a/docs/docs/api/expansion/api-reference.md b/docs/docs/api/expansion/api-reference.md index 12bb8684d3..3829a52f44 100644 --- a/docs/docs/api/expansion/api-reference.md +++ b/docs/docs/api/expansion/api-reference.md @@ -2,48 +2,151 @@ Routing algorithms find the best path by _expanding_ their search from start nodes/edges across the routing network until the destination is reached (unidirectional) or both search branches meet (bidirectional). This service could be subject to change in terms of API until we remove the BETA label. -The expansion service wraps the `route` and `isochrone` services and returns a GeoJSON with all network edges (way segments) the underlying routing algorithm visited during the expansion with relevant properties for each edge (e.g. `duration` & `distance`). A top-level `algorithm` propertry informs about the used algorithm: unidirectional & bidirectional A* (for `route`) and unidirectional Dijkstra (for `isochrone`). +The expansion service wraps the `route`, `isochrone` and `sources_to_targets` services and returns a GeoJSON with all network edges (way segments) the underlying routing algorithm visited during the expansion, with relevant properties for each edge (e.g. `duration` & `distance`). A top-level `algorithm` propertry informs about the used algorithm: unidirectional & bidirectional A* (for `route`), unidirectional Dijkstra (for `isochrone`) or bidirectional Dijkstra (for `sources_to_targets`). -**Note**, for even moderately long routes or isochrones the `/expansion` action can produce gigantic GeoJSON responses of 10s of MB. +**Note**, for even moderately long routes (or isochrones or few sources/targets) the `/expansion` action can produce gigantic GeoJSON responses of 100s of MB. ![A 11 km isochrone expansion result in Vienna, Austria](../images/expansion_dijkstra.png) ## Inputs of the Expansion service -Since this service wraps other services, the request format mostly follows the ones of the [route](../turn-by-turn/api-reference.md#inputs-of-a-route) and [isochrone](../isochrone/api-reference.md#inputs-of-the-isochrone-service). Additionally, it accepts the following parameters: +Since this service wraps other services, the request format mostly follows the ones of the [route](../turn-by-turn/api-reference.md#inputs-of-a-route), [isochrone](../isochrone/api-reference.md#inputs-of-the-isochrone-service) and [matrix](../matrix/api-reference.md#inputs-of-the-matrix-service). Additionally, it accepts the following parameters: | Parameter | Description | |:----------------------------------| :------------------------------------ | -| `action` (required) | The service whose expansion should be tracked. Currently one of `route` or `isochrone`. | +| `action` (required) | The service whose expansion should be tracked. Currently one of `route`, `isochrone` or `sources_to_targets`. | | `skip_opposites` (optional) | If set to `true` the output won't contain an edge's opposing edge. Opposing edges can be thought of as both directions of one road segment. Of the two, we discard the directional edge with higher cost and keep the one with less cost. Default false. | -| `expansion_properties` (optional) | A JSON array of strings of the GeoJSON property keys you'd like to have in the response. One or multiple of "durations", "distances", "costs", "edge_ids", "statuses". **Note**, that each additional property will increase the output size by minimum ~ 25%. By default an empty `properties` object is returned. | +| `dedupe` (optional) | If set to `true`, the output will contain each edge only once, significantly reducing the response size. The expansion will keep track of already visited edges and override their properties, ensuring that only the one with higher edge state is returned. Default `false`. | +| `expansion_properties` (optional) | A JSON array of strings of the GeoJSON property keys you'd like to have in the response. One or multiple of "duration", "distance", "cost", "edge_id", "pred_edge_id", "edge_status" or "expansion_type". **Note**, that each additional property will increase the output size by minimum ~ 10%. By default an empty `properties` object is returned. | The `expansion_properties` choices are as follows: | Property | Description | | :--------- | :------------------------------------ | -| `distances` | Returns the accumulated distance in meters for each edge in order of graph traversal. | -| `durations` | Returns the accumulated duration in seconds for each edge in order of graph traversal. | -| `costs` | Returns the accumulated cost for each edge in order of graph traversal. | -| `edge_ids` | Returns the internal edge IDs for each edge in order of graph traversal. Mostly interesting for debugging. | -| `statuses` | Returns the edge states for each edge in order of graph traversal. Mostly interesting for debugging. Can be one of "r" (reached), "s" (settled), "c" (connected). | +| `distance` | Returns the accumulated distance in meters for each edge in order of graph traversal. | +| `duration` | Returns the accumulated duration in seconds for each edge in order of graph traversal. | +| `cost` | Returns the accumulated cost for each edge in order of graph traversal. | +| `edge_id` | Returns the internal edge IDs for each edge in order of graph traversal. Mostly interesting for debugging. | +| `pred_edge_id` | Returns the internal edge IDs of the predecessor for each edge in order of graph traversal. Mostly interesting for debugging. | +| `edge_status` | Returns the edge states for each edge in order of graph traversal. Mostly interesting for debugging. Can be one of "r" (reached), "s" (settled), "c" (connected). | +| `expansion_type` | Returns the expansion direction from which the edge was encountered. 0 for forward, 1 for reverse. | An example request is: ```json -{"expansion_properties":["distances", "durations", "costs"],"contours":[{"time":1.0}],"locations":[{"lon":0.00026949361342338066,"lat":-0.00017966240895360996}],"costing":"auto","action":"isochrone"} +{ + "costing": "pedestrian", + "action": "isochrone", + "id": 1, + "locations": [ + { + "lon": 14.440689, + "lat": 50.087052 + } + ], + "contours": [ + { + "time": 1 + } + ], + "skip_opposites": true, + "expansion_properties": [ + "duration", + "edge_id", + "pred_edge_id", + "edge_status", + "cost", + "expansion_type" + ] +} ``` ## Outputs of the Expansion service -In the service response, the expanded way segments are returned as [GeoJSON](http://geojson.org/). The geometry is a single `MultiLineString` with each `LineString` representing one way segment (edge). Due to the verbosity of the GeoJSON format, single geometry features would produce prohibitively huge responses. However, that also means that the `properties` contain arrays of the tracked attributes, where the indices are correlating to the `coordinates` array, i.e. the 3rd element in a `properties` array will correspond to the 3rd `LineString` in the `MultiLineString` geometry. +In the service response, the expanded way segments are returned as [GeoJSON](http://geojson.org/) as plain `LineString`s. Due to the verbosity of the GeoJSON format, single geometry features would produce prohibitively huge responses. The output will only contain the `properties` which were specified in the `expansion_properties` request array. If the parameter was omitted in the request, the output will contain an empty `properties` object. An example response for `"action": "isochrone"` is: ```json -{"properties":{"algorithm":"unidirectional_dijkstra"},"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"MultiLineString","coordinates":[[[0.00027,-0.00017],[0.00027,0.0]],[[0.00027,-0.00017],[0.00027,-0.00035]],[[0.00027,-0.00035],[0.00027,-0.00017]],[[0.00027,0.0],[0.00027,-0.00017]],[[0.00027,-0.00017],[0.00053,-0.00017]],[[0.00027,-0.00017],[0.0,-0.00017]],[[0.0,-0.00017],[0.00027,-0.00017]],[[0.00053,-0.00017],[0.0008,-0.00017]],[[0.0008,-0.00017],[0.00053,-0.00017]],[[0.00053,-0.00017],[0.00027,-0.00017]],[[0.00053,-0.00017],[0.0008,0.0]]]},"properties":{"distances":[20,20,40,40,30,30,60,60,90,120,80],"durations":[0,0,29,29,1,1,30,2,31,33,5],"costs":[0,0,1,1,1,1,2,2,3,4,11]}}]} +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 14.441804, + 50.087371 + ], + [ + 14.440275, + 50.087137 + ] + ] + }, + "properties": { + "duration": 19, + "cost": 19, + "edge_status": "s", + "edge_id": 4049718357265, + "pred_edge_id": 70368744177663 + } + }, + { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 14.440275, + 50.087137 + ], + [ + 14.439981, + 50.087084 + ] + ] + }, + "properties": { + "duration": 34, + "cost": 34, + "edge_status": "s", + "edge_id": 4049617693969, + "pred_edge_id": 4049718357265 + } + }, + { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 14.439981, + 50.087084 + ], + [ + 14.438998, + 50.086788 + ] + ] + }, + "properties": { + "duration": 89, + "cost": 89, + "edge_status": "s", + "edge_id": 4444184259857, + "pred_edge_id": 4049617693969 + } + } + ], + "properties": { + "algorithm": "dijkstras" + } +} ``` ## Credits diff --git a/docs/docs/api/isochrone/api-reference.md b/docs/docs/api/isochrone/api-reference.md index 2f1a864149..cdedab9ad8 100644 --- a/docs/docs/api/isochrone/api-reference.md +++ b/docs/docs/api/isochrone/api-reference.md @@ -44,16 +44,18 @@ The isochrone service uses the `auto`, `bicycle`, `pedestrian`, and `multimodal` | `date_time` | The local date and time at the location.
  • `type`
    • 0 - Current departure time.
    • 1 - Specified departure time.
    • 2 - Specified arrival time. Note: This is not yet implemented for `multimodal`.
  • `value` - the date and time specified in ISO 8601 format (YYYY-MM-DDThh:mm) in the local time zone of departure or arrival. For example, "2016-07-03T08:06"
| | `id` | Name of the isochrone request. If `id` is specified, the name is returned with the response. | | `contours` | A JSON array of contour objects with the time in minutes or distance in kilometers and color to use for each isochrone contour. You can specify up to four contours (by default).
  • `time` - A floating point value specifying the time in minutes for the contour.
  • `distance` - A floating point value specifying the distance in kilometers for the contour.
  • `color` - The color for the output of the contour. Specify it as a [Hex value](http://www.w3schools.com/colors/colors_hexadecimal.asp), but without the `#`, such as `"color":"ff0000"` for red. If no color is specified, the isochrone service will assign a default color to the output.
You can only specify **one metric per contour**, i.e. `time` or `distance`. | -| `polygons` | A Boolean value to determine whether to return geojson polygons or linestrings as the contours. The default is `false`, which returns lines; when `true`, polygons are returned. Note: When `polygons` is `true`, any contour that forms a ring is returned as a polygon. | +| `polygons` | A Boolean value to determine whether to return geojson polygons or linestrings as the contours. The default is `false`, which returns lines; when `true`, polygons are returned. Note: When `polygons` is `true`, a feature's geometry type can be either `Polygon` or `MultiPolygon`, depending on the number of exterior rings formed for a given interval. | | `denoise` | A floating point value from `0` to `1` (default of `1`) which can be used to remove smaller contours. A value of `1` will only return the largest contour for a given time value. A value of `0.5` drops any contours that are less than half the area of the largest contour in the set of contours for that same time value. | | `generalize` | A floating point value in meters used as the tolerance for [Douglas-Peucker](https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm) generalization. Note: Generalization of contours can lead to self-intersections, as well as intersections of adjacent contours. | -| `show_locations` | A boolean indicating whether the input locations should be returned as MultiPoint features: one feature for the exact input coordinates and one feature for the coordinates of the network node it snapped to. Default false. +| `show_locations` | A boolean indicating whether the input locations should be returned as MultiPoint features: one feature for the exact input coordinates and one feature for the coordinates of the network node it snapped to. Default false. | +| `reverse` | A boolean which can be set to do inverse expansion of the isochrone. The reverse isochrone will show from which area the given location can be reached within the given time. + ## Outputs of the Isochrone service -In the service response, the isochrone contours are returned as [GeoJSON](http://geojson.org/), which can be integrated into mapping applications. +In the service response, the isochrone contours are returned as [GeoJSON](http://geojson.org/), which can be integrated into mapping applications. Alternatively, the grid data that underlies these contours can be returned as a [GeoTIFF](https://www.ogc.org/standard/geotiff/). -The contours are calculated using rasters and are returned as either polygon or line features, depending on your input setting for the `polygons` parameter. If an isochrone request has been named using the optional `&id=` input, then the `id` is returned as a name property for the feature collection within the GeoJSON response. A `metric` attribute lets you know whether it's a `distance` or `time` contour. A warnings array may also be included. This array may contain warning objects informing about deprecated request parameters, clamped values etc. | +The isochrone service returns contours as GeoJSON line or polygon features for the requested intervals (depending on the value of the `polygons` request parameter). These contours are calculated using a two dimensional grid. If the `format` request parameter is set to `geotiff`, the underlying grid data is returned directly instead of the contours derived from it. It will return one band for each requested metric (i.e. one for `time` and one for `distance`). If an isochrone request has been named using the optional `&id=` input, then the `id` is returned as a name property for the feature collection within the GeoJSON response. A `metric` attribute lets you know whether it's a `distance` or `time` contour. A warnings array may also be included. This array may contain warning objects informing about deprecated request parameters, clamped values etc. | See the [HTTP return codes](../turn-by-turn/api-reference.md#http-status-codes-and-conditions) for more on messages you might receive from the service. @@ -63,21 +65,7 @@ Most JavaScript-based GeoJSON renderers, including [Leaflet](http://leafletjs.co When making a map, drawing the isochrone contours as lines is more straightforward than polygons, and, therefore, currently is the default and recommended method. When deciding between the output as lines and polygons, consider your use case and the additional styling considerations involved with polygons. For example, fills should be rendered as semi-transparent over the other map layers so they are visible, although you may have more flexibility when using a vector-based map. In addition, polygons from multiple contour levels do not have overlapping areas cut out or removed. In other words, the outer contours include the areas of any inner contours, causing the colors and transparencies to blend when multiple contour polygons are drawn at the same time. -The Valhalla team has plans to improving the polygon isochrone output and rendering capabilities, including by demoting some rings to be inners of other rings and removing potential self-intersections in polygon geometries. - -## Future work on the isochrone service - -The Isochrone service is in active development. To report software issues or suggest enhancements, open an issue in the [Valhalla GitHub repository](https://github.com/valhalla/valhalla/issues). - -Several other options are being considered as future service enhancements. These include: - -* ~~Using distance rather than time for each unit.~~ -* Generating outer contours or contours with interior holes for regions that cannot be accessed within the specified time. -* ~~Options to control the minimum size of interior holes.~~ -* Removing self intersections from polygonal contours. -* ~~Allowing multiple locations to compute the region reachable from any of the locations within a specified time.~~ -* ~~Generating contours with reverse access logic to see the region that can reach a specific location within the specified time.~~ -* Returning raster data for potential animation using OpenGL shaders. This also has analysis use for being able to query thousands of locations to determine the time to each location, including improvements with one-to-many requests to the Valhalla Time-Distance Matrix service. +(TODO: write something about rendering the GeoTIFF output.) ## Data credits diff --git a/docs/docs/api/locate/api-reference.md b/docs/docs/api/locate/api-reference.md index 08dff5f03f..17bfe07717 100644 --- a/docs/docs/api/locate/api-reference.md +++ b/docs/docs/api/locate/api-reference.md @@ -18,7 +18,7 @@ This request provides detailed information about specific streets and intersecti There is an option to name your request. You can do this by adding and `id` key to your request. The `id` is returned with the response so a user could match to the corresponding request. -Because the locate service is designed to work in tandem with the route service API, the inputs for the two APIs are identical. For detailed options regarding specifiying locations, costing models, costing options, directions options please see the relevant sections in the [routing API docs](../turn-by-turn/api-reference.md#inputs-of-a-route) +Because the locate service is designed to work in tandem with the route service API, the inputs for the two APIs are identical. For detailed options regarding specifying locations, costing models, costing options, directions options please see the relevant sections in the [routing API docs](../turn-by-turn/api-reference.md#inputs-of-a-route) ### Other request options @@ -31,7 +31,7 @@ Because the locate service is designed to work in tandem with the route service If a request has been named using the optional `id` key, then this `id` key and value will be echoed in the JSON response object. -The locate results are returned as a JSON array, with one JSON object per input location in the order specified. In `verbose` mode details about the streets and intersections includding mode of travel access, names, way ids, shape, side of street as well as the closest point to the input along these features will be returned. If `verbose` was not enabled only the closest point, way id and side of street will be returned. A warnings array may also be included. This array may contain warning objects informing about deprecated request parameters, clamped values etc. | +The locate results are returned as a JSON array, with one JSON object per input location in the order specified. In `verbose` mode details about the streets and intersections including mode of travel access, names, way ids, shape, side of street as well as the closest point to the input along these features will be returned. If `verbose` was not enabled only the closest point, way id and side of street will be returned. A warnings array may also be included. This array may contain warning objects informing about deprecated request parameters, clamped values etc. | Here are some sample results with `verbose` set to `false`: diff --git a/docs/docs/api/map-matching/api-reference.md b/docs/docs/api/map-matching/api-reference.md index 42536f5952..e3dbf34037 100644 --- a/docs/docs/api/map-matching/api-reference.md +++ b/docs/docs/api/map-matching/api-reference.md @@ -14,6 +14,8 @@ The `trace_route` action takes the costing mode and a list of latitude,longitude By default a single trip leg is returned in a trace_route response. You can split the route response into multiple legs by setting `"type":"break"` on any of the input shape objects in the `shape` parameter of your query. The first and last locations should always have type break. Note that setting breaks is not supported for encoded_polyline input, and is only supported for `map_snap` mode of the trace_route endpoint. +If the path contains one or more discontinuities (i.e. no path can be found between two locations), it is split into multiple paths. Any remaining paths from the first discontinuity onwards are stored as route alternates on the response. + ## Trace attributes action The `trace_attributes` action takes the costing mode and a GPS trace or latitude,longitude positions and returns detailed attribution along the portion of the route. This includes details for each section of road along the path, as well as any intersections along the path. Some of the use cases for `trace_attributes` include getting: @@ -39,9 +41,7 @@ Note that the attributes that are returned are Valhalla routing attributes, not ### Costing models and other options -Valhalla Map Matching uses the `auto`, `auto_shorter`, `bicycle`, `bus`, and `pedestrian` costing models available in the Valhalla route service. Refer to the [route costing models](../turn-by-turn/api-reference.md#costing-models) and [costing options](../turn-by-turn/api-reference.md#costing-options) documentation for more on how to specify this input. - -Costing for `multimodal` is not supported for map matching because it would be difficult to get favorable GPS traces. +Valhalla Map Matching can use any costing model available in the Valhalla route service except for `multimodal` (it would be difficult to get a GPS trace and detect certain mode changes). Refer to the [route costing models](../turn-by-turn/api-reference.md#costing-models) and [costing options](../turn-by-turn/api-reference.md#costing-options) documentation for more on how to specify this input. You can also set `directions_options` to specify output units, language, and whether or not to return directions in a narrative form. Refer to the [route options](../turn-by-turn/api-reference.md#directions-options) documentation for examples. @@ -72,7 +72,7 @@ These are the available filter keys. Review their [descriptions](#outputs-of-tra ``` // Edge filter Keys edge.names -edge.length +edge.length # can also set source/target_percent_along edge.speed edge.road_class edge.begin_heading @@ -115,6 +115,7 @@ edge.density edge.speed_limit edge.truck_speed edge.truck_route +edge.country_crossing // Node filter keys node.intersecting_edge.begin_heading @@ -175,7 +176,9 @@ Each `edge` may include: | Edge item | Description | | :--------- | :---------- | | `names` | List of names. | -| `length` | Edge length in the units specified. The default is kilometers. | +| `source_percent_along` | The start of an edge's match as percentage of its length in (0, 1) range. If an edge was fully matched, we omit this value. +| `target_percent_along` | The end of an edge's match as percentage of its length in (0, 1) range. If an edge was fully matched, we omit this value. +| `length` | The **matched** edge length in the units specified (default is kilometers). If `source_percent_along` and/or `target_percent_along` are present, this represents the partially matched edge length, otherwise the full edge length. | | `speed` | Edge speed in the units specified. The default is kilometers per hour. | | `road_class` | Road class values:
  • `motorway`
  • `trunk`
  • `primary`
  • `secondary`
  • `tertiary`
  • `unclassified`
  • `residential`
  • `service_other`
| | `begin_heading` | The direction at the beginning of an edge. The units are degrees from north in a clockwise direction. | @@ -183,7 +186,7 @@ Each `edge` may include: | `begin_shape_index` | Index into the list of shape points for the start of the edge. | | `end_shape_index` | Index into the list of shape points for the end of the edge. | | `traversability` | Traversability values, if available:
  • `forward`
  • `backward`
  • `both`
| -| `use` | Use values:
  • `tram`
  • `road`
  • `ramp`
  • `turn_channel`
  • `track`
  • `driveway`
  • `alley`
  • `parking_aisle`
  • `emergency_access`
  • `drive_through`
  • `culdesac`
  • `cycleway`
  • `mountain_bike`
  • `sidewalk`
  • `footway`
  • `steps`
  • `other`
  • `rail-ferry`
  • `ferry`
  • `rail`
  • `bus`
  • `rail_connection`
  • `bus_connnection`
  • `transit_connection`
| +| `use` | Use values:
  • `tram`
  • `road`
  • `ramp`
  • `turn_channel`
  • `track`
  • `driveway`
  • `alley`
  • `parking_aisle`
  • `emergency_access`
  • `drive_through`
  • `culdesac`
  • `cycleway`
  • `mountain_bike`
  • `sidewalk`
  • `footway`
  • `steps`
  • `other`
  • `rail-ferry`
  • `ferry`
  • `rail`
  • `bus`
  • `egress_connection`
  • `platform_connection`
  • `transit_connection`
| | `toll` | True if the edge has any toll. | | `unpaved` | True if the edge is unpaved or rough pavement. | | `tunnel` | True if the edge is a tunnel. | @@ -216,6 +219,9 @@ Each `edge` may include: | `truck_speed` | Edge truck speed in the units specified. The default is kilometers per hour. | | `truck_route` | True if edge is part of a truck network/route. | | `end_node` | The node at the end of this edge. See the list of [end node items](#end-node-items) for details. | +| `landmarks` | List of landmarks along the edge. They are used as direction support in navigation. | | +| `country_crossing` | True if the edge is a country crossing. | + #### Sign items @@ -253,7 +259,7 @@ Each `intersecting_edge` may include: | `driveability` | Driveability values, if available:
  • `forward`
  • `backward`
  • `both`
| | `cyclability` | Cyclability values, if available:
  • `forward`
  • `backward`
  • `both`
| | `walkability` | Walkability values, if available:
  • `forward`
  • `backward`
  • `both`
| -| `use` | Use values:
  • `tram`
  • `road`
  • `ramp`
  • `turn_channel`
  • `track`
  • `driveway`
  • `alley`
  • `parking_aisle`
  • `emergency_access`
  • `drive_through`
  • `culdesac`
  • `cycleway`
  • `mountain_bike`
  • `sidewalk`
  • `footway`
  • `steps`
  • `other`
  • `rail-ferry`
  • `ferry`
  • `rail`
  • `bus`
  • `rail_connection`
  • `bus_connnection`
  • `transit_connection`
| +| `use` | Use values:
  • `tram`
  • `road`
  • `ramp`
  • `turn_channel`
  • `track`
  • `driveway`
  • `alley`
  • `parking_aisle`
  • `emergency_access`
  • `drive_through`
  • `culdesac`
  • `cycleway`
  • `mountain_bike`
  • `sidewalk`
  • `footway`
  • `steps`
  • `other`
  • `rail-ferry`
  • `ferry`
  • `rail`
  • `bus`
  • `egress_connection`
  • `platform_connection`
  • `transit_connection`
| | `road_class` | Road class values:
  • `motorway`
  • `trunk`
  • `primary`
  • `secondary`
  • `tertiary`
  • `unclassified`
  • `residential`
  • `service_other`
| #### Admin items diff --git a/docs/docs/api/matrix/api-reference.md b/docs/docs/api/matrix/api-reference.md index e59a46c631..18ca3fcb4f 100644 --- a/docs/docs/api/matrix/api-reference.md +++ b/docs/docs/api/matrix/api-reference.md @@ -16,19 +16,19 @@ For example, while at your office, you want to know the times and distances to w `one-to-many using /sources_to_targets?` ```json -{"sources":[{"lat":40.744014,"lon":-73.990508}],"targets":[{"lat":40.744014,"lon":-73.990508},{"lat":40.739735,"lon":-73.979713},{"lat":40.752522,"lon":-73.985015},{"lat":40.750117,"lon":-73.983704},{"lat":40.750552,"lon":-73.993519}],"costing":"pedestrian"}&id=ManyToMany_NYC_work_dinner +{"sources":[{"lat":40.744014,"lon":-73.990508}],"targets":[{"lat":40.744014,"lon":-73.990508},{"lat":40.739735,"lon":-73.979713},{"lat":40.752522,"lon":-73.985015},{"lat":40.750117,"lon":-73.983704},{"lat":40.750552,"lon":-73.993519}],"costing":"pedestrian"} ``` `many-to-one using /sources_to_targets?` ```json -{"sources":[{"lat":40.744014,"lon":-73.990508},{"lat":40.739735,"lon":-73.979713},{"lat":40.752522,"lon":-73.985015},{"lat":40.750117,"lon":-73.983704},{"lat":40.750552,"lon":-73.993519}],"targets":[{"lat":40.750552,"lon":-73.993519}],"costing":"pedestrian"}&id=ManyToMany_NYC_work_dinner +{"sources":[{"lat":40.744014,"lon":-73.990508},{"lat":40.739735,"lon":-73.979713},{"lat":40.752522,"lon":-73.985015},{"lat":40.750117,"lon":-73.983704},{"lat":40.750552,"lon":-73.993519}],"targets":[{"lat":40.750552,"lon":-73.993519}],"costing":"pedestrian"} ``` `many-to-many using /sources_to_targets?` ```json -{"sources":[{"lat":40.744014,"lon":-73.990508},{"lat":40.739735,"lon":-73.979713},{"lat":40.752522,"lon":-73.985015},{"lat":40.750117,"lon":-73.983704},{"lat":40.750552,"lon":-73.993519}],"targets":[{"lat":40.744014,"lon":-73.990508},{"lat":40.739735,"lon":-73.979713},{"lat":40.752522,"lon":-73.985015},{"lat":40.750117,"lon":-73.983704},{"lat":40.750552,"lon":-73.993519}],"costing":"pedestrian"}&id=ManyToMany_NYC_work_dinner +{"sources":[{"lat":40.744014,"lon":-73.990508},{"lat":40.739735,"lon":-73.979713},{"lat":40.752522,"lon":-73.985015},{"lat":40.750117,"lon":-73.983704},{"lat":40.750552,"lon":-73.993519}],"targets":[{"lat":40.744014,"lon":-73.990508},{"lat":40.739735,"lon":-73.979713},{"lat":40.752522,"lon":-73.985015},{"lat":40.750117,"lon":-73.983704},{"lat":40.750552,"lon":-73.993519}],"costing":"pedestrian"} ``` ### Source and target parameters @@ -60,6 +60,8 @@ The Time-Distance Matrix service uses the `auto`, `bicycle`, `pedestrian` and `b | `id` | Name your matrix request. If `id` is specified, the naming will be sent thru to the response. | | `matrix_locations` | For one-to-many or many-to-one requests this specifies the minimum number of locations that satisfy the request. However, when specified, this option allows a partial result to be returned. This is basically equivalent to "find the closest/best `matrix_locations` locations out of the full location set". | | `date_time` | This is the local date and time at the location.
  • `type`
    • 0 - Current departure time.
    • 1 - Specified departure time
    • 2 - Specified arrival time.
  • `value` - the date and time is specified in ISO 8601 format (YYYY-MM-DDThh:mm) in the local time zone of departure or arrival. For example "2016-07-03T08:06"

| +| `verbose` | If `true` it will output a flat list of objects for `distances` & `durations` explicitly specifying the source & target indices. If `false` will return more compact, nested row-major `distances` & `durations` arrays and not echo `sources` and `targets`. Default `true`. | +| `shape_format` | Specifies the optional format for the path shape of each connection. One of `polyline6`, `polyline5`, `geojson` or `no_shape` (default). | ### Time-dependent matrices diff --git a/docs/docs/api/optimized/api-reference.md b/docs/docs/api/optimized/api-reference.md index 2ce6e112fd..fbec3c7fc9 100644 --- a/docs/docs/api/optimized/api-reference.md +++ b/docs/docs/api/optimized/api-reference.md @@ -35,11 +35,11 @@ A location must include a latitude and longitude in decimal degrees. The coordin | `lat` | Latitude of the location in degrees. | | `lon` | Longitude of the location in degrees. | -Refer to the [route location documentation](/turn-by-turn/api-reference.md#locations) for more information on specifying locations. +Refer to the [route location documentation](../turn-by-turn/api-reference.md#locations) for more information on specifying locations. ### Costing parameters -The Optimized Route service uses the `auto`, `bicycle` and `pedestrian` costing models available in the Valhalla route service. The **multimodal costing is not supported** for the Optimized Route service at this time. Refer to the [route costing models](/turn-by-turn/api-reference.md#costing-models) and [costing options](/turn-by-turn/api-reference.md#costing-options) documentation for more on how to specify this input. +The Optimized Route service uses the `auto`, `bicycle` and `pedestrian` costing models available in the Valhalla route service. The **multimodal costing is not supported** for the Optimized Route service at this time. Refer to the [route costing models](../turn-by-turn/api-reference.md#costing-models) and [costing options](../turn-by-turn/api-reference.md#costing-options) documentation for more on how to specify this input. ### Other request options @@ -70,4 +70,4 @@ This is an example which should return: `400::Location at index 3 is unreachable {"locations":[{"lat":40.306600,"lon":-76.900022},{"lat":40.293246,"lon":-76.936230},{"lat":40.448678,"lon":-76.932885},{"lat":40.419753,"lon":-76.999632},{"lat":40.211050,"lon":-76.777071},{"lat":40.306600,"lon":-76.900022}],"costing":"auto"} ``` -See the [HTTP return codes](/turn-by-turn/api-reference.md#http-status-codes-and-conditions) for more on messages you might receive from the service. +See the [HTTP return codes](../turn-by-turn/api-reference.md#http-status-codes-and-conditions) for more on messages you might receive from the service. diff --git a/docs/docs/api/turn-by-turn/add-routing-to-a-map.md b/docs/docs/api/turn-by-turn/add-routing-to-a-map.md deleted file mode 100644 index e226e61c4b..0000000000 --- a/docs/docs/api/turn-by-turn/add-routing-to-a-map.md +++ /dev/null @@ -1,8 +0,0 @@ -# Add Turn-by-Turn routing to a map - -The [result of a routing request](api-reference/#outputs-of-a-route) is a special format that needs some processing to show in a JavaScript-based web map application. - -For [Leaflet](http://leafletjs.com/), the [Leaflet Routing Machine](https://www.liedman.net/leaflet-routing-machine/) via the [lrm-valhalla](https://github.com/valhalla/lrm-valhalla) plugin helps. -Please note that [mapzen](https://github.com/mapzen/mapzen.js) urls are no longer available, so one must set the `serviceUrl` within the `options` of the `L.Routing.mapzen` router to point to a valid Valhalla route service! - -You can review the [documentation](api-reference.md) to learn more about routing with Turn-by-Turn. diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index 36a0fe9eee..c00e646f67 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -41,10 +41,11 @@ To build a route, you need to specify two `break` locations. In addition, you ca | `preferred_side` | If the location is not offset from the road centerline or is closest to an intersection this option has no effect. Otherwise the determined side of street is used to determine whether or not the location should be visited from the `same`, `opposite` or `either` side of the road with respect to the side of the road the given locale drives on. In Germany (driving on the right side of the road), passing a value of `same` will only allow you to leave from or arrive at a location such that the location will be on your right. In Australia (driving on the left side of the road), passing a value of `same` will force the location to be on your left. A value of `opposite` will enforce arriving/departing from a location on the opposite side of the road from that which you would be driving on while a value of `either` will make no attempt limit the side of street that is available for the route. | | `display_lat` | Latitude of the map location in degrees. If provided the `lat` and `lon` parameters will be treated as the routing location and the `display_lat` and `display_lon` will be used to determine the side of street. Both `display_lat` and `display_lon` must be provided and valid to achieve the desired effect. | | `display_lon` | Longitude of the map location in degrees. If provided the `lat` and `lon` parameters will be treated as the routing location and the `display_lat` and `display_lon` will be used to determine the side of street. Both `display_lat` and `display_lon` must be provided and valid to achieve the desired effect. | -| `search_cutoff` | The cutoff at which we will assume the input is too far away from civilisation to be worth correlating to the nearest graph elements | -| `node_snap_tolerance` | During edge correlation this is the tolerance used to determine whether or not to snap to the intersection rather than along the street, if the snap location is within this distance from the intersection the intersection is used instead. The default is 5 meters | -| `street_side_tolerance` | If your input coordinate is less than this tolerance away from the edge centerline then we set your side of street to none otherwise your side of street will be left or right depending on direction of travel | -| `street_side_max_distance` | The max distance in meters that the input coordinates or display ll can be from the edge centerline for them to be used for determining the side of street. Beyond this distance the side of street is set to none | +| `search_cutoff` | The cutoff at which we will assume the input is too far away from civilisation to be worth correlating to the nearest graph elements. The default is 35 km. | +| `node_snap_tolerance` | During edge correlation this is the tolerance used to determine whether or not to snap to the intersection rather than along the street, if the snap location is within this distance from the intersection the intersection is used instead. The default is 5 meters. | +| `street_side_tolerance` | If your input coordinate is less than this tolerance away from the edge centerline then we set your side of street to none otherwise your side of street will be left or right depending on direction of travel. The default is 5 meters. | +| `street_side_max_distance` | The max distance in meters that the input coordinates or display ll can be from the edge centerline for them to be used for determining the side of street. Beyond this distance the side of street is set to none. The default is 1000 meters. | +| `street_side_cutoff` | Disables the `preferred_side` when set to `same` or `opposite` if the edge has a road class less than that provided by `street_side_cutoff`. The road class must be one of the following strings: motorway, trunk, primary, secondary, tertiary, unclassified, residential, service_other. The default value is `service_other` so that `preferred_side` will not be disabled for any edges. | | `search_filter` | A set of optional filters to exclude candidate edges based on their attribution. The following exclusion filters are supported:
  • `exclude_tunnel` (boolean, defaults to `false`): whether to exclude roads marked as tunnels
  • `exclude_bridge` (boolean, defaults to `false`): whether to exclude roads marked as bridges
  • `exclude_ramp` (boolean, defaults to `false`): whether to exclude link roads marked as ramps, note that some turn channels are also marked as ramps
  • `exclude_closures` (boolean, defaults to `true`): whether to exclude roads considered closed due to live traffic closure. **Note:** This option cannot be set if `costing_options..ignore_closures` is also specified. An error is returned if both options are specified. **Note 2:** Ignoring closures at destination and source locations does NOT work for date_time type `0/1` & `2` respectively
  • `min_road_class` (string, defaults to `"service_other"`): lowest road class allowed
  • `max_road_class` (string, defaults to `"motorway"`): highest road class allowed
Road classes from highest to lowest are: motorway, trunk, primary, secondary, tertiary, unclassified, residential, service_other. Optionally, you can include the following location information without impacting the routing. This information is carried through the request and returned as a convenience. @@ -92,6 +93,15 @@ A special costing option is `shortest`, which, when `true`, will solely use dist Another special case is `disable_hierarchy_pruning` costing option. As the name indicates, `disable_hierarchy_pruning = true` will disable hierarchies in routing algorithms, which allows us to find the actual optimal route even in edge cases. For example, together with `shortest = true` they can find the actual shortest route. When `disable_hierarchy_pruning` is `true` and arc distances between source and target are not above the max limit, the actual optimal route will be calculated at the expense of performance. Note that if arc distances between locations exceed the max limit, `disable_hierarchy_pruning` is `true` will not be applied. This costing option is available for all motorized costing models, i.e `auto`, `motorcycle`, `motor_scooter`, `bus`, `truck` & `taxi`. For `bicycle` and `pedestrian` hierarchies are always disabled by default. +Additionally to the main costing option, the `recostings` option can be used to calculate the travelling time of the found route based on different costing options. By e.g. adding +```json +"recostings":[ + {"costing":"auto","fixed_speed":20,"name":"auto_20"}, + {"costing":"auto","fixed_speed":50,"name":"auto_50"} +] +``` +to the route request, the values `time_auto_20` and `time_auto_50` will be added to summaries to show how much time the route would cost with these given costing options. Passing a recosting which make the route impossible to follow (e.g. the main rout is by car over a motorway and recosting with pedestrian costing) leads to a `none` result of this recosting. + ##### Automobile and bus costing options These options are available for `auto`, `bus`, and `truck` costing methods. @@ -102,6 +112,7 @@ These options are available for `auto`, `bus`, and `truck` costing methods. | `gate_cost` | A cost applied when a [gate](http://wiki.openstreetmap.org/wiki/Tag:barrier%3Dgate) with undefined or private access is encountered. This cost is added to the estimated time / elapsed time. The default gate cost is 30 seconds. | | `gate_penalty` | A penalty applied when a [gate](https://wiki.openstreetmap.org/wiki/Tag:barrier%3Dgate) with no access information is on the road. The default gate penalty is 300 seconds. | | `private_access_penalty` | A penalty applied when a [gate](https://wiki.openstreetmap.org/wiki/Tag:barrier%3Dgate) or [bollard](https://wiki.openstreetmap.org/wiki/Tag:barrier%3Dbollard) with `access=private` is encountered. The default private access penalty is 450 seconds. | +| `destination_only_penalty` | A penalty applied when entering an road which is only allowed to enter if necessary to reach the [destination](https://wiki.openstreetmap.org/wiki/Tag:vehicle%3Ddestination). | | `toll_booth_cost` | A cost applied when a [toll booth](http://wiki.openstreetmap.org/wiki/Tag:barrier%3Dtoll_booth) is encountered. This cost is added to the estimated and elapsed times. The default cost is 15 seconds. | | `toll_booth_penalty` | A penalty applied to the cost when a [toll booth](http://wiki.openstreetmap.org/wiki/Tag:barrier%3Dtoll_booth) is encountered. This penalty can be used to create paths that avoid toll roads. The default toll booth penalty is 0. | | `ferry_cost` | A cost applied when entering a ferry. This cost is added to the estimated and elapsed times. The default cost is 300 seconds (5 minutes). | @@ -115,11 +126,18 @@ These options are available for `auto`, `bus`, and `truck` costing methods. | `country_crossing_cost` | A cost applied when encountering an international border. This cost is added to the estimated and elapsed times. The default cost is 600 seconds. | | `country_crossing_penalty` | A penalty applied for a country crossing. This penalty can be used to create paths that avoid spanning country boundaries. The default penalty is 0. | | `shortest` | Changes the metric to quasi-shortest, i.e. purely distance-based costing. Note, this will disable all other costings & penalties. Also note, `shortest` will not disable hierarchy pruning, leading to potentially sub-optimal routes for some costing models. The default is `false`. | +| `use_distance` | A factor that allows controlling the contribution of distance and time to the route costs. The value is in range between 0 and 1, where 0 only takes time into account (default) and 1 only distance. A factor of 0.5 will weight them roughly equally. **Note:** this costing is currently only available for auto costing. | | `disable_hierarchy_pruning` | Disable hierarchies to calculate the actual optimal route. The default is `false`. **Note:** This could be quite a performance drainer so there is a upper limit of distance. If the upper limit is exceeded, this option will always be `false`. | -| `top_speed` | Top speed the vehicle can go. Also used to avoid roads with higher speeds than this value. `top_speed` must be between 10 and 252 KPH. The default value is 140 KPH. | +| `top_speed` | Top speed the vehicle can go. Also used to avoid roads with higher speeds than this value. `top_speed` must be between 10 and 252 KPH. The default value is 120 KPH for `truck` and 140 KPH for `auto` and `bus`. | | `fixed_speed` | Fixed speed the vehicle can go. Used to override the calculated speed. Can be useful if speed of vehicle is known. `fixed_speed` must be between 1 and 252 KPH. The default value is 0 KPH which disables fixed speed and falls back to the standard calculated speed based on the road attribution. | | `ignore_closures` | If set to `true`, ignores all closures, marked due to live traffic closures, during routing. **Note:** This option cannot be set if `location.search_filter.exclude_closures` is also specified in the request and will return an error if it is | | `closure_factor` | A factor that penalizes the cost when traversing a closed edge (eg: if `search_filter.exclude_closures` is `false` for origin and/or destination location and the route starts/ends on closed edges). Its value can range from `1.0` - don't penalize closed edges, to `10.0` - apply high cost penalty to closed edges. Default value is `9.0`. **Note:** This factor is applicable only for motorized modes of transport, i.e `auto`, `motorcycle`, `motor_scooter`, `bus`, `truck` & `taxi`. | +| `ignore_restrictions` | If set to `true`, ignores any restrictions (e.g. turn/dimensional/conditional restrictions). Especially useful for matching GPS traces to the road network regardless of restrictions. Default is `false`. | +| `ignore_oneways` | If set to `true`, ignores one-way restrictions. Especially useful for matching GPS traces to the road network ignoring uni-directional traffic rules. Not included in `ignore_restrictions` option. Default is `false`. | +| `ignore_non_vehicular_restrictions` | Similar to `ignore_restrictions`, but will respect restrictions that impact vehicle safety, such as weight and size restrictions. | +| `ignore_access` | Will ignore mode-specific access tags. Especially useful for matching GPS traces to the road network regardless of restrictions. Default is `false`. | +| `ignore_closures` | Will ignore traffic closures. Default is `false`. | +| `speed_types` | Will determine which speed sources are used, if available. A list of strings with the following possible values:
  • freeflow
  • constrained
  • predicted
  • current
Default is all sources (again, only if available). | ###### Other costing options The following options are available for `auto`, `bus`, `taxi`, and `truck` costing methods. @@ -143,16 +161,20 @@ The following options are available for `truck` costing. | `axle_load` | The axle load of the truck (in metric tons). Default 9.07. | | `axle_count` | The axle count of the truck. Default 5. | | `hazmat` | A value indicating if the truck is carrying hazardous materials. Default false. | +| `hgv_no_access_penalty` | A penalty applied to roads with no HGV/truck access. If set to a value less than 43200 seconds, HGV will be allowed on these roads and the penalty applies. Default 43200, i.e. trucks are not allowed. | +| `low_class_penalty` | A penalty (in seconds) which is applied when going to residential or service roads. Default is 30 seconds. | +| `use_truck_route` | This value is a range of values from 0 to 1, where 0 indicates a light preference for streets marked as truck routes, and 1 indicates that streets not marked as truck routes should be avoided. This information is derived from the `hgv=designated` tag. Note that even with values near 1, there is no guarantee the returned route will include streets marked as truck routes. The default value is 0. | + ##### Bicycle costing options The default bicycle costing is tuned toward road bicycles with a slight preference for using [cycleways](http://wiki.openstreetmap.org/wiki/Key:cycleway) or roads with bicycle lanes. Bicycle routes use regular roads where needed or where no direct bicycle lane options exist, but avoid roads without bicycle access. The costing model recognizes several factors unique to bicycle travel and offers several options for tuning bicycle routes. Several factors unique to travel by bicycle influence the resulting route. -* The types of roads suitable for bicycling depend on the type of bicycle. Road bicycles (skinny or narrow tires) generally are suited to paved roads or perhaps very short sections of compacted gravel. They are not designed for riding on coarse gravel or most paths and tracks through wooded areas or farmland. Mountain bikes, on the other hand, are able to traverse a wider set of surfaces. -* Average travel speed can be highly variable and can depend on bicycle type, fitness and experience of the cyclist, road surface, and hills. The costing model assumes a default speed on smooth, flat roads for each supported bicycle type. This speed can be overridden by an input option. The base speed is modulated by surface type (in conjunction with the bicycle type). In addition, speed is modified based on the hilliness of a road section. -* Bicyclists vary in their tolerance for riding on roads. Most novice bicyclists, and even other bicyclists, prefer cycleways and dedicated cycling paths and would rather avoid all but the quietest neighborhood roads. Other cyclists may be experienced riding on roads and prefer to take roadways because they often provide the fastest way to get between two places. The bicycle costing model accounts for this with a `use_roads` factor to indicate a cyclist's tolerance for riding on roads. -* Bicyclists vary in their fitness level and experience level, and many want to avoid hilly roads, and especially roads with very steep uphill or even downhill sections. Even if the fastest path is over a mountain, many cyclists prefer a flatter path that avoids the climb and descent up and over the mountain. +* The types of roads suitable for bicycling depend on the type of bicycle. Road bicycles (skinny or narrow tires) generally are suited to paved roads or perhaps very short sections of compacted gravel. They are not designed for riding on coarse gravel or most paths and tracks through wooded areas or farmland. Mountain bikes, on the other hand, are able to traverse a wider set of surfaces. +* Average travel speed can be highly variable and can depend on bicycle type, fitness and experience of the cyclist, road surface, and hills. The costing model assumes a default speed on smooth, flat roads for each supported bicycle type. This speed can be overridden by an input option. The base speed is modulated by surface type (in conjunction with the bicycle type). In addition, speed is modified based on the hilliness of a road section. +* Bicyclists vary in their tolerance for riding on roads. Most novice bicyclists, and even other bicyclists, prefer cycleways and dedicated cycling paths and would rather avoid all but the quietest neighborhood roads. Other cyclists may be experienced riding on roads and prefer to take roadways because they often provide the fastest way to get between two places. The bicycle costing model accounts for this with a `use_roads` factor to indicate a cyclist's tolerance for riding on roads. +* Bicyclists vary in their fitness level and experience level, and many want to avoid hilly roads, and especially roads with very steep uphill or even downhill sections. Even if the fastest path is over a mountain, many cyclists prefer a flatter path that avoids the climb and descent up and over the mountain. -The following options described above for autos also apply to bicycle costing methods: `maneuver_penalty`, `gate_cost`, `gate_penalty`, `country_crossing_cost`, `country_costing_penalty`, and `service_penalty`. +The following options described above for autos also apply to bicycle costing methods: `maneuver_penalty`, `gate_cost`, `gate_penalty`, `destination_only_penalty` , `country_crossing_cost`, `country_costing_penalty`, and `service_penalty`. These additional options are available for bicycle costing methods. @@ -165,7 +187,7 @@ These additional options are available for bicycle costing methods. | `use_ferry` | This value indicates the willingness to take ferries. This is a range of values between 0 and 1. Values near 0 attempt to avoid ferries and values near 1 will favor ferries. Note that sometimes ferries are required to complete a route so values of 0 are not guaranteed to avoid ferries entirely. The default value is 0.5. | | `use_living_streets` | This value indicates the willingness to take living streets. This is a range of values between 0 and 1. Values near 0 attempt to avoid living streets and values from 0.5 to 1 will currently have no effect on route selection. The default value is 0.5. Note that sometimes living streets are required to complete a route so values of 0 are not guaranteed to avoid living streets entirely. | | `avoid_bad_surfaces` | This value is meant to represent how much a cyclist wants to avoid roads with poor surfaces relative to the bicycle type being used. This is a range of values between 0 and 1. When the value is 0, there is no penalization of roads with different surface types; only bicycle speed on each surface is taken into account. As the value approaches 1, roads with poor surfaces for the bike are penalized heavier so that they are only taken if they significantly improve travel time. When the value is equal to 1, all bad surfaces are completely disallowed from routing, including start and end points. The default value is 0.25. | -|`bss_return_cost`| This value is useful when `bikeshare` is chosen as travel mode. It is meant to give the time will be used to return a rental bike. This value will be displayed in the final directions and used to calculate the whole duation. The default value is 120 seconds.| +|`bss_return_cost`| This value is useful when `bikeshare` is chosen as travel mode. It is meant to give the time will be used to return a rental bike. This value will be displayed in the final directions and used to calculate the whole duration. The default value is 120 seconds.| |`bss_return_penalty`| This value is useful when `bikeshare` is chosen as travel mode. It is meant to describe the potential effort to return a rental bike. This value won't be displayed and used only inside of the algorithm.| | `shortest` | Changes the metric to quasi-shortest, i.e. purely distance-based costing. Note, this will disable all other costings & penalties. Also note, `shortest` will not disable hierarchy pruning, leading to potentially sub-optimal routes for some costing models. The default is `false`. | @@ -177,8 +199,8 @@ All of the options described above for autos also apply to motor_scooter costing | Motor_scooter options | Description | | :-------------------------- | :----------- | | `top_speed` | Top speed the motorized scooter can go. Used to avoid roads with higher speeds than this value. For `motor_scooter` this value must be between 20 and 120 KPH. The default value is 45 KPH (~28 MPH) | -| `use_primary` | A riders's propensity to use primary roads. This is a range of values from 0 to 1, where 0 attempts to avoid primary roads, and 1 indicates the rider is more comfortable riding on primary roads. Based on the `use_primary` factor, roads with certain classifications and higher speeds are penalized in an attempt to avoid them when finding the best path. The default value is 0.5. | -| `use_hills` | A riders's desire to tackle hills in their routes. This is a range of values from 0 to 1, where 0 attempts to avoid hills and steep grades even if it means a longer (time and distance) path, while 1 indicates the rider does not fear hills and steeper grades. Based on the `use_hills` factor, penalties are applied to roads based on elevation change and grade. These penalties help the path avoid hilly roads in favor of flatter roads or less steep grades where available. Note that it is not always possible to find alternate paths to avoid hills (for example when route locations are in mountainous areas). The default value is 0.5. | +| `use_primary` | A rider's propensity to use primary roads. This is a range of values from 0 to 1, where 0 attempts to avoid primary roads, and 1 indicates the rider is more comfortable riding on primary roads. Based on the `use_primary` factor, roads with certain classifications and higher speeds are penalized in an attempt to avoid them when finding the best path. The default value is 0.5. | +| `use_hills` | A rider's desire to tackle hills in their routes. This is a range of values from 0 to 1, where 0 attempts to avoid hills and steep grades even if it means a longer (time and distance) path, while 1 indicates the rider does not fear hills and steeper grades. Based on the `use_hills` factor, penalties are applied to roads based on elevation change and grade. These penalties help the path avoid hilly roads in favor of flatter roads or less steep grades where available. Note that it is not always possible to find alternate paths to avoid hills (for example when route locations are in mountainous areas). The default value is 0.5. | | `shortest` | Changes the metric to quasi-shortest, i.e. purely distance-based costing. Note, this will disable all other costings & penalties. Also note, `shortest` will not disable hierarchy pruning, leading to potentially sub-optimal routes for some costing models. The default is `false`. | | `disable_hierarchy_pruning` | Disable hierarchies to calculate the actual optimal route. The default is `false`. **Note:** This could be quite a performance drainer so there is a upper limit of distance. If the upper limit is exceeded, this option will always be `false`. | @@ -214,12 +236,16 @@ These options are available for pedestrian costing methods. | `use_lit` | This value is a range of values from 0 to 1, where 0 indicates indifference towards lit streets, and 1 indicates that unlit streets should be avoided. Note that even with values near 1, there is no guarantee the returned route will include lit segments. The default value is 0. | | `service_penalty` | A penalty applied for transition to generic service road. The default penalty is 0. | | `service_factor` | A factor that modifies (multiplies) the cost when generic service roads are encountered. The default `service_factor` is 1. | +| `destination_only_penalty` | A penalty applied when entering an road which is only allowed to enter if necessary to reach the [destination](https://wiki.openstreetmap.org/wiki/Tag:vehicle%3Ddestination) | | `max_hiking_difficulty` | This value indicates the maximum difficulty of hiking trails that is allowed. Values between 0 and 6 are allowed. The values correspond to *sac_scale* values within OpenStreetMap, see reference [here](https://wiki.openstreetmap.org/wiki/Key:sac_scale). The default value is 1 which means that well cleared trails that are mostly flat or slightly sloped are allowed. Higher difficulty trails can be allowed by specifying a higher value for max_hiking_difficulty. -|`bss_rent_cost`| This value is useful when `bikeshare` is chosen as travel mode. It is meant to give the time will be used to rent a bike from a bike share station. This value will be displayed in the final directions and used to calculate the whole duation. The default value is 120 seconds.| +|`bss_rent_cost`| This value is useful when `bikeshare` is chosen as travel mode. It is meant to give the time will be used to rent a bike from a bike share station. This value will be displayed in the final directions and used to calculate the whole duration. The default value is 120 seconds.| |`bss_rent_penalty`| This value is useful when `bikeshare` is chosen as travel mode. It is meant to describe the potential effort to rent a bike from a bike share station. This value won't be displayed and used only inside of the algorithm.| | `shortest` | Changes the metric to quasi-shortest, i.e. purely distance-based costing. Note, this will disable all other costings & penalties. Also note, `shortest` will not disable hierarchy pruning, leading to potentially sub-optimal routes for some costing models. The default is `false`. | +| `max_distance` | Sets the maximum total walking distance of a route. Default is 100 km (~62 miles). | | `transit_start_end_max_distance` | A pedestrian option that can be added to the request to extend the defaults (2145 meters or approximately 1.5 miles). This is the maximum walking distance at the beginning or end of a route.| | `transit_transfer_max_distance` | A pedestrian option that can be added to the request to extend the defaults (800 meters or 0.5 miles). This is the maximum walking distance between transfers.| +| `type` | If set to `blind`, enables additional route instructions, especially useful for blind users: Announcing crossed streets, the stairs, bridges, tunnels, gates and bollards, which need to be passed on route; information about traffic signals on crosswalks; route numbers not announced for named routes. Default `foot` | +| `mode_factor` | A factor which the cost of a pedestrian edge will be multiplied with on multimodal request, e.g. `bss` or `multimodal/transit`. Default is a factor of 1.5, i.e. avoiding walking. ##### Transit costing options @@ -268,8 +294,18 @@ Directions options should be specified at the top level of the JSON object. | `units` | Distance units for output. Allowable unit types are miles (or mi) and kilometers (or km). If no unit type is specified, the units default to kilometers. | | `language` | The language of the narration instructions based on the [IETF BCP 47](https://tools.ietf.org/html/bcp47) language tag string. If no language is specified or the specified language is unsupported, United States-based English (en-US) is used. [Currently supported language list](#supported-language-tags) | | `directions_type` | An enum with 3 values.
  • `none` indicating no maneuvers or instructions should be returned.
  • `maneuvers` indicating that only maneuvers be returned.
  • `instructions` indicating that maneuvers with instructions should be returned (this is the default if not specified).
| +| `format` | Four options are available:
  • `json` is default valhalla routing directions JSON format
  • `gpx` returns the route as a GPX (GPS exchange format) XML track
  • `osrm` creates a OSRM compatible route directions JSON
  • `pbf` formats the result using protocol buffers
| +| `shape_format` | If `"format" : "osrm"` is set: Specifies the optional format for the path shape of each connection. One of `polyline6` (default), `polyline5`, `geojson` or `no_shape`. | +| `banner_instructions` | If the format is `osrm`, this boolean indicates if each step should have the additional `bannerInstructions` attribute, which can be displayed in some navigation system SDKs. | +| `voice_instructions` | If the format is `osrm`, this boolean indicates if each step should have the additional `voiceInstructions` attribute, which can be heard in some navigation system SDKs. | | `alternates` | A number denoting how many alternate routes should be provided. There may be no alternates or less alternates than the user specifies. Alternates are not yet supported on multipoint routes (that is, routes with more than 2 locations). They are also not supported on time dependent routes. | +For example a bus request with the result in Spanish using the OSRM (Open Source Routing Machine) format with the additional bannerInstructions and voiceInstructions in the steps would use the following json: + +```json +{"locations":[{"lat":40.730930,"lon":-73.991379},{"lat":40.749706,"lon":-73.991562}],"format":"osrm","costing":"bus","banner_instructions":true,"voice_instructions":true,"language":"es-ES"} +``` + ##### Supported language tags | Language tag | Language alias | Description | @@ -309,12 +345,13 @@ Directions options should be specified at the top level of the JSON object. | Options | Description | | :------------------ | :----------- | | `exclude_locations` | A set of locations to exclude or avoid within a route can be specified using a JSON array of avoid_locations. The avoid_locations have the same format as the locations list. At a minimum each avoid location must include latitude and longitude. The avoid_locations are mapped to the closest road or roads and these roads are excluded from the route path computation.| -| `exclude_polygons` | One or multiple exterior rings of polygons in the form of nested JSON arrays, e.g. `[[[lon1, lat1], [lon2,lat2]],[[lon1,lat1],[lon2,lat2]]]`. Roads intersecting these rings will be avoided during path finding. If you only need to avoid a few specific roads, it's **much** more efficient to use `exclude_locations`. Valhalla will close open rings (i.e. copy the first coordingate to the last position).| +| `exclude_polygons` | One or multiple exterior rings of polygons in the form of nested JSON arrays, e.g. `[[[lon1, lat1], [lon2,lat2]],[[lon1,lat1],[lon2,lat2]]]`. Roads intersecting these rings will be avoided during path finding. If you only need to avoid a few specific roads, it's **much** more efficient to use `exclude_locations`. Valhalla will close open rings (i.e. copy the first coordinate to the last position).| | `date_time` | This is the local date and time at the location.
  • `type`
    • 0 - Current departure time.
    • 1 - Specified departure time
    • 2 - Specified arrival time. Not yet implemented for multimodal costing method.
    • 3 - Invariant specified time. Time does not vary over the course of the path. Not implemented for multimodal or bike share routing
  • `value` - the date and time is specified in ISO 8601 format (YYYY-MM-DDThh:mm) in the local time zone of departure or arrival. For example "2016-07-03T08:06"

| -| `out_format` | Output format. If no `out_format` is specified, JSON is returned. Future work includes PBF (protocol buffer) support. | +| `elevation_interval` | Elevation interval (meters) for requesting elevation along the route. Valhalla data must have been generated with elevation data. If no `elevation_interval` is specified, no elevation will be returned for the route. An elevation interval of 30 meters is recommended when elevation along the route is desired, matching the default data source's resolution. | | `id` | Name your route request. If `id` is specified, the naming will be sent thru to the response. | | `linear_references` | When present and `true`, the successful `route` response will include a key `linear_references`. Its value is an array of base64-encoded [OpenLR location references][openlr], one for each graph edge of the road network matched by the input trace. | | `prioritize_bidirectional` | Prioritize `bidirectional a*` when `date_time.type = depart_at/current`. By default `time_dependent_forward a*` is used in these cases, but `bidirectional a*` is much faster. Currently it does not update the time (and speeds) when searching for the route path, but the ETA on that route is recalculated based on the time-dependent speeds | +| `roundabout_exits` | A boolean indicating whether exit instructions at roundabouts should be added to the output or not. Default is true. | [openlr]: https://www.openlr-association.com/fileadmin/user_upload/openlr-whitepaper_v1.5.pdf @@ -341,9 +378,9 @@ The summary JSON object includes: | :---- | :----------- | | `time` | Estimated elapsed time to complete the trip. | | `length` | Distance traveled for the entire trip. Units are either miles or kilometers based on the input units specified. | -| `has_toll`| Flag indicating if the the path uses one or more toll segments. | -| `has_highway`| Flag indicating if the the path uses one or more highway segments. | -| `has_ferry`| Flag indicating if the the path uses one or more ferry segments. | +| `has_toll`| Flag indicating if the path uses one or more toll segments. | +| `has_highway`| Flag indicating if the path uses one or more highway segments. | +| `has_ferry`| Flag indicating if the path uses one or more ferry segments. | | `min_lat` | Minimum latitude of a bounding box containing the route. | | `min_lon` | Minimum longitude of a bounding box containing the route. | | `max_lat` | Maximum latitude of a bounding box containing the route. | @@ -354,7 +391,9 @@ The summary JSON object includes: A `trip` contains one or more `legs`. For *n* number of `break` locations, there are *n-1* legs. `Through` locations do not create separate legs. -Each leg of the trip includes a summary, which is comprised of the same information as a trip summary but applied to the single leg of the trip. It also includes a `shape`, which is an [encoded polyline](https://developers.google.com/maps/documentation/utilities/polylinealgorithm) of the route path (with 6 digits decimal precision), and a list of `maneuvers` as a JSON array. For more about decoding route shapes, see these [code examples](/docs/decoding.md). +Each leg of the trip includes a summary, which is comprised of the same information as a trip summary but applied to the single leg of the trip. It also includes a `shape`, which is an [encoded polyline](https://developers.google.com/maps/documentation/utilities/polylinealgorithm) of the route path (with 6 digits decimal precision), and a list of `maneuvers` as a JSON array. For more about decoding route shapes, see these [code examples](../../decoding.md). + +If `elevation_interval` is specified, each leg of the trip will return `elevation` along the route as a JSON array. The `elevation_interval` is also returned. Units for both `elevation` and `elevation_interval` are either meters or feet based on the input units specified. Each maneuver includes: @@ -430,6 +469,11 @@ kTransitConnectionDestination = 35; kPostTransitConnectionDestination = 36; kMergeRight = 37; kMergeLeft = 38; +kElevatorEnter = 39; +kStepsEnter = 40; +kEscalatorEnter = 41; +kBuildingEnter = 42; +kBuildingExit = 43; ``` The maneuver `sign` may contain four lists of interchange sign elements as follows: @@ -456,7 +500,7 @@ A maneuver `transit_info` includes: | `headsign` | The sign on a public transport vehicle that identifies the route destination to passengers. For example "ASTORIA - DITMARS BLVD". | | `color` | The numeric color value associated with a transit route. The value for yellow would be "16567306". | | `text_color` | The numeric text color value associated with a transit route. The value for black would be "0". | -| `description` | The description of the the transit route. For example "Trains operate from Ditmars Boulevard, Queens, to Stillwell Avenue, Brooklyn, at all times. N trains in Manhattan operate along Broadway and across the Manhattan Bridge to and from Brooklyn. Trains in Brooklyn operate along 4th Avenue, then through Borough Park to Gravesend. Trains typically operate local in Queens, and either express or local in Manhattan and Brooklyn, depending on the time. Late night trains operate via Whitehall Street, Manhattan. Late night service is local". | +| `description` | The description of the transit route. For example "Trains operate from Ditmars Boulevard, Queens, to Stillwell Avenue, Brooklyn, at all times. N trains in Manhattan operate along Broadway and across the Manhattan Bridge to and from Brooklyn. Trains in Brooklyn operate along 4th Avenue, then through Borough Park to Gravesend. Trains typically operate local in Queens, and either express or local in Manhattan and Brooklyn, depending on the time. Late night trains operate via Whitehall Street, Manhattan. Late night service is local". | | `operator_onestop_id` | Global operator/agency identifier. | | `operator_name` | Operator/agency name. For example, "BART", "King County Marine Division", and so on. Short name is used over long name. | | `operator_url` | Operator/agency URL. For example, "http://web.mta.info/". | @@ -606,4 +650,5 @@ The codes correspond to code returned from a particular [Valhalla project](https |**5xx** | **Tyr project codes** | |500 | Failed to parse intermediate request format | |501 | Failed to parse TripDirections | +|504 | GeoTIFF serialization not supported by service | |599 | Unknown | diff --git a/docs/docs/api/turn-by-turn/overview.md b/docs/docs/api/turn-by-turn/overview.md index 322376aa7a..0c678e738c 100644 --- a/docs/docs/api/turn-by-turn/overview.md +++ b/docs/docs/api/turn-by-turn/overview.md @@ -8,7 +8,7 @@ When you [request a route](api-reference.md#inputs-of-a-route), you are sending The service [route results](api-reference.md#outputs-of-a-route) provide details about the trip, including locations, a summary with basic information about the entire trip and a list of legs. Each leg has its own summary, a shape, which is an encoded polyline of the route path, and a list of maneuvers. These maneuvers provide written narrative instructions, plus verbal alerts that can be used as audio guidance in navigation apps. -The JSON returned from the route query can be drawn on a map and shown as instructions for maneuvers along the route. You can [display Valhalla routes](add-routing-to-a-map.md) on web and mobile maps. +The JSON returned from the route query can be drawn on a map and shown as instructions for maneuvers along the route. You can display Valhalla routes on web and mobile maps, e.g. [https://valhalla.openstreetmap.de](https://valhalla.openstreetmap.de). ## Data sources in Turn-by-Turn diff --git a/docs/docs/baldr.md b/docs/docs/baldr.md index a8c5ccd3ec..2c05ce3140 100644 --- a/docs/docs/baldr.md +++ b/docs/docs/baldr.md @@ -3,7 +3,7 @@ Baldr serves as a set of routing-specific data structures for use within other pieces of the valhalla library. In keeping with the Norse mythological theme, the name [Baldr](http://en.wikipedia.org/wiki/Baldr) was chosen as a backronym standing for: Base ALgorithms and Data Resource. Since baldr deals mostly with accessing routing data and algorithms related to routing subproblems. -Baldr is essentially a set of various data structures and alogrithms which deal with things like: route data tiles, tile caching, hierarchical tile layout and tile data members such as nodes, edgeds and exits. +Baldr is essentially a set of various data structures and algorithms which deal with things like: route data tiles, tile caching, hierarchical tile layout and tile data members such as nodes, edgeds and exits. ## Components diff --git a/docs/docs/building.md b/docs/docs/building.md index bdb9f86e19..50cb7dac78 100644 --- a/docs/docs/building.md +++ b/docs/docs/building.md @@ -26,11 +26,32 @@ Important build options include: | `-DENABLE_SANITIZERS` (`ON` / `OFF`) | Build with all the integrated sanitizers (defaults to off).| | `-DENABLE_ADDRESS_SANITIZER` (`ON` / `OFF`) | Build with address sanitizer (defaults to off).| | `-DENABLE_UNDEFINED_SANITIZER` (`ON` / `OFF`) | Build with undefined behavior sanitizer (defaults to off).| +| `-DPREFER_SYSTEM_DEPS` (`ON` / `OFF`) | Whether to use internally vendored headers or find the equivalent external package (defaults to off).| +| `-DENABLE_GDAL` (`ON` / `OFF`) | Whether to include GDAL as a dependency (used for GeoTIFF serialization of isochrone grid) (defaults to off).| -If you're building on Apple Silicon and use the Rosetta terminal (see below), you might need to additionally specify the appropriate options: +### Building with `vcpkg` - any platform -``` -cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64" +Instead of installing the dependencies system-wide, you can also opt to use [`vcpkg`](https://github.com/microsoft/vcpkg). + +The following commands should work on all platforms: + +```bash +git clone --recurse-submodules https://github.com/valhalla/valhalla +cd valhalla +git clone https://github.com/microsoft/vcpkg && git -C vcpkg checkout +./vcpkg/bootstrap-vcpkg.sh +# windows: cmd.exe /c bootstrap-vcpkg.bat +# only build Release versions of dependencies, not Debug +echo "set(VCPKG_BUILD_TYPE release)" >> vcpkg/triplets/x64-linux.cmake +# windows: echo.set(VCPKG_BUILD_TYPE release)>> .\vcpkg\triplets\x64-windows.cmake +# osx: echo "set(VCPKG_BUILD_TYPE release)" >> vcpkg/triplets/arm64-osx.cmake + +# vcpkg will install everything during cmake configuration +# if you want to ENABLE_SERVICES=ON, install https://github.com/kevinkreiser/prime_server#build-and-install (no Windows) +cmake -B build -DCMAKE_TOOLCHAIN_FILE=$PWD/vcpkg/scripts/buildsystems/vcpkg.cmake -DENABLE_SERVICE=OFF +cmake --build build -- -j$(nproc) +# windows: cmake --build build --config Release -- /clp:ErrorsOnly /p:BuildInParallel=true /m:4 +# osx: cmake --build build -- -j$(sysctl -n hw.physicalcpu) ``` ### Building from Source - Linux @@ -52,38 +73,13 @@ sudo make -C build install ### Building from Source - macOS -#### Configuring Rosetta for ARM64 MacBook - -Check your architecture typing `arch` in the terminal. In case the result is `arm64` set up Rosetta terminal to emulate x86_64 behavior. Otherwise, skip this step. - -1. Go to `Finder > Application > Utilities`. -2. Select `Terminal` and right-click on it, then choose `Duplicate`. -3. Rename the duplicated app `Rosetta Terminal`. -4. Now select `Rosetta Terminal` application, right-click and choose `Get Info` . -5. Check the box for `Open using Rosetta`, then close the `Get Info` window. -6. Make shure you get `i386` after typing `arch` command in `Rosetta Terminal`. -7. Now it fully supports Homebrew and other x86_64 command line applications. - -Install [Homebrew](http://brew.sh) in the `Rosetta Terminal` app and update the aliases. - -``` -echo "alias ibrew='arch -x86_64 /usr/local/bin/brew'" >> ~/.zshrc -echo "alias mbrew='arch -arm64e /opt/homebrew/bin/brew'" >> ~/.zshrc -``` - -You will use them to specify the platform when installing a library. Note: use `ibrew` in `Rosetta Terminal` to install all dependencies for `valhalla` and `prime_server` projects. - -**_NOTE:_** If when installing packages below you get message `attempting to link with file built for macOS-arm64`, you can remove already installed packages for arm64 i.e. `mbrew uninstall ...`. Also, if there are problems with individual packages, you can install them from sources e.g. [geos](https://github.com/libgeos/geos) or [sqlite](https://www.sqlite.org/download.html). - -**_NOTE:_** It is possible to build Valhalla natively for Apple Silicon, but some dependencies(e.g. LuaJIT) don't have stable versions supporting Apple Silicon and have to be built and installed manually from source. +Both arm64 and x64 should build cleanly with the below commands. -#### Installing dependencies - -To install valhalla on macOS, you need to install its dependencies with [Homebrew](http://brew.sh): +To install Valhalla on macOS, you need to install its dependencies with [Homebrew](http://brew.sh): ```bash # install dependencies (automake & czmq are required by prime_server) -brew install automake cmake libtool protobuf-c libspatialite pkg-config sqlite3 jq curl wget czmq lz4 spatialite-tools unzip luajit +brew install automake cmake libtool protobuf-c libspatialite pkg-config sqlite3 jq curl wget czmq lz4 spatialite-tools unzip luajit boost # following packages are needed for running Linux compatible scripts brew install bash coreutils binutils # Update your PATH env variable to include /usr/local/opt/binutils/bin:/usr/local/opt/coreutils/libexec/gnubin @@ -114,25 +110,37 @@ It's recommended to work with the following toolset: - [vcpkg](https://github.com/Microsoft/vcpkg) to install packages - [CMake](https://cmake.org/download/) -1. Install the following packages with `vcpkg` and your platform triplet (e.g. `x64-windows`). Note, you can remove all packages after `zlib` in `.\.vcpkg_deps.txt` if you don't want to build `TOOLS` & `DATA_TOOLS`: +1. Install the dependencies with `vcpkg`: ``` -# Basic packages -git -C C:\path\to\vcpkg checkout f4bd6423 -cd C:\path\to\project -C:\path\to\vcpkg.exe --triplet x64-windows install "@.vcpkg_deps.txt" +git -C C:\path\to\vcpkg checkout f330a32 +# only build release versions for vcpkg packages +echo.set(VCPKG_BUILD_TYPE release)>> path\to\vcpkg\triplets\x64-windows.cmake +cd C:\path\to\valhalla +C:\path\to\vcpkg.exe install --triplet x64-windows ``` 2. Let CMake configure the build with the required modules enabled. The final command for `x64` could look like ``` -"C:\Program Files\CMake\bin\cmake.EXE" --no-warn-unused-cli -DENABLE_TOOLS=ON -DENABLE_DATA_TOOLS=ON -DENABLE_PYTHON_BINDINGS=ON -DENABLE_HTTP=ON -DENABLE_CCACHE=OFF -DENABLE_SERVICES=OFF -DENABLE_BENCHMARKS=OFF -DENABLE_TESTS=OFF -DVCPKG_TARGET_TRIPLET=x64-windows -DCMAKE_TOOLCHAIN_FILE=path\to\vcpkg\scripts\buildsystems\vcpkg.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -Hpath/to/project -Bpath/to/project/build -G "Visual Studio 16 2019" -T host=x64 -A x64 +"C:\Program Files\CMake\bin\cmake.EXE" --no-warn-unused-cli -DENABLE_TOOLS=ON -DENABLE_DATA_TOOLS=ON -DENABLE_PYTHON_BINDINGS=ON -DENABLE_HTTP=ON -DENABLE_CCACHE=OFF -DENABLE_SERVICES=OFF -DENABLE_BENCHMARKS=OFF -DENABLE_TESTS=OFF -DVCPKG_TARGET_TRIPLET=x64-windows -DCMAKE_TOOLCHAIN_FILE=path\to\vcpkg\scripts\buildsystems\vcpkg.cmake -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -Hpath/to/valhalla -Bpath/to/valhalla/build -G "Visual Studio 16 2019" -T host=x64 -A x64 ``` 3. Run the build for all targets. ``` -cd C:\path\to\project -cmake -B build --config Release -- /clp:ErrorsOnly /p:BuildInParallel=true /m:8 +cmake -B build -S C:\path\to\valhalla --config Release -- /clp:ErrorsOnly /p:BuildInParallel=true /m:8 ``` The artifacts will be built to `./build/Release`. +#### Troubleshooting + +- if the build fails on something with `date_time`, chances are you don't have [`make`](https://gnuwin32.sourceforge.net/packages/make.htm) and/or [`awk`](https://gnuwin32.sourceforge.net/packages/gawk.htm) installed, which is needed to properly configure `third_party/tz`. Even so, it might still fail because the used MS shell can't handle `mv` properly. In that case simply mv `third_party/tz/leapseconds.out` to `third_party/tz/leapseconds` and start the build again + +### Include Valhalla as a project dependency + +When importing `libvalhalla` as a dependency in a project, it's important to know that we're using both CMake and `pkg-config` to resolve our own dependencies. Check the root `CMakeLists.txt` for details. This is important in case you'd like to bring your own dependencies, such as cURL or protobuf. It's always safe to use `PKG_CONFIG_PATH` environment variable to point CMake to custom installations, however, for dependencies we resolve with `find_package` you'll need to check CMake's built-in `Find*` modules on how to provide the proper paths. + +To resolve `libvalhalla`'s linker/library paths/options, we recommend to use `pkg-config` or `pkg_check_modules` (in CMake). + +Currently, `rapidjson`, `date` & `dirent` (Win only) headers are vendored in `third_party`. Consuming applications are encouraged to use `pkg-config` to resolve Valhalla and its dependencies which will automatically install those headers to `/path/to/include/valhalla/third_party/{rapidjson, date, dirent.h}` and can be `#include`d appropriately. + ## Running Valhalla server on Unix The following script should be enough to make some routing data and start a server using it. (Note - if you would like to run an elevation lookup service with Valhalla follow the instructions [here](./elevation.md)). @@ -146,8 +154,9 @@ mkdir -p valhalla_tiles valhalla_build_config --mjolnir-tile-dir ${PWD}/valhalla_tiles --mjolnir-tile-extract ${PWD}/valhalla_tiles.tar --mjolnir-timezone ${PWD}/valhalla_tiles/timezones.sqlite --mjolnir-admin ${PWD}/valhalla_tiles/admins.sqlite > valhalla.json # build timezones.sqlite to support time-dependent routing valhalla_build_timezones > valhalla_tiles/timezones.sqlite +# build admins.sqlite to support admin-related properties such as access restrictions, driving side, ISO codes etc +valhalla_build_admins -c valhalla.json switzerland-latest.osm.pbf liechtenstein-latest.osm.pbf # build routing tiles -# TODO: run valhalla_build_admins? valhalla_build_tiles -c valhalla.json switzerland-latest.osm.pbf liechtenstein-latest.osm.pbf # tar it up for running the server # either run this to build a tile index for faster graph loading times diff --git a/docs/docs/loki.md b/docs/docs/loki.md index c34d7a4a02..30b9bd19ad 100644 --- a/docs/docs/loki.md +++ b/docs/docs/loki.md @@ -2,7 +2,7 @@ Loki can be used to associate location information to an underlying graph tile object for use in creating input to the [routing engine](thor.md). In keeping with the Norse mythological theme, the name [Loki](http://en.wikipedia.org/wiki/Loki) was chosen as a play on the word locate. Since loki deals mostly with correlating some input (minimally a lat,lon) to an object within a graph tile, this seemed like a fitting name! -Loki is essentially a set of various data structures and alogrithms which deal with things like: correlating an input location to the underlying graph, partial distance along an edge and filtering edges which shouldn't be considered for correlation. +Loki is essentially a set of various data structures and algorithms which deal with things like: correlating an input location to the underlying graph, partial distance along an edge and filtering edges which shouldn't be considered for correlation. ## Components ## @@ -38,11 +38,11 @@ In terms of features there are a few things I didn't mention that the current (a Another feature provided by the current (and previous) version of loki is that of favoring a direction of travel. This is very useful in a mobile navigation context. Say you are speeding down a two lane road when your passenger presses the 'get route' button. As previously mentioned, even if you two are smack in the middle of a long edge with no other connecting edges around, technically there are two results, the edge in the direction you are traveling and the edge in the reverse direction of travel. The thing is, you don't care about the route in the reverse direction of travel. So loki allows you to specify a favored direction of travel and if one of the directions is with in a 90 degree window of that then it will be included in the candidate set passed onto the routing algorithm. -You might be noticing a theme here. In some cases we want to reject or favor certain edges based on some criteria (usually derived from the users request parameters). And this is the first area of future work. For a given edge we essentially want to assign a probability which denotes its likelihood of being the actual edge intended in the request. At this point the probabilty is binary. It either is traversable by your mode of travel or not. It either is in the general direction you are going, or not. It either is the closest edge to your input or not. It would seem we should want to have each of these factors influence a portion of the final probability. This would allow us to add other factors in the future. For example, if the request supplied a street name with the coordinate we could give a higher weighting to edges with similar street names. This means we would want to actually look at least a little bit further than the absolute closest edge in the presence of that edge not matching the provided street name. +You might be noticing a theme here. In some cases we want to reject or favor certain edges based on some criteria (usually derived from the users request parameters). And this is the first area of future work. For a given edge we essentially want to assign a probability which denotes its likelihood of being the actual edge intended in the request. At this point the probability is binary. It either is traversable by your mode of travel or not. It either is in the general direction you are going, or not. It either is the closest edge to your input or not. It would seem we should want to have each of these factors influence a portion of the final probability. This would allow us to add other factors in the future. For example, if the request supplied a street name with the coordinate we could give a higher weighting to edges with similar street names. This means we would want to actually look at least a little bit further than the absolute closest edge in the presence of that edge not matching the provided street name. -Another general problem is that of islands of connectivity. Without traversing the route network, one cannot say for certain that two edges are reachable from one another (for a given mode of travel). So it happens with some degree of probability that loki will correlate a pair of inputs but the routing algorithm will find no path between them. In some cases this is by design. In fact at a coarse level, if two regions are not possibly connected, think O'ahu and Mt. Everest, we won't even ask loki to correlate the coordinates to the graph. Even places that are quite close can have reachability issues which indeed should fail to find paths (unless you have a boat/hanglider/climbing gear handy). However there are a number of fairly frequently occurring cases that follow a pattern similar to the following. A user is somewhere on the road network (highly connected). User wants to go to a POI. The POI itself might be a polygon such as a park, the white house or a golf course, which will give an input coordiate as the centroid of that feature. Or it may be a point POI, such as a city center or something relatively small like an overlook on a mountain. The problem with all of these is that their location, the coordinate that will be provided as input to loki was picked so that the feature could be properly labeled on the map. In other words the coordinates are those you'd expect to use if you wanted to see where the thing is. This is often very unhelpful for the purpose of routing. Not only could routing to that label point be suboptimal (the parking for the golf course is not near its centroid), but it could actually put you on an island of limited connectivity. So that when you pass these results to the routing algorithm you won't find a path. +Another general problem is that of islands of connectivity. Without traversing the route network, one cannot say for certain that two edges are reachable from one another (for a given mode of travel). So it happens with some degree of probability that loki will correlate a pair of inputs but the routing algorithm will find no path between them. In some cases this is by design. In fact at a coarse level, if two regions are not possibly connected, think O'ahu and Mt. Everest, we won't even ask loki to correlate the coordinates to the graph. Even places that are quite close can have reachability issues which indeed should fail to find paths (unless you have a boat/hanglider/climbing gear handy). However there are a number of fairly frequently occurring cases that follow a pattern similar to the following. A user is somewhere on the road network (highly connected). User wants to go to a POI. The POI itself might be a polygon such as a park, the white house or a golf course, which will give an input coordinate as the centroid of that feature. Or it may be a point POI, such as a city center or something relatively small like an overlook on a mountain. The problem with all of these is that their location, the coordinate that will be provided as input to loki was picked so that the feature could be properly labeled on the map. In other words the coordinates are those you'd expect to use if you wanted to see where the thing is. This is often very unhelpful for the purpose of routing. Not only could routing to that label point be suboptimal (the parking for the golf course is not near its centroid), but it could actually put you on an island of limited connectivity. So that when you pass these results to the routing algorithm you won't find a path. -To tackle this issue we have a few options. We could at data creation time crawl the route network to find small islands of connectivity. We could mark the edges in these islands so that loki would know to only send them to the routing algorithm if both input coordinates were in the same island. Or we could use a multi-pass approach in which we have the routing algorithm detect when its search is trapped in an island of connectivity and send the list of edges with in back to loki as a set of edges excluded from the correlation process. That latter would seem like the best option at this point in time simply because the information needed to store and time to crawl the tiles to find these small islands of connectivity would be prohibative. +To tackle this issue we have a few options. We could at data creation time crawl the route network to find small islands of connectivity. We could mark the edges in these islands so that loki would know to only send them to the routing algorithm if both input coordinates were in the same island. Or we could use a multi-pass approach in which we have the routing algorithm detect when its search is trapped in an island of connectivity and send the list of edges with in back to loki as a set of edges excluded from the correlation process. That latter would seem like the best option at this point in time simply because the information needed to store and time to crawl the tiles to find these small islands of connectivity would be prohibitive. The final area for future work would be an elaboration to what was said earlier about wanting only to look a the highest detail level of route network data. One could conceive of a scenario in which a user has a route and they want to drag a portion of that route so as to force it toward a certain feature. If the route network is dense where that feature lives but the users map is zoomed out such that the user only sees certain route network edges loki should attempt to correlate to those rather than the possibly not visible edges in the area. Essentially when doing a correlation at a course zoom level we may want to exclude certain classes of edges that are unlikely to be visible to the user interacting with the map. diff --git a/docs/docs/meili/overview.md b/docs/docs/meili/overview.md index 16a5687012..a74817f635 100644 --- a/docs/docs/meili/overview.md +++ b/docs/docs/meili/overview.md @@ -18,7 +18,7 @@ The above is an image of the 4 magenta edge candidates for an input trace point ## Viterbi -Viterbi is a dynamic programming algorithm used to find paths (eg. Markov chains) through a hidden state diagram such as a hidden Markov model. Read more about it here: https://en.wikipedia.org/wiki/Viterbi_algorithm. Within Meili we use the algorithm to determine the highest probability map match while doing the least number of routing calculations possible. You can think of the matrix described above as a series of nodes in a state diagram. Each row in each column has a connection to each row in the prevoius and next column in the diagram. We refer to these unique column/row pairs as `State`s through out the code and each is given a `StateId` which represents which column (0..n) and which candidate (0..m) it refers to. In the figure below candidates 0 1 and 2 are in column 0 whereas candidates 3, 4, 5 and 6 are in column 1. +Viterbi is a dynamic programming algorithm used to find paths (eg. Markov chains) through a hidden state diagram such as a hidden Markov model. Read more about it here: https://en.wikipedia.org/wiki/Viterbi_algorithm. Within Meili we use the algorithm to determine the highest probability map match while doing the least number of routing calculations possible. You can think of the matrix described above as a series of nodes in a state diagram. Each row in each column has a connection to each row in the previous and next column in the diagram. We refer to these unique column/row pairs as `State`s through out the code and each is given a `StateId` which represents which column (0..n) and which candidate (0..m) it refers to. In the figure below candidates 0 1 and 2 are in column 0 whereas candidates 3, 4, 5 and 6 are in column 1. ![Model](figures/model.png) @@ -36,11 +36,11 @@ The `MatchResult`s that we got back only included those points which `AppendMeas p1p2------p3-------p4p5p6-----------p7--------------p8-----------p9p10 -In the above `{p2, p5, p6, p9}` are all interpolated because they are close in distance to a prior point or in `p9`s case close to the last point which cannot be interpolated. So what we do next is we loop over pairs of states again and if between those two states there were unused (ie interpolated) input points then we project each of those points in succession onto the route between them. Using the example above, if we found a route between p4 and p7, then we would project p5 and p6 onto this route geometry to compute their `MatchResults`. Interpolation also gaurantees that sequence ordering remains unchanged, ie p4 comes before p5 comes before p6 comes before p7 in the final route geometry. +In the above `{p2, p5, p6, p9}` are all interpolated because they are close in distance to a prior point or in `p9`s case close to the last point which cannot be interpolated. So what we do next is we loop over pairs of states again and if between those two states there were unused (ie interpolated) input points then we project each of those points in succession onto the route between them. Using the example above, if we found a route between p4 and p7, then we would project p5 and p6 onto this route geometry to compute their `MatchResults`. Interpolation also guarantees that sequence ordering remains unchanged, ie p4 comes before p5 comes before p6 comes before p7 in the final route geometry. ## Route Building -Next we get the second part of Meil's output which is the actual path through the graph that was taken. We compute this with a call to `ConstructRoute`. Construct route uses the `States` to get at sequences of edges stored as a collection of `EdgeLabel`s in a `LabelSet`. The `LableSet` contains all the edge labels a graph expansion saw. For a given `State` we know which was the last label it saw when it reached the destination `State`. So to get the route out we simply follow the chain of `EdgeLabel`s back to the origin `State`. Its like a linkedlist but without pointers, instead it uses indices into the `LabelSet`. The `MergeRoute` function is responsible for this recovery of a path between two states. +Next we get the second part of Meil's output which is the actual path through the graph that was taken. We compute this with a call to `ConstructRoute`. Construct route uses the `States` to get at sequences of edges stored as a collection of `EdgeLabel`s in a `LabelSet`. The `LabelSet` contains all the edge labels a graph expansion saw. For a given `State` we know which was the last label it saw when it reached the destination `State`. So to get the route out we simply follow the chain of `EdgeLabel`s back to the origin `State`. Its like a linkedlist but without pointers, instead it uses indices into the `LabelSet`. The `MergeRoute` function is responsible for this recovery of a path between two states. From these `EdgeLabels` we make a vector of `EdgeSegment` objects, which is the final object that is returned. The `EdgeSegment` stores information about which edge in the graph it is, how much of it was used and what was the first and last `MatchResult` that got matched onto this `EdgeSegment`. It also contains information about whether or not there is a discontinuity after this `EdgeSegment`. As described before a discontinuity occurs when no paths for any candidates can be found from between two columns. @@ -52,7 +52,7 @@ Meili supports the notion of alternatives. The API calls this "best_paths" but s The first caveat is that if there is a discontinuity in one of the results we will not return any more results after that. So if the first match had a discontinuity you'll only get 1 result even if you asked for 2. -The second caveat is that redundant paths are not returned. What is a redundant path? Its a path that has already been seen in a previous result. Technically this means that the sequence of `EdgeSegments` has already been seen. Why would this happen? It is common that a `MatchResult` has two candidates, but no matter which one is used, the sequence of edges in the path remains the same. Basically the `MatchResults` may move around but the path is the same. This commonly occurs at intersections where one candidate may be some distance along the edge and the alternative candiate will be at the end of the previous edge. Either way both edges are on the path, the path didn't materially change. +The second caveat is that redundant paths are not returned. What is a redundant path? Its a path that has already been seen in a previous result. Technically this means that the sequence of `EdgeSegments` has already been seen. Why would this happen? It is common that a `MatchResult` has two candidates, but no matter which one is used, the sequence of edges in the path remains the same. Basically the `MatchResults` may move around but the path is the same. This commonly occurs at intersections where one candidate may be some distance along the edge and the alternative candidate will be at the end of the previous edge. Either way both edges are on the path, the path didn't materially change. ## Complications @@ -62,4 +62,4 @@ The biggest complication in map matching by far is that of dealing with node sna Meili itself does not have an external API. It kind of used to but has since been refactored to be accessed via the rest of our routing APIs. What this means is that it must fulfill the same contract that Thor does. That contract consists of a series of `Location`s with the chosen candidate for each `Location` getting filled out (in this case we translate the `MatchResult` into this) as well as a path which is a series of `PathInfo`s representing the edgs on the path and their cost/duration. -The bulk of what has been described about Meili above was refering to its main entry point `OfflineMatch` (offline refers to the type of algorithm, see here: https://en.wikipedia.org/wiki/Online_algorithm). Thor must take the results of this function which, as described earlier, is a series of `MatchResults` and a series of `EdgeSegments` and convert them. This conversion takes place first via `FormPath` (every path finding algorithm implements one of these) and then via a call to `TripLegBuilder::Build` for each leg of the route. `FormPath` builds the vector of `PathInfo` objects from the `EdgeSegments`. The `MatchResults` are used to build origin and destination `Location`s. The `PathInfo`s and `Location`s are then sent to `TripLegBuilder::Build` as with every other routing operation. +The bulk of what has been described about Meili above was referring to its main entry point `OfflineMatch` (offline refers to the type of algorithm, see here: https://en.wikipedia.org/wiki/Online_algorithm). Thor must take the results of this function which, as described earlier, is a series of `MatchResults` and a series of `EdgeSegments` and convert them. This conversion takes place first via `FormPath` (every path finding algorithm implements one of these) and then via a call to `TripLegBuilder::Build` for each leg of the route. `FormPath` builds the vector of `PathInfo` objects from the `EdgeSegments`. The `MatchResults` are used to build origin and destination `Location`s. The `PathInfo`s and `Location`s are then sent to `TripLegBuilder::Build` as with every other routing operation. diff --git a/docs/docs/meili/service_api.md b/docs/docs/meili/service_api.md index 04271106b8..78e7b05e43 100644 --- a/docs/docs/meili/service_api.md +++ b/docs/docs/meili/service_api.md @@ -13,7 +13,7 @@ the `POST` request body. URL Parameter | Description | Default ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | ---------- -`mode` | Transport mode of the sequence. Possible modes are: `auto`, `bycicle`, `pedestrian` and `multimodal`. | `multimodal` +`mode` | Transport mode of the sequence. Possible modes are: `auto`, `bicycle`, `pedestrian` and `multimodal`. | `multimodal` `search_radius` | A numeric value in range `[0, 100]` to specify a radius (in meters) within which to search road candidates for each measurement. | 40 * Specifying a transport mode can limit the type of roads to match diff --git a/docs/docs/mjolnir.md b/docs/docs/mjolnir.md index b8b6dff01b..a6f4507b16 100644 --- a/docs/docs/mjolnir.md +++ b/docs/docs/mjolnir.md @@ -1,6 +1,6 @@ # Mjolnir -The mjolnir is essentially a set of applications, data structures and alogrithms which deal with things like: parsing OSM data extracts, cutting routable "graph" tiles, generating tile hierarchies and testing for data deficiencies. This tiled routing data is used in routing and searching under the valhalla organization. In keeping with the Norse mythological theme, the name [Mjölnir](http://en.wikipedia.org/wiki/Mj%C3%B6lnir) was chosen as it represents a weapon of mass destruction. This seemed fitting since the main application deals mostly with pounding planet sized OSM data into tiny routable tile fragments. +The mjolnir is essentially a set of applications, data structures and algorithms which deal with things like: parsing OSM data extracts, cutting routable "graph" tiles, generating tile hierarchies and testing for data deficiencies. This tiled routing data is used in routing and searching under the valhalla organization. In keeping with the Norse mythological theme, the name [Mjölnir](http://en.wikipedia.org/wiki/Mj%C3%B6lnir) was chosen as it represents a weapon of mass destruction. This seemed fitting since the main application deals mostly with pounding planet sized OSM data into tiny routable tile fragments. ## Components ## diff --git a/docs/docs/mjolnir/getting_started_guide.md b/docs/docs/mjolnir/getting_started_guide.md index a7aff79d1d..b5e1af92b8 100644 --- a/docs/docs/mjolnir/getting_started_guide.md +++ b/docs/docs/mjolnir/getting_started_guide.md @@ -1,8 +1,8 @@ # Mjolnir: Getting started guide -The mjolnir library is essentially a set of applications, data structures and alogrithms which deal with things like: parsing OpenStreetMap data extracts, cutting routable "graph" tiles, generating tile hierarchies and testing for data deficiencies. +The mjolnir library is essentially a set of applications, data structures and algorithms which deal with things like: parsing OpenStreetMap data extracts, cutting routable "graph" tiles, generating tile hierarchies and testing for data deficiencies. -If you would like to create your own routing tiles, this guilde will help you get started. +If you would like to create your own routing tiles, this guide will help you get started. ### Data diff --git a/docs/docs/mjolnir/historical_traffic.md b/docs/docs/mjolnir/historical_traffic.md new file mode 100644 index 0000000000..6504497a22 --- /dev/null +++ b/docs/docs/mjolnir/historical_traffic.md @@ -0,0 +1,59 @@ +# Traffic in Valhalla + +Traffic data for routing engines can be divided into two different types: + +- *Historical traffic* describes the typically observed speed on a road at a specific point in time. So e.g. the typical speed on a Monday morning at 8 o'clock. +- *Live traffic* describes the currently observed speed on a road according to the actual traffic situation. + +Valhalla supports both types of traffic for all APIs, excluding multimodal costing. For more information about how to route with traffic data and time information, check the respective API documentation. In the following it will be explained how to integrate historical traffic data into the valhalla graph. + +## Graph Association + +### With OSM IDs + +If the available traffic data is associated to OSM ways, these ways have to be mapped on valhallas internal graph ids to then add the traffic data to the routing graph. The valhalla tool `valhalla_ways_to_edges` can be used to generate a mapping from OSM way ids to the valhalla graph ids. An example for an `way_edges.txt` file created by the tool looks like this: +``` +1175181586,1,112642252344 +984719585,1,110964530744,1,112508034616,1,112843578936 +``` +The format of each row is `,[, ]`. Accordingly, the `osm_way_id` is mapped to multiple `graph_id`'s. An `graph_id` can be converted to the required string format according to the `to_string` function of the class `valhalla::baldr::GraphId`, whose implementation can be found in the valhalla repository. The result is a string of the following form `level/tile_id/id` (e.g. `1/47701/130`). + +## Historical Traffic + +Valhalla requires the historical speed information in following CSV file format: + +``` +edge_id,freeflow_speed,constrained_speed,historical_speeds +1/47701/130,50,40,AQ0AAAAAAA... +1/47701/131,50,40,AQ0AAAAAAA... +``` + +The first three columns are required to assign historical traffic to an edge, while the `historical_speeds` column is optional for providing more detailed speed information. The `edge_id` column represents the internal `graph_id` of ways in the valhalla graph. Check the previous section to see how your data can be mapped on these `graph_id`'s. The columns `freeflow_speed` represents the typical speed during night and `constrained_speed` is the typical speed during day (in km/h). + +The `historical_speeds` column contains more detailed information about how the traffic on a segment changes over the week. It requires the estimated speed (in km/h) of a road segment in 5 minute intervals covering a whole week. This leads to a total of 2016 speed values per road segment, which should start at Sunday 0:00. The `historical_speeds` column contains an DCT-II encoded version of the 2016 speed values. It can be obtained by using functions whose implementation can be found in the valhalla source code. First `compress_speed_buckets(const float* speeds)` can be called to obtain an array of coefficients. In a next step `encode_compressed_speeds(const int16_t* coefficients)` can be used to convert these to the string encoded version of the speed values, which can be put into the CSV-file. + +Finally, the speed of each way has to be put into the correct place in a folder hierarchy. This folder hierarch should look similar to valhalla’s tile hierarchy. An example tile hierarchy could look like this: +``` +. +|-- 0 +| `-- 003 +| `-- 015.gph +|-- 1 +| `-- 047 +| `-- 701.gph +``` + +The traffic folder structure, containing the traffic CSV files, should accordingly look like this: +``` +. +|-- 0 +| `-- 003 +| `-- 015.csv +|-- 1 +| `-- 047 +| `-- 701.csv +``` + +If a way's information is found in `0/003/015.gph`, then its matching speed values have to be saved in `0/003/015.csv`. To find the path in the tile hierarchy to save a speed value in, the function `GraphTile::FileSuffix`can be used to obtain the path from a `graph_id`. Its implementation can be found in the valhalla source code. + +In a last step, the traffic data has to be added to the routing graph. This can be done by using the `valhalla_add_predicted_traffic` tool. Its parameter `-t` can be used to hand over the folder which contains the traffic CSV files. \ No newline at end of file diff --git a/docs/docs/odin.md b/docs/docs/odin.md index 3970b46a5b..7994f46144 100644 --- a/docs/docs/odin.md +++ b/docs/docs/odin.md @@ -2,7 +2,7 @@ Odin serves as a directions engine for annotating a path as input from the [routing engine](https://github.com/valhalla/thor) for use in navigation. In keeping with the Norse mythological theme, the name [Odin](http://en.wikipedia.org/wiki/Odin) was chosen as he has often been noted as being very wise. Since the library deals mostly with providing, hopefully wise, guidance along a path to be used for navigation, this seemed like a fitting name! We've also managed to create a backronym out of Odin which stands for: Open Directions and Improved Narrative. -Odin contains a set of various data structures and alogrithms which deal with things like: maneuver generation, streetname matching and narrative generation. +Odin contains a set of various data structures and algorithms which deal with things like: maneuver generation, streetname matching and narrative generation. ## Components ## diff --git a/docs/docs/releasing.md b/docs/docs/releasing.md index c2f560516b..adcadff259 100644 --- a/docs/docs/releasing.md +++ b/docs/docs/releasing.md @@ -8,7 +8,7 @@ We are giving the following guarantees between versions: ### Major version change -- There are no guarantees about compatiblity of APIs or datasets +- There are no guarantees about compatibility of APIs or datasets - Breaking changes will be noted as `BREAKING` in the changelog ### Minor version change diff --git a/docs/docs/route_overview.md b/docs/docs/route_overview.md index 4c0eda9316..a7f8928b70 100644 --- a/docs/docs/route_overview.md +++ b/docs/docs/route_overview.md @@ -44,7 +44,7 @@ The route narrative/guidance generating code is located in the *odin* directory. - route_serializer.cc - **serializeDirections**(request, path_legs, directions_legs) → outputs a json string - toOSRM - Serialize route response in OSRM compatible format - - toJSON - Returns a trip object formated to JSON + - toJSON - Returns a trip object formatted to JSON - **jsonToProtoRoute**(json_route, proto_route) → used by navigator - Transfers the JSON route information returned from a route request into the Route pbf object passed in by reference. - To summarize, Valhalla builds a JSON “trip” object which contains the locations, route summary with basic info about the trip, a list of legs of the trip, status, units and language. diff --git a/docs/docs/sif.md b/docs/docs/sif.md index bba8075268..7cc2764ae4 100644 --- a/docs/docs/sif.md +++ b/docs/docs/sif.md @@ -2,7 +2,7 @@ Sif provides dynamic, extensible costing for edges and transitions between edges (turn costs). Its primary use is in the [routing engine](https://github.com/valhalla/thor) when forming the best path between locations. In keeping with the Norse mythological theme, the name [Sif](http://en.wikipedia.org/wiki/Sif) was chosen since Sif is a companion to Thor. -Sif is essentially a set of various data structures and alogrithms which deal with things like: correlating an input location to the underlying graph, partial distance along an edge and filtering edges which shouldn't be considered for correlation. +Sif is essentially a set of various data structures and algorithms which deal with things like: correlating an input location to the underlying graph, partial distance along an edge and filtering edges which shouldn't be considered for correlation. ## Components ## diff --git a/docs/docs/sif/dynamic-costing.md b/docs/docs/sif/dynamic-costing.md index 97c6d9bd98..d5634e879f 100644 --- a/docs/docs/sif/dynamic-costing.md +++ b/docs/docs/sif/dynamic-costing.md @@ -14,7 +14,7 @@ Valhalla uses dynamic, run-time costing when computing route paths and can consi #### Costing Interface -Costing methods have access to all attributes of an edge (road section between 2 intersections) to form the cost along the edge and when transitoning between edges. Within Sif, costing methods are created by deriving a class from the base dynamic costing class or one of the existing costing classes. Each costing method must override 3 different methods to create the unique costing logic: +Costing methods have access to all attributes of an edge (road section between 2 intersections) to form the cost along the edge and when transitioning between edges. Within Sif, costing methods are created by deriving a class from the base dynamic costing class or one of the existing costing classes. Each costing method must override 3 different methods to create the unique costing logic: virtual bool Allowed(const baldr::NodeInfo* node) const = 0; Checks if access is allowed for the provided node. For example, node access can be restricted for specific modes of travel if bollards are present. diff --git a/docs/docs/sif/elevation_costing.md b/docs/docs/sif/elevation_costing.md index 4f1aa46fa2..990f8630e5 100644 --- a/docs/docs/sif/elevation_costing.md +++ b/docs/docs/sif/elevation_costing.md @@ -18,7 +18,7 @@ The intuition is that steeper sections will require more "cost" to traverse whet The weighted grade is used within bicycle costing in 2 ways: -Weighted grade impacts the speed / resulting time along an edge. The default speed used for bicycle routing is assumed to be the average speed the bicyclist can mainatin on level grades over the length of the route. This speed is modulated based on the weighted grade: increased for grades indicating a descent, and decreased for uphill grades. The higher the weighting factor the steeper the "average" grade along the edge, resulting in a higher reduction of speed results. Thus, the weighted grade of an edge impacts the time along the edge and helps avoid steep grades. It also helps provide a beter estimate of the actual time along the route. In general, routes with hills take longer than routes on level ground. +Weighted grade impacts the speed / resulting time along an edge. The default speed used for bicycle routing is assumed to be the average speed the bicyclist can mainatin on level grades over the length of the route. This speed is modulated based on the weighted grade: increased for grades indicating a descent, and decreased for uphill grades. The higher the weighting factor the steeper the "average" grade along the edge, resulting in a higher reduction of speed results. Thus, the weighted grade of an edge impacts the time along the edge and helps avoid steep grades. It also helps provide a better estimate of the actual time along the route. In general, routes with hills take longer than routes on level ground. A second use for weighted grade considers the bicyclists desire to avoid or use hills in the route. A single option called use_hills has been added to the Valhalla bicycle costing module. This option is similar to the use_roads option. It is a value from 0 to 1.0 indicating the bicyclists comfort level with hills and steep grades. A value of 1.0 indicates a strong, experienced cyclists who does not mind a path with hills. A value of 0.0 indicates the cyclist wishes to find paths that try to avoid hills. Based on this fact, extra cost (penalties) are applied to edges based on the grade. Note that penalties are applied to downhill grades as well! The old adage among cyclists is that what goes down, eventually must go up so after every long downhill there will eventually be a long uphill! diff --git a/docs/docs/skadi.md b/docs/docs/skadi.md index 33c2ecccb1..046aa885f7 100644 --- a/docs/docs/skadi.md +++ b/docs/docs/skadi.md @@ -2,7 +2,7 @@ Skadi can be used to access digital elevation model data which is useful in computing steepness of edges in the route graph or generating an elevation profile along a computed route. In keeping with the Norse mythological theme, the jotunn/goddess [Skaði](http://en.wikipedia.org/wiki/Skaði) was chosen as she is associated, among other things, with the mountains. Since skadi deals mostly with extracting elevation data from various datasets, this seemed like a fitting name! -Skadi is essentially a set of various data structures and alogrithms which deal with things like: sampling elevation data in an adhoc or evenly sampled manner as well as serving elevation data in structured (json) or raw (geotiff) formats. +Skadi is essentially a set of various data structures and algorithms which deal with things like: sampling elevation data in an adhoc or evenly sampled manner as well as serving elevation data in structured (json) or raw (geotiff) formats. ## Components ## diff --git a/docs/docs/thor.md b/docs/docs/thor.md index 85c74ac3b0..3a40f10888 100644 --- a/docs/docs/thor.md +++ b/docs/docs/thor.md @@ -2,7 +2,7 @@ Thor serves as a routing engine backed by tiled open source routing data. Thor is a companion to Sif which it relies heavily on to determine the appropriate graph traversal. The resulting path can be used as input for creating guidance/narrative. The name Thor was chosen as an acronym standing for: Tiled Hierarchical Open Routing and was the foundational idea around which the organization Valhalla and its Norse mythology theme was formed. -The thor library is essentially a set of various data structures and alogrithms which deal with things like: A* graph traversal, edge costing, vertex costing and path construction. It also includes methods for computing time-distance matrices, optimized routing, and isochrones. +The thor library is essentially a set of various data structures and algorithms which deal with things like: A* graph traversal, edge costing, vertex costing and path construction. It also includes methods for computing time-distance matrices, optimized routing, and isochrones. ## Components ## diff --git a/docs/docs/thor/isochrones.md b/docs/docs/thor/isochrones.md index 07b370e79f..54d47675b3 100644 --- a/docs/docs/thor/isochrones.md +++ b/docs/docs/thor/isochrones.md @@ -9,7 +9,7 @@ In this image the green, yellow, orange and red contour lines represent 15, 30, How are Isochrone Maps Useful? ------------------------------ -Isochrone maps can be used to make informed decisions about travel at both an individual level and en masse. You can get quantitative answeres to questions like: +Isochrone maps can be used to make informed decisions about travel at both an individual level and en masse. You can get quantitative answers to questions like: * What are our lunch options within 5 minutes from here? * How much of the city lives within walking range of public transit? @@ -27,7 +27,7 @@ The 2-D grid is used to find the isocrhone contours by using a well-known contou After forming sets of contour polygons, KEVIN -please write a paragraph or 2 to describe how the contours are formed and output! -This 2-D grid of times can be useful for other purposes as well. It provides a very fast way to query a single location to see how long it takes to get there from the test location. Ultimately this could be a way to do very large one-to-many matrices. At this time we do not return the 2-D array of times, but this is a possibility in the future. +This 2-D grid can be useful for other purposes as well. It provides a very fast way to query a single location to see how long it takes to get there from the test location. Ultimately this could be a way to do very large one-to-many matrices. Where is it? ------------ diff --git a/docs/docs/thor/path-algorithm.md b/docs/docs/thor/path-algorithm.md index 6ac251e8f9..e18239064e 100644 --- a/docs/docs/thor/path-algorithm.md +++ b/docs/docs/thor/path-algorithm.md @@ -12,7 +12,7 @@ Thor uses several different algorithms to compute the least cost path. These alg #### A\* -The basic algorithm provided within Thor is an A\* algorithm. This algorithm searches in one direction - from the origin towards the destination. The A\* heuristic is added to the cost to help guide the search more rapidly towards the destination. The A\* method has been superceded for most cases by the bidirectional A\* algorithm which has better performance. Also, the A\* algorithm does not work as well with transitions to upper hierarchy levels as the path approaches the destination. +The basic algorithm provided within Thor is an A\* algorithm. This algorithm searches in one direction - from the origin towards the destination. The A\* heuristic is added to the cost to help guide the search more rapidly towards the destination. The A\* method has been superseded for most cases by the bidirectional A\* algorithm which has better performance. Also, the A\* algorithm does not work as well with transitions to upper hierarchy levels as the path approaches the destination. #### Bidirectional A\* @@ -70,7 +70,7 @@ EdgeStatus is constructed given an initial size of the edge status map. To avoid The AdjacencyList class provides a sorting order to the edge labels that are marked as temporary and are adjacent to edges that have lowest cost path found. The adjacency list uses a bucket sort implementation for performance. An "overflow" bucket is maintained to allow reduced memory use - costs outside the current bucket range get placed into the overflow bucket and are moved into the low-level buckets as needed. The adjacency list stores indexes into a list (vector) of labels where complete cost and predecessor information are stored. The adjacency list simply provides a fast sorting method. Benchmarks show a marked improvement over using an STL priority_queue, even in cases where the overflow bucket is utilized. -An AdjacencyList is contructed using a minimum cost (based on the A* heuristic distance from the origin location to the destination location), a range of costs held within the bucket sort, and a bucket size. All costs above mincost + range are stored in an "overflow" bucket. The following methods are provided in the AdjacencyList class: +An AdjacencyList is constructed using a minimum cost (based on the A* heuristic distance from the origin location to the destination location), a range of costs held within the bucket sort, and a bucket size. All costs above mincost + range are stored in an "overflow" bucket. The following methods are provided in the AdjacencyList class: - **Add** - Adds a label index to the sorted list. Adds it to the appropriate bucket given the sort cost. If the sortcost is greater than maxcost_ the label is placed in the overflow bucket. If the sortcost is < the current bucket cost then the label is placed at the front of the current bucket (this prevents underflow). - **DecreaseCost** - The specified label index now has a smaller cost. Reorders it in the sorted bucket list. diff --git a/docs/docs/thor/simple_traffic.md b/docs/docs/thor/simple_traffic.md deleted file mode 100644 index 94e2b12c5e..0000000000 --- a/docs/docs/thor/simple_traffic.md +++ /dev/null @@ -1,72 +0,0 @@ -##Traffic Influenced Routing - Proof of Concept - -While Valhalla does not support traffic influenced routing at the current time, the tiled routing graph design and dynamic costing methods used by Valhalla should readily support integration of both real-time and historical traffic or speed information. This paper describes a proof of concept that was developed to demonstrated traffic-influenced routing. The proof of concept allows entry of speeds for a set of OpenStreetMap (OSM) ways. These ways are then correlated with Valhalla graph edges to generate a set of "speed tiles". These speed tiles can be thought of as "lookaside" speed tables which are used by Valhalla dynamic costing methods to produce traffic-influenced routes. The proof of concept shows how route paths change and estimated times for the route increase in the presence of congestion. - -#####Speed Tiles -A key method used in the proof of concept is to create speed tiles that correlate to the existing Valhalla graph edges. The speed data is stored in a 1:1 correlation to each graph edge. This allows easy and efficient access of speed data using the existing Valhalla graph Ids that index each graph edge. Storing dynamic speed data separately from the static graph tile data also allows the dynamic speed information to be read, cached, and updated separately without impacting the more static routing tiles. The dynamic speed data is much smaller than the static graph tiles and can readily be updated and read as new routes are created. Valhalla graph tiles can remain cached. - -Speeds can be represented using a single byte per graph edge. This allows speeds from 0 to 255 kph. A specific value (e.g., 0) is used to indicate that no real-time speed exists for the edge and that the speed must be read from the Valhalla graph tile, which maintains a speed for each graph edge derived from the OSM max_speed tag or approximated based on the OSM highway tag. - -The proof of concept only considered real-time speed information which meant only a single speed is maintained for each edge. Historical speed data could also be supported as a set of speeds for specific time periods for each graph edge. For example, 168 different speed values could be stored to indicate the average speed along a road segment for each hour of the week. Historical speed data would be more static - it would not be updated every several minutes but could be read in and cached just as the Valhalla graph tiles are. Historical speed data can be used to provide time-dependent speed information that shows expected traffic patterns like rush hour commuting patterns vs. mid-day weekend traffic patterns. - -#####Associating Way Ids to Valhalla Edges - -One possible means of specifying speed or traffic information is to associate a current speed to an OSM way. This provides an easy method of adding speed data to Valhalla. An association of way Ids to Valhalla graph Ids was created for the traffic proof of concept. This was stored as a simple CSV (comma separated values) file listing the OSM way Id and the Valhalla graph Ids of the directed edges and their direction (forward or backward) along the way. A simple process was created to read a CSV file of way Ids with a forward direction speed and a reverse direction speed along the way. This process associated the way Ids to Valhalla directed edges and stored the corresponding speeds in a real-time speed file for each Valhalla tile where edges had real-time speeds were specified. This Valhalla real-time speed tile simply stores an array of speeds in a one to one correlation to the directed edges in the tile. If a directed edge did not have any speed assigned (the majority of edges) then a value of 0 was used to indicate no speed exists. Using real-time speed tiles in this manner allows the real-time speed to be accessed using the same Valhalla graph Id as the directed edge. - -For the proof of concept the process that assigns speed to OSM ways, and thus Valhalla graph edges, was executed prior to running the Valhalla server so that the real-time speeds were populated and usable by Valhalla routing. Caching of the real-time speed data was implemented only on a per route basis. This meant that each new route would load real-time speed tiles that it needed. This allowed the proof of concept to inject updated speed information and then re-run a route and see the impact of the new or additional real-time speeds. The proof of concept involved a manual entry of speed information, it was in no way automated or using real traffic data. The purpose was to prove a method of influencing Valhalla routes using auxiliary speed tiles. Methods to ingest traffic information and automatically create speed tiles was outside the scope of the proof of concept. - -The downside of using OSM way Ids for traffic specification is that OSM ways can very long or very short. This can lead to difficulties when trying to localize congestion. OSM ways are often defined in such a way that manual addition or editing of a road is simplified. They often span many intersections or a long stretch of highway. When this happens, a single speed will become assigned to many graph edges and there is no way to represent variaiblity of speed along the way. Conversely, OSM ways can be very short and represent only a small portion of a road between two intersections or can represent a small overpass on a highway. In this case it becomes difficult to assign speeds to many of these short OSM ways, leading to gaps in speed coverage. - -#####Dynamic Costing with Traffic - -Valhalla uses dynamic, run-time costing when computing route paths. Costing methods often use speed and edge length to compute time as the costing parameter and thus create least-time routes. Currently the speed used in these computations comes from OSM max_speed tags or from a speed assigned based on highway tags (if no max_speed tag is present). A custom, dynamic costing method was created for the traffic proof of concept. This costing method uses real-time speeds if available and falls back to the OSM assigned speeds if not available. - -The proof of concept did not consider how to handle edge transition costs. These are costs to traverse intersections and are used to approximate time spent stopped or waiting at intersections. With real-time traffic information many of these transition costs become part of the real-time speed for a segment of road that may traverse several intersections. It is likely that when good real-time speed coverage is available that the edge transitions costing will need to be updated to lessen the impact of transition costs and thus rely more on the real-time speed data. - -####Example -The following images show an example of traffic influenced routing with Valhalla using the proof of concept described above. The examples below all use a former commuting route that I took from Elkridge, Maryland to the Applied Physics Laboratory near Laurel, Maryland. - -The first image shows the route without any traffic influence. The main part of the path takes I-95 South to MD-32 West to US-29 South with an estimated time of 17 minutes. - -Using the OSM way Ids along I-95 South, I created real-time speed tiles that set a current speed of 45 kph along I-95 South. With the dynamic costing method using the adjusted real-time speed data along I-95 the resulting new route detoured to take US-1 South to MD-32 West to US-29 South. US-1 has a lower speed limit and more intersections, so this route takes an estimated 19 minutes. This is shown in Figure 2. - -Using the OSM way Ids along US-1 South, I added real-time speeds of 40 kph along US-1 in addition to the reduced speeds along I-95 South. With the updated real-time speed tiles and dynamic costing the new route follows local roads to MD-100 West to US-29 South. This route takes and estimated 22 minutes and is longer distance as well. - -These examples show what you would expect as traffic worsens - the optimal route path may become longer distance and may take some lower class roads to find a detour. The time to reach the destination increases as the initial shortest time path becomes slower due to traffic. - -![Traffic0](images/no_traffic.png "Without Traffic") - -Figure 1; Commute Route without Real-Time Traffic - -![Traffic1](images/with_traffic1.png "With Traffic 1") - -Figure 2: Commute Route with Reduced Real-Time Speeds along I-95 South - -![Traffic1](images/with_traffic2.png "With Traffic 2") - -Figure 3: Commute Route with Reduced Real-Time Speeds along I-95 South and US-1 South - -####Suitability of Valhalla for Traffic Integration - -Valhalla's tiled data structures should work well for potential traffic integration. First and foremost, tiling allows for efficient distribution of regional sets of speed data and also allows distributed processing of traffic data based on tiles. The concept of "look-aside" speed tiles has several advantages: - -- **Easy and rapid access** using the same indexes as the Valhalla graph edges. -- **Small data size for real-time speeds.** This is crucial since real-time speeds will need to be updated frequently to provide a robust and current routing solution in presence of traffic. Latency and delay in updating current speeds needs to be kept to a minimum in a responsive traffic-influenced routing system. Small data size for dynamic speed data also makes for more efficient access during route computation as less data needs to be read from disk and cached. - -The one major disadvantage of the stratefy of using look-aside speed tiles is that the speed tiles need to be matched to a specific Valhalla routing tile data set. Valhalla graph Ids are not persistent and depend on the data import process, so speed tiles need to be matched to a specific Valhalla data set so that indexes (graph Ids) match. - -Work remains to be done to produce production-worthy, traffic-influenced routing within Valhalla. Here are some considerations and possibilities that we are considering. - -#####Highway Hierarchies -Valhalla creates highway hierarchies in a manner similar to how roads are often presented at different zoom-levels in a map. The local hierarchy corresponds to the highest zoom levels where all roads and paths are stored or displayed. The arterial hierarchy removes residential roads, service roads, cycleways, walking paths, and other lower-class roads not generally used except when near the route origin or destination location. This is similar to a map at a middle zoom level. The highway hierarchy only includes motorways, trunks, and (currently) primary roads. This is similar to the lower zoom levels of a map where only important, higher classification roads are shown. In addition to the grouping of graph nodes and edges into the highway hierarchy, the arterial and highway hierarchy levels also contain "shortcut edges" that bypass any nodes that only connect to lower hierarchy level edges. This creates efficiency when computing long routes. - -For the proof of concept, real-time speeds were only assigned to edges on the local graph hierarchy and thus were not assigned to shortcut edges. Since shortcut edges generally connect or combine edges with different OSM way Ids, the mapping of way Ids to Valhalla graph edges becomes more complicated. We have left this mapping for future efforts if needed. Alternatively, a referencing system such as OpenLR might be used to associate traffic information to Valhalla routing edges. This has many advantages which we expect to investigate in the future. - -#####Possible Optimizations Using Highway Hierarchies -We are considering a shift in the way Valhalla stores the routing graph in separate highway hierarchies. There is currently duplication of edges across different highway hierarchies. For example, a motorway edge is stored in all 3 hierarchies: local, arterial, and highway. This has some nice properties when used in a bidirectional, A* algorithm. In particular, the search paths only need to transition upwards in the hierarchy (from local to arterial to highway) and never needs to transition downwards. The 2 search paths meet in the middle, usually on the highway hierarchy for any driving route. Pedestrian and bicycle routes never transition upwards, they only traverse the local hierarchy. - -If Valhalla were to store each edge only on the hierarchy level which it lies then this duplication would be removed. This reduces the total size of the Valhalla graph tiles and should lead to reduced memory use as well. For traffic integration this may have even more impact. If speed data is only provided for the arterial and highway hierarchies this would greatly reduce the number of traffic tiles required to support traffic and also would reduce the total size of any speed lookaside tiles. This seems like a reasonable assumption - residential roads, service roads, parking areas, and especially cycleways and walkways do not generally need traffic data as speeds are usually consistent and traffic volumes are usually too low to generate enough probe data to get meaningful traffic information. - -This type of change to the Valhalla routing graph will need to be developed, validated, and tested to make sure routing performance and quality remains high. However, this idea seems promising and is worth pursuing as we move forward with traffic investigation and integration. - -Keep watch for traffic integration work in the future! diff --git a/docs/docs/tyr.md b/docs/docs/tyr.md index 12e41ff523..b13ad22d87 100644 --- a/docs/docs/tyr.md +++ b/docs/docs/tyr.md @@ -1,6 +1,6 @@ # Tyr -Tyr is a service layer taking locations and options as input and returning a route and maneuvers as output essentially linking together all other projects under the valhalla organization. In keeping with the Norse mythological theme, the name Tyr was chosen as backcronym standing for: Take Your Route. Since this software deals mostly with providing routes based on http requests, this seemed like a fitting name! Tyr is essentially a set of various data structures and alogrithms which deal with things like: data marshalling, http, request parsing, response serializing, and interprocess communication. +Tyr is a service layer taking locations and options as input and returning a route and maneuvers as output essentially linking together all other projects under the valhalla organization. In keeping with the Norse mythological theme, the name Tyr was chosen as backcronym standing for: Take Your Route. Since this software deals mostly with providing routes based on http requests, this seemed like a fitting name! Tyr is essentially a set of various data structures and algorithms which deal with things like: data marshalling, http, request parsing, response serializing, and interprocess communication. ## Components ## diff --git a/docs/docs/tzdb_update.md b/docs/docs/tzdb_update.md new file mode 100644 index 0000000000..1e052e87b9 --- /dev/null +++ b/docs/docs/tzdb_update.md @@ -0,0 +1,31 @@ +## Updating the timezone database + +The timezone information is coming from 2 repositories: +- [eggert/tz](https://github.com/eggert/tz): Contains the IANA rules and prepares releases for each IANA release. We have this repo submodule'd and configure the files via CMake. +- [evansiroky/timezone-boundary-builder](https://github.com/evansiroky/timezone-boundary-builder): Contains the geometries of IANA timezones, mostly sourced from OSM. Releases comprise the `timezones-with-oceans.shapefile.zip` shapefile which we use to build our timezone SQLite database. + +Updating the timezone information regularly is paramount to keep up with: +- renamed/merged timezones, which implicitly deprecates (but still preserves) old timezones +- geometry changes of timezones +- entirely new timezones which are carved out due to local/regional DST changes +- DST changes of existing timezones + +DST changes are the most important reason to update regularly. + +### Update process + +1. Update the `tz` submodule + ``` + git -C third_party/tz checkout + ``` +2. Update `scripts/valhalla_build_timezones` to download the latest release +3. Run `datetime` test. If any timezones were merged/renamed, it'll fail with a pretty-print of new/old elements for copy/pasting convenience. However, if entirely new timezones were added, there's more manual work: +- identify which timezone the new one is carved out of (look into the submoduled `tz` repo's NEWS) +- if the parent timezone has an ID < 387, it just needs a bit shift of the parent timezone +- if the parent timezone is itself a previously added new timezone, one might add another field to NodeInfo; follow the instructions in baldr/nodeinfo.h/cc +4. Sanity check step 3! Both looking at the release notes of the IANA data and adding test cases. +### Important notes +- The 2 repos we source our information from are not always in-sync. If there's a IANA release being skipped in the boundary builder project, it usually means that nothing changed wrt geometries. However, you'll need to verify that. +- Regarding data compatibility: + - **new code/old data**: the algorithms can ask for nodes' timezone indices of potentially deprecated timezones (old tile data), and it'll receive the new timezone information, which is not a problem + - **old code/new data**: for entirely new timezones, the new data has an additional field in NodeInfo which tells new code that it's a new timezone, while old code continues to see the new timezone's parent. \ No newline at end of file diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index df1bcf5b9a..711ac0aa0b 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -45,11 +45,12 @@ markdown_extensions: - pymdownx.tasklist: custom_checkbox: true - pymdownx.emoji: - emoji_index: !!python/name:materialx.emoji.twemoji - emoji_generator: !!python/name:materialx.emoji.to_svg + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg site_name: Valhalla Docs repo_url: https://github.com/valhalla/valhalla/ +edit_uri: edit/master/docs/docs/ site_url: https://valhalla.github.io/valhalla docs_dir: docs nav: @@ -81,6 +82,7 @@ nav: - Elevation influenced bicycle routing: sif/elevation_costing.md - elevation.md - testing.md + - tzdb_update.md - Internal components: - Baldr (routing data strutures/algorithms): baldr.md - Loki (associate locations with graph edges): loki.md @@ -104,11 +106,11 @@ nav: - mjolnir/map_roulette.md - mjolnir/map_roulette_blog.md - mjolnir/tag_parsing.md + - mjolnir/historical_traffic.md - Thor (routing algorithms): - thor.md - thor/isochrones.md - thor/path-algorithm.md - - thor/simple_traffic.md - incidents.md - Data sources: - Data sources: mjolnir/data_sources.md diff --git a/docs/requirements.txt b/docs/requirements.txt index 754d1f7c2f..eaaf4864e7 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1 +1 @@ -mkdocs-material>=8.5, <9.0 \ No newline at end of file +mkdocs-material<10.0 \ No newline at end of file diff --git a/include/.keep b/include/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/libvalhalla.pc.in b/libvalhalla.pc.in index 45391b8310..886b7fdf93 100644 --- a/libvalhalla.pc.in +++ b/libvalhalla.pc.in @@ -1,10 +1,12 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ -libdir=${exec_prefix}/@libdir@ -includedir=${prefix}/@includedir@ +libdir=@libdir@ +includedir=@includedir@ Name: libvalhalla Description: valhalla c++ library Version: @VERSION@ -Libs: -L${libdir} -lvalhalla @deplibs@ -Cflags: -I${includedir} +Libs: -L${libdir} -lvalhalla +Libs.private: @LIBS_PRIVATE@ +Requires: @REQUIRES@ +Cflags: -I${includedir} @CFLAGS@ diff --git a/locales/bg-BG.json b/locales/bg-BG.json index 622eb709d4..4529332389 100644 --- a/locales/bg-BG.json +++ b/locales/bg-BG.json @@ -45,7 +45,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -85,7 +88,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -143,7 +149,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " километра", @@ -229,7 +241,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ферибот", "example_phrases": { @@ -395,7 +413,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ферибот", "example_phrases": { @@ -448,7 +469,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -949,7 +985,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -984,7 +1023,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -1023,7 +1065,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -1076,7 +1121,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ "километри", @@ -1399,7 +1453,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -1439,7 +1496,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -1495,7 +1555,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ "километри", @@ -1958,7 +2024,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -1998,7 +2067,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -2040,7 +2112,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -2088,7 +2163,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/ca-ES.json b/locales/ca-ES.json index bac124f069..ca5f2d042d 100644 --- a/locales/ca-ES.json +++ b/locales/ca-ES.json @@ -45,7 +45,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -85,7 +88,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -143,7 +149,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilòmetres", @@ -229,7 +241,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -395,7 +413,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -448,7 +469,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -949,7 +985,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -984,7 +1023,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -1023,7 +1065,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -1076,7 +1121,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilòmetres", @@ -1399,7 +1453,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -1439,7 +1496,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -1495,7 +1555,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilòmetres", @@ -1958,7 +2024,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -1998,7 +2067,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -2040,7 +2112,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -2088,7 +2163,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/cs-CZ.json b/locales/cs-CZ.json index f0d29224c7..d5d42b66d9 100644 --- a/locales/cs-CZ.json +++ b/locales/cs-CZ.json @@ -45,7 +45,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -85,7 +88,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -143,7 +149,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrů", @@ -229,7 +241,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "chodník", "tcyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Trajekt", "example_phrases": { @@ -395,7 +413,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Trajekt", "example_phrases": { @@ -448,7 +469,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -949,7 +985,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -984,7 +1023,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -1023,7 +1065,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -1076,7 +1121,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrů", @@ -1399,7 +1453,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -1439,7 +1496,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -1495,7 +1555,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrů", @@ -1958,7 +2024,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -1998,7 +2067,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -2040,7 +2112,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -2088,7 +2163,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/da-DK.json b/locales/da-DK.json index 94fad718ac..c8618f3764 100644 --- a/locales/da-DK.json +++ b/locales/da-DK.json @@ -45,7 +45,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -85,7 +88,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -143,7 +149,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -229,7 +241,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Færge", "example_phrases": { @@ -395,7 +413,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Færge", "example_phrases": { @@ -448,7 +469,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -949,7 +985,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -984,7 +1023,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1023,7 +1065,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1076,7 +1121,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -1399,7 +1453,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1439,7 +1496,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1495,7 +1555,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -1958,7 +2024,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1998,7 +2067,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -2040,7 +2112,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -2088,7 +2163,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/de-DE.json b/locales/de-DE.json index e3e79d9966..750d55a283 100644 --- a/locales/de-DE.json +++ b/locales/de-DE.json @@ -45,7 +45,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -85,7 +88,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -143,7 +149,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " Kilometer", @@ -229,7 +241,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Fähre", "example_phrases": { @@ -395,7 +413,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Fähre", "example_phrases": { @@ -448,7 +469,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -949,7 +985,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -984,7 +1023,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -1023,7 +1065,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -1076,7 +1121,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " Kilometer", @@ -1399,7 +1453,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -1439,7 +1496,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -1495,7 +1555,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " Kilometer", @@ -1958,7 +2024,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -1998,7 +2067,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -2040,7 +2112,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -2088,7 +2163,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Den Aufzug nehmen.", diff --git a/locales/el-GR.json b/locales/el-GR.json index 74d56c4824..a0f3efd415 100644 --- a/locales/el-GR.json +++ b/locales/el-GR.json @@ -45,7 +45,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -85,7 +88,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -143,7 +149,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " χιλιόμετρα", @@ -229,7 +241,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Πλοίο", "example_phrases": { @@ -395,7 +413,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Πλοίο", "example_phrases": { @@ -448,7 +469,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -949,7 +985,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -984,7 +1023,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -1023,7 +1065,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -1076,7 +1121,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " χιλιόμετρα", @@ -1399,7 +1453,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -1439,7 +1496,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -1495,7 +1555,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " χιλιόμετρα", @@ -1958,7 +2024,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -1998,7 +2067,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -2040,7 +2112,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -2088,7 +2163,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/en-GB.json b/locales/en-GB.json index b68ea280d4..9e27c98552 100644 --- a/locales/en-GB.json +++ b/locales/en-GB.json @@ -43,7 +43,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -83,7 +86,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -141,7 +147,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -173,7 +182,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometres", @@ -227,7 +239,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -364,7 +379,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -393,7 +411,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -446,7 +467,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -543,7 +567,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -723,7 +750,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -751,7 +781,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -900,7 +933,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -947,7 +983,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -982,7 +1021,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1021,7 +1063,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1074,7 +1119,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1114,7 +1162,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1161,7 +1212,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometres", @@ -1397,7 +1451,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1437,7 +1494,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1493,7 +1553,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1581,7 +1644,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometres", @@ -1956,7 +2022,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1996,7 +2065,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -2038,7 +2110,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -2086,7 +2161,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -2171,6 +2249,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the lift.", diff --git a/locales/en-US-x-pirate.json b/locales/en-US-x-pirate.json index 826e3ca309..db2aee4b63 100644 --- a/locales/en-US-x-pirate.json +++ b/locales/en-US-x-pirate.json @@ -45,7 +45,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -85,7 +88,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -143,7 +149,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometers", @@ -229,7 +241,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -395,7 +413,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -448,7 +469,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -949,7 +985,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -984,7 +1023,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1023,7 +1065,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1076,7 +1121,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometers", @@ -1399,7 +1453,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1439,7 +1496,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1495,7 +1555,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometers", @@ -1958,7 +2024,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "port", @@ -1998,7 +2067,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "port", @@ -2040,7 +2112,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "port", @@ -2088,7 +2163,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "port", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/en-US.json b/locales/en-US.json index fbd1eb226e..88630241a0 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -45,7 +45,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -85,7 +88,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -143,7 +149,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometers", @@ -229,7 +241,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -395,7 +413,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -448,7 +469,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -949,7 +985,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -984,7 +1023,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1023,7 +1065,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1076,7 +1121,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometers", @@ -1399,7 +1453,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1439,7 +1496,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1495,7 +1555,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometers", @@ -1958,7 +2024,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1998,7 +2067,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -2040,7 +2112,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -2088,7 +2163,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/es-ES.json b/locales/es-ES.json index 3599b3b4b2..d50d8e3468 100644 --- a/locales/es-ES.json +++ b/locales/es-ES.json @@ -45,7 +45,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -85,7 +88,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -143,7 +149,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilómetros", @@ -229,7 +241,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferri", "example_phrases": { @@ -395,7 +413,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferri", "example_phrases": { @@ -448,7 +469,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -949,7 +985,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -984,7 +1023,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -1023,7 +1065,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -1076,7 +1121,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilómetros", @@ -1399,7 +1453,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -1439,7 +1496,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -1495,7 +1555,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilómetros", @@ -1958,7 +2024,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -1998,7 +2067,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -2040,7 +2112,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -2088,7 +2163,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Tome el ascensor.", diff --git a/locales/et-EE.json b/locales/et-EE.json index b1b1f1e102..1b186c2829 100644 --- a/locales/et-EE.json +++ b/locales/et-EE.json @@ -45,7 +45,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -85,7 +88,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -143,7 +149,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilomeetrit", @@ -229,7 +241,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Praam", "example_phrases": { @@ -395,7 +413,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Praam", "example_phrases": { @@ -448,7 +469,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakule", @@ -949,7 +985,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakule", @@ -984,7 +1023,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakule", @@ -1023,7 +1065,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -1076,7 +1121,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilomeetrit", @@ -1399,7 +1453,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -1439,7 +1496,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -1495,7 +1555,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilomeetrit", @@ -1958,7 +2024,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -1998,7 +2067,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -2040,7 +2112,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -2088,7 +2163,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Astu lifti.", diff --git a/locales/fi-FI.json b/locales/fi-FI.json index 8b06698a55..6b96240faf 100644 --- a/locales/fi-FI.json +++ b/locales/fi-FI.json @@ -45,7 +45,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalle", @@ -85,7 +88,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalle", @@ -143,7 +149,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometriä", @@ -229,7 +241,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Lautta", "example_phrases": { @@ -395,7 +413,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Lautta", "example_phrases": { @@ -448,7 +469,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalla", @@ -949,7 +985,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalla", @@ -984,7 +1023,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalla", @@ -1023,7 +1065,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalla", @@ -1076,7 +1121,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometriä", @@ -1399,7 +1453,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalle", @@ -1439,7 +1496,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalle", @@ -1495,7 +1555,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometriä", @@ -1958,7 +2024,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalle", @@ -1998,7 +2067,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalle", @@ -2040,7 +2112,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalle", @@ -2088,7 +2163,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalle", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/hi-IN.json b/locales/hi-IN.json index 1614c4753e..aa2e01035d 100644 --- a/locales/hi-IN.json +++ b/locales/hi-IN.json @@ -45,7 +45,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -85,7 +88,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -143,7 +149,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " किलोमीटर", @@ -229,7 +241,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -395,7 +413,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -448,7 +469,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -949,7 +985,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -984,7 +1023,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -1023,7 +1065,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -1076,7 +1121,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " किलोमीटर", @@ -1399,7 +1453,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -1439,7 +1496,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -1495,7 +1555,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " किलोमीटर", @@ -1958,7 +2024,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -1998,7 +2067,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -2040,7 +2112,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -2088,7 +2163,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/hu-HU.json b/locales/hu-HU.json index 8fa76dfb84..fc97c5a708 100644 --- a/locales/hu-HU.json +++ b/locales/hu-HU.json @@ -45,7 +45,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpár út", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -85,7 +88,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -143,7 +149,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilométerek", @@ -229,7 +241,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Komp", "example_phrases": { @@ -395,7 +413,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Komp", "example_phrases": { @@ -448,7 +469,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "a sétány", "kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "a sétány", "a kerékpárút", "a hegyi kerkpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -949,7 +985,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -984,7 +1023,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -1023,7 +1065,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -1076,7 +1121,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilométerek", @@ -1399,7 +1453,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -1439,7 +1496,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -1495,7 +1555,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilométerek", @@ -1958,7 +2024,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -1998,7 +2067,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -2040,7 +2112,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -2088,7 +2163,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Menjen a lifttel.", diff --git a/locales/it-IT.json b/locales/it-IT.json index 70e066b1d0..2d3029f362 100644 --- a/locales/it-IT.json +++ b/locales/it-IT.json @@ -45,7 +45,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -85,7 +88,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -143,7 +149,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ "chilometri", @@ -229,7 +241,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Traghetto", "example_phrases": { @@ -395,7 +413,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Traghetto", "example_phrases": { @@ -448,7 +469,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -949,7 +985,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -984,7 +1023,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -1023,7 +1065,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -1076,7 +1121,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " chilometri", @@ -1399,7 +1453,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -1439,7 +1496,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -1495,7 +1555,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " chilometri", @@ -1958,7 +2024,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -1998,7 +2067,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -2040,7 +2112,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -2088,7 +2163,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/ja-JP.json b/locales/ja-JP.json index 6f88ff2ce2..54042425d9 100644 --- a/locales/ja-JP.json +++ b/locales/ja-JP.json @@ -45,7 +45,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -85,7 +88,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -143,7 +149,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ "キロメートル", @@ -229,7 +241,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "フェリー", "example_phrases": { @@ -395,7 +413,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "フェリー", "example_phrases": { @@ -448,7 +469,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -949,7 +985,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -984,7 +1023,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -1023,7 +1065,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -1076,7 +1121,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ "キロメートル", @@ -1399,7 +1453,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -1439,7 +1496,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -1495,7 +1555,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ "キロメートル", @@ -1958,7 +2024,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -1998,7 +2067,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -2040,7 +2112,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -2088,7 +2163,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/nb-NO.json b/locales/nb-NO.json index 059d7aa923..1a4f42b530 100644 --- a/locales/nb-NO.json +++ b/locales/nb-NO.json @@ -45,7 +45,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -85,7 +88,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -143,7 +149,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -229,7 +241,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferje", "example_phrases": { @@ -395,7 +413,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferje", "example_phrases": { @@ -448,7 +469,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -949,7 +985,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -984,7 +1023,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1023,7 +1065,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1076,7 +1121,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -1399,7 +1453,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1439,7 +1496,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1495,7 +1555,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -1958,7 +2024,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1998,7 +2067,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -2040,7 +2112,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -2088,7 +2163,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/nl-NL.json b/locales/nl-NL.json index 63fd829162..20597df4b1 100644 --- a/locales/nl-NL.json +++ b/locales/nl-NL.json @@ -45,7 +45,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -85,7 +88,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -143,7 +149,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -229,7 +241,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Veerboot", "example_phrases": { @@ -395,7 +413,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Veerboot", "example_phrases": { @@ -448,7 +469,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -949,7 +985,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -984,7 +1023,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -1023,7 +1065,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -1076,7 +1121,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -1399,7 +1453,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -1439,7 +1496,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -1495,7 +1555,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -1958,7 +2024,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -1998,7 +2067,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -2040,7 +2112,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -2088,7 +2163,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Neem de lift.", diff --git a/locales/pl-PL.json b/locales/pl-PL.json index 22ed49ee0e..8809846f69 100644 --- a/locales/pl-PL.json +++ b/locales/pl-PL.json @@ -45,7 +45,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "w lewo", @@ -85,7 +88,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "w lewo", @@ -143,7 +149,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "metric_lengths": [ " km", @@ -229,7 +241,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "ferry_label": "prom", "example_phrases": { @@ -395,7 +413,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "ferry_label": "prom", "example_phrases": { @@ -448,7 +469,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "lewej strony", @@ -949,7 +985,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "lewej strony", @@ -984,7 +1023,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "lewej strony", @@ -1023,7 +1065,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "lewej strony", @@ -1076,7 +1121,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "metric_lengths": [ " km", @@ -1399,7 +1453,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "w lewo", @@ -1439,7 +1496,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "w lewo", @@ -1495,7 +1555,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "metric_lengths": [ " km", @@ -1948,8 +2014,8 @@ "turn": { "phrases": { "0": "Skręć .", - "1": "Skręć w stronę: .", - "2": "Skręć w stronę: . Kontynuuj wzdłuż: .", + "1": "Skręć w .", + "2": "Skręć w . Kontynuuj wzdłuż: .", "3": "Skręć , pozostając na: .", "4": "Skręć na .", "5": "Skręć , kierunek: ." @@ -1958,7 +2024,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "w lewo", @@ -1988,8 +2057,8 @@ "turn_verbal": { "phrases": { "0": "Skręć .", - "1": "Skręć w stronę: .", - "2": "Skręć w stronę: .", + "1": "Skręć w .", + "2": "Skręć w .", "3": "Skręć , pozostając na: .", "4": "Skręć na .", "5": "Skręć , kierunek: ." @@ -1998,7 +2067,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "w lewo", @@ -2028,7 +2100,7 @@ "uturn": { "phrases": { "0": "Zawróć .", - "1": "Zawróć w stronę: .", + "1": "Zawróć w .", "2": "Zawróć , pozostając na: .", "3": "Zawróć na: .", "4": "Zawróć na: w stronę: .", @@ -2040,7 +2112,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "w lewo", @@ -2076,7 +2151,7 @@ "uturn_verbal": { "phrases": { "0": "Zawróć .", - "1": "Zawróć w stronę: .", + "1": "Zawróć w .", "2": "Zawróć , pozostając na: .", "3": "Zawróć na: .", "4": "Zawróć na: w stronę: .", @@ -2088,7 +2163,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "w lewo", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Przejdź .", + "1": "Przejdź na światłach ." + }, + "object_labels": [ + "bramę", + "słupki", + "skrzyżowanie dróg" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Skorzystaj z windy.", @@ -2180,10 +2274,10 @@ }, "example_phrases": { "0": [ - "Take the elevator." + "Wsiądź do windy." ], "1": [ - "Take the elevator to Level 1." + "Wsiądź do windy i jedź na poziom 1." ] } }, @@ -2194,10 +2288,10 @@ }, "example_phrases": { "0": [ - "Take the stairs." + "Wejdź na schody." ], "1": [ - "Take the stairs to Level 2." + "Przejdź schodami na poziom 2." ] } }, diff --git a/locales/pt-BR.json b/locales/pt-BR.json index 67e198ae8b..019f6fabac 100644 --- a/locales/pt-BR.json +++ b/locales/pt-BR.json @@ -43,7 +43,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -83,7 +86,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -141,7 +147,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -173,7 +182,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilômetros", @@ -227,7 +239,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -364,7 +379,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Balsa", "example_phrases": { @@ -393,7 +411,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Balsa", "example_phrases": { @@ -446,7 +467,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -543,7 +567,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -723,7 +750,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -751,7 +781,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -900,7 +933,10 @@ "a estrada pedonal", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -947,7 +983,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -982,7 +1021,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -1021,7 +1063,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -1074,7 +1119,10 @@ "calçada", "ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1114,7 +1162,10 @@ "calçada", "ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1161,7 +1212,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilômetros", @@ -1397,7 +1451,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -1437,7 +1494,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -1493,7 +1553,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1581,7 +1644,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilômetros", @@ -1956,7 +2022,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -1996,7 +2065,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -2038,7 +2110,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -2086,7 +2161,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -2171,6 +2249,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/pt-PT.json b/locales/pt-PT.json index c580c9e10c..8b5300fcde 100644 --- a/locales/pt-PT.json +++ b/locales/pt-PT.json @@ -45,7 +45,10 @@ "o passeio", "a ciclovia", "o trilho para bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -85,7 +88,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -143,7 +149,10 @@ "o passeio", "a ciclovia", "o trilho de bibicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilómetros", @@ -229,7 +241,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -395,7 +413,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -448,7 +469,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -949,7 +985,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -984,7 +1023,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -1023,7 +1065,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -1076,7 +1121,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilómetros", @@ -1399,7 +1453,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -1439,7 +1496,10 @@ "o passeio", "a ciclovia", "o trilho de bicileta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -1495,7 +1555,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilómetros", @@ -1958,7 +2024,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -1998,7 +2067,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -2040,7 +2112,10 @@ "o passeio", "a ciclovia", "o trilho de bicilceta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -2088,7 +2163,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/ro-RO.json b/locales/ro-RO.json index cc66be3a12..aab7bcfe66 100644 --- a/locales/ro-RO.json +++ b/locales/ro-RO.json @@ -45,7 +45,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "trecerea de pietoni" + "trecerea de pietoni", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -85,7 +88,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "trecerea de pietoni" + "trecerea de pietoni", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -143,7 +149,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "trecerea de pietoni" + "trecerea de pietoni", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "trecerea de pietoni" + "trecerea de pietoni", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometri", @@ -229,7 +241,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Feribot", "example_phrases": { @@ -395,7 +413,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Feribot", "example_phrases": { @@ -448,7 +469,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "alee", "pistă de biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "alee", "pistă de biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -949,7 +985,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -984,7 +1023,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -1023,7 +1065,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -1076,7 +1121,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometri", @@ -1399,7 +1453,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -1439,7 +1496,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -1495,7 +1555,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "alee", "pistă de biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometri", @@ -1958,7 +2024,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -1998,7 +2067,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -2040,7 +2112,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -2088,7 +2163,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/ru-RU.json b/locales/ru-RU.json index 52c6f3fe1c..e1dcdecacb 100644 --- a/locales/ru-RU.json +++ b/locales/ru-RU.json @@ -45,7 +45,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "налево", @@ -85,7 +88,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "налево", @@ -143,7 +149,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходному переходу", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " километров", @@ -229,7 +241,10 @@ "пешеходная дорожка", "велосипедная дорожка", "дорожка для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходному переходу", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Паром", "example_phrases": { @@ -395,7 +413,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходному переходу", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Паром", "example_phrases": { @@ -448,7 +469,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "левее", @@ -949,7 +985,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "левее", @@ -984,7 +1023,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "левее", @@ -1023,7 +1065,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "левой стороны", @@ -1076,7 +1121,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходному переходу", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " километров", @@ -1399,7 +1453,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходному переходу", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "налево", @@ -1439,7 +1496,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходному переходу", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "налево", @@ -1495,7 +1555,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " километров", @@ -1958,7 +2024,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "налево", @@ -1998,7 +2067,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "налево", @@ -2039,8 +2111,11 @@ "empty_street_name_labels": [ "пешеходной дорожке", "велосипедной дорожке", + "пешеходном переходе", "дорожке для горных велосипедов", - "пешеходном переходе" + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "налево", @@ -2088,7 +2163,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходном переходе" + "пешеходном переходе", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "налево", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Воспользуйтесь лифтом.", @@ -2224,7 +2318,7 @@ "пешеходной дорожке", "велосипедную дорожку", "дорожка для горных велосипедов", - "пешеходному переходу" + "пешеходный переход" ], "example_phrases": { "0": [ diff --git a/locales/sk-SK.json b/locales/sk-SK.json index 6b6f587828..d41f248a10 100644 --- a/locales/sk-SK.json +++ b/locales/sk-SK.json @@ -43,9 +43,12 @@ }, "empty_street_name_labels": [ "chodník", - "cyklocesta", +"cyklocesta", "cesta pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -85,7 +88,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -143,7 +149,10 @@ "chodníku", "cykloceste", "ceste pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "chodníku", "cykloceste", "ceste pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrov", @@ -229,7 +241,10 @@ "chodníku", "cykloceste", "ceste pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "trajekt", "example_phrases": { @@ -395,7 +413,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "trajekt", "example_phrases": { @@ -448,7 +469,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -949,7 +985,10 @@ "chodníku", "cykloceste", "ceste pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -984,7 +1023,10 @@ "chodníku", "cykloceste", "ceste pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -1023,7 +1065,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -1076,7 +1121,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "chodníku", "cykloceste", "ceste pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrov", @@ -1399,7 +1453,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -1439,7 +1496,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -1495,7 +1555,10 @@ "chodníku", "cykloceste", "ceste pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "chodníku", "cykloceste", "ceste pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrov", @@ -1958,7 +2024,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -1998,7 +2067,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -2040,7 +2112,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -2088,7 +2163,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/sl-SI.json b/locales/sl-SI.json index 00f974b643..0a2e8fb9b0 100644 --- a/locales/sl-SI.json +++ b/locales/sl-SI.json @@ -45,7 +45,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -85,7 +88,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -143,7 +149,10 @@ "pešpoti", "kolesarski stezi", "stezi za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "pešpoti", "kolesarski stezi", "stezi za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrov", @@ -229,7 +241,10 @@ "pešpoti", "kolesarski stezi", "stezi za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Trajekt", "example_phrases": { @@ -395,7 +413,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Trajekt", "example_phrases": { @@ -448,7 +469,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -949,7 +985,10 @@ "pešpoti", "kolesarski stezi", "stezi za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -984,7 +1023,10 @@ "pešpoti", "kolesarski stezi", "stezi za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -1023,7 +1065,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -1076,7 +1121,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "pešpoti", "kolesarski stezi", "stezi za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrov", @@ -1399,7 +1453,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -1439,7 +1496,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -1495,7 +1555,10 @@ "pešpoti", "kolesarski stezi", "stezi za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "pešpoti", "kolesarski stezi", "stezi za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrov", @@ -1958,7 +2024,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -1998,7 +2067,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -2040,7 +2112,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -2088,7 +2163,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/sv-SE.json b/locales/sv-SE.json index 0b9fa77ba5..859f7d444b 100644 --- a/locales/sv-SE.json +++ b/locales/sv-SE.json @@ -45,7 +45,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -85,7 +88,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -143,7 +149,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -229,7 +241,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Färja", "example_phrases": { @@ -395,7 +413,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Färja", "example_phrases": { @@ -448,7 +469,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -949,7 +985,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -984,7 +1023,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -1023,7 +1065,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -1076,7 +1121,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -1399,7 +1453,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -1439,7 +1496,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -1495,7 +1555,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -1958,7 +2024,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -1998,7 +2067,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -2040,7 +2112,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -2088,7 +2163,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/tr-TR.json b/locales/tr-TR.json index ff5b0f0dcb..193d5b6940 100644 --- a/locales/tr-TR.json +++ b/locales/tr-TR.json @@ -45,7 +45,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -85,7 +88,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -143,7 +149,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometre", @@ -229,7 +241,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Feribot", "example_phrases": { @@ -395,7 +413,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Feribot", "example_phrases": { @@ -448,7 +469,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -949,7 +985,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -984,7 +1023,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -1023,7 +1065,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -1076,7 +1121,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ "kilometre", @@ -1399,7 +1453,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -1439,7 +1496,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -1495,7 +1555,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ "kilometre", @@ -1958,7 +2024,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -1998,7 +2067,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -2040,7 +2112,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -2088,7 +2163,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/uk-UA.json b/locales/uk-UA.json index ade762af37..fc1a180739 100644 --- a/locales/uk-UA.json +++ b/locales/uk-UA.json @@ -45,7 +45,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -85,7 +88,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -143,7 +149,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " кілометрів", @@ -229,7 +241,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "на пішохідній доріжці", "на велодоріжці", "на шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Пором", "example_phrases": { @@ -395,7 +413,10 @@ "на пішохідній доріжці", "на велодоріжці", "на шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Пором", "example_phrases": { @@ -448,7 +469,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "пішохідну доріжку", "велодоріжку", "шлях для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -949,7 +985,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -984,7 +1023,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -1023,7 +1065,10 @@ "пішохідну доріжку", "велодоріжку", "шлях для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -1076,7 +1121,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " кілометрів", @@ -1399,7 +1453,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -1439,7 +1496,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -1495,7 +1555,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " кілометрів", @@ -1958,7 +2024,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -1998,7 +2067,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -2040,7 +2112,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -2088,7 +2163,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/lua/admin.lua b/lua/admin.lua index ba00df3bf8..50db4e83c0 100644 --- a/lua/admin.lua +++ b/lua/admin.lua @@ -126,17 +126,39 @@ end --we save admins as 2(country) or 4(state/prov). function rels_proc (kv, nokeys) + + if (kv["type"] == "boundary" and kv["default_language"] and + (((kv["boundary"] == "administrative" or kv["boundary"] == "territorial") and (kv["admin_level"] and tonumber(kv["admin_level"]) > 4)) or + (kv["boundary"] == "political" or kv["political_division"] == "linguistic_community"))) then + + kv["iso_code"] = nil + kv["admin_level"] = kv["admin_level"] or "15" --assign a high admin level for linguistic_community + kv["drive_on_right"] = "false" + kv["allow_intersection_names"] = "false" + + delete_tags = { 'FIXME', 'note', 'source' } + + for i,k in ipairs(delete_tags) do + kv[k] = nil + end + + return 0, kv + end + if (kv["type"] == "boundary" and (kv["boundary"] == "administrative" or kv["boundary"] == "territorial") and (kv["admin_level"] == "2" or kv["admin_level"] == "3" or kv["admin_level"] == "4" or kv["admin_level"] == "6")) then - - if (kv["admin_level"] == "3" and kv["name"] ~= "Guyane" and kv["name"] ~= "Guadeloupe" and kv["name"] ~= "La Réunion" and - kv["name"] ~= "Martinique" and kv["name"] ~= "Mayotte" and kv["name"] ~= "Saint-Pierre-et-Miquelon" and - kv["name"] ~= "Saint-Barthélemy" and kv["name"] ~= "Saint-Martin (France)" and kv["name"] ~= "Polynésie Française" and - kv["name"] ~= "Wallis-et-Futuna" and kv["name"] ~= "Nouvelle-Calédonie" and kv["name"] ~= "Île de Clipperton" and - kv["name"] ~= "Terres australes et antarctiques françaises" and kv["name:en"] ~= "Metropolitan France" and - kv["name:en"] ~= "Hong Kong" and kv["name"] ~= "Metro Manila") then - return 1, kv + if (kv["admin_level"] == "3") then + if (kv["name"] ~= "Guyane" and kv["name"] ~= "Guadeloupe" and kv["name"] ~= "La Réunion" and + kv["name"] ~= "Martinique" and kv["name"] ~= "Mayotte" and kv["name"] ~= "Saint-Pierre-et-Miquelon" and + kv["name"] ~= "Saint-Barthélemy" and kv["name"] ~= "Saint-Martin (France)" and kv["name"] ~= "Polynésie Française" and + kv["name"] ~= "Wallis-et-Futuna" and kv["name"] ~= "Nouvelle-Calédonie" and kv["name"] ~= "Île de Clipperton" and + kv["name"] ~= "Terres australes et antarctiques françaises" and kv["name:en"] ~= "Metropolitan France" and + kv["name:en"] ~= "Hong Kong" and kv["name"] ~= "Metro Manila") then + return 1, kv + elseif kv["default_language"] == nil and kv["name:en"] ~= "Hong Kong" and kv["name"] ~= "Metro Manila" then + kv["default_language"] = "fr" + end end if kv["admin_level"] == "6" and kv["name"] ~= "District of Columbia" then diff --git a/lua/graph.lua b/lua/graph.lua index 1304a6717f..1587725c5b 100644 --- a/lua/graph.lua +++ b/lua/graph.lua @@ -91,7 +91,7 @@ access = { ["forestry"] = "false", ["destination"] = "true", ["customers"] = "true", -["official"] = "false", +["official"] = "true", ["public"] = "true", ["restricted"] = "true", ["allowed"] = "true", @@ -155,7 +155,7 @@ motor_vehicle = { ["forestry"] = "false", ["destination"] = "true", ["customers"] = "true", -["official"] = "false", +["official"] = "true", ["public"] = "true", ["restricted"] = "true", ["allowed"] = "true", @@ -232,7 +232,7 @@ bus = { ["restricted"] = "true", ["destination"] = "true", ["delivery"] = "false", -["official"] = "false", +["official"] = "true", ["permit"] = "true" } @@ -245,7 +245,7 @@ taxi = { ["restricted"] = "true", ["destination"] = "true", ["delivery"] = "false", -["official"] = "false", +["official"] = "true", ["permit"] = "true" } @@ -270,10 +270,10 @@ truck = { ["agricultural"] = "false", ["private"] = "true", ["discouraged"] = "false", -["permissive"] = "false", +["permissive"] = "true", ["unsuitable"] = "false", ["agricultural;forestry"] = "false", -["official"] = "false", +["official"] = "true", ["forestry"] = "false", ["destination;delivery"] = "true", ["permit"] = "true", @@ -435,7 +435,7 @@ motor_vehicle_node = { ["forestry"] = 0, ["destination"] = 1, ["customers"] = 1, -["official"] = 0, +["official"] = 1, ["public"] = 1, ["restricted"] = 1, ["allowed"] = 1, @@ -537,7 +537,7 @@ motor_cycle_node = { ["forestry"] = 0, ["destination"] = 1024, ["customers"] = 1024, -["official"] = 0, +["official"] = 1024, ["public"] = 1024, ["restricted"] = 1024, ["allowed"] = 1024, @@ -553,7 +553,7 @@ bus_node = { ["restricted"] = 64, ["destination"] = 64, ["delivery"] = 0, -["official"] = 0, +["official"] = 64, ["permit"] = 64 } @@ -566,7 +566,7 @@ taxi_node = { ["restricted"] = 32, ["destination"] = 32, ["delivery"] = 0, -["official"] = 0, +["official"] = 32, ["permit"] = 32 } @@ -580,10 +580,10 @@ truck_node = { ["agricultural"] = 0, ["private"] = 8, ["discouraged"] = 0, -["permissive"] = 0, +["permissive"] = 8, ["unsuitable"] = 0, ["agricultural;forestry"] = 0, -["official"] = 0, +["official"] = 8, ["forestry"] = 0, ["destination;delivery"] = 8, ["permit"] = 8, @@ -789,7 +789,7 @@ function normalize_measurement(measurement) -- more complicated case, try some Lua patterns. they're almost like regular -- expressions, so there'll probably be some unintended consequences! -- - -- because we want to parse compount expressions such as 3ft6in, then we use + -- because we want to parse compound expressions such as 3ft6in, then we use -- an accumulator to sum up each term in meters. this has the unintended -- side-effect that 10m6ft would also be valid... but whatever. local sum = 0 @@ -873,6 +873,11 @@ function filter_tags_generic(kv) return 1 end + --toss actual areas + if kv["area"] == "yes" then + return 1 + end + --figure out what basic type of road it is local forward = highway[kv["highway"]] if kv["highway"] == "construction" then @@ -971,15 +976,6 @@ function filter_tags_generic(kv) kv["motorcycle_forward"] = motor_vehicle[kv["motorcycle"]] or motor_vehicle[kv["motor_vehicle"]] or kv["motorcycle_forward"] kv["motorcycle_tag"] = motor_vehicle[kv["motorcycle"]] or motor_vehicle[kv["motor_vehicle"]] or nil - if kv["bike_tag"] == nil then - if kv["sac_scale"] == "hiking" then - kv["bike_forward"] = "true" - kv["bike_tag"] = "true" - elseif kv["sac_scale"] then - kv["bike_forward"] = "false" - end - end - if kv["access"] == "psv" then kv["taxi_forward"] = "true" kv["taxi_tag"] = "true" @@ -991,7 +987,7 @@ function filter_tags_generic(kv) if kv["motorroad"] == "yes" then kv["motorroad_tag"] = "true" end - + -- its not a highway type that we know of else --if its a ferry and these tags dont show up we want to set them to true local default_val = tostring(ferry) @@ -1256,6 +1252,10 @@ function filter_tags_generic(kv) else kv["pedestrian_backward"] = kv["pedestrian_forward"] end + + -- what on earth is going on here? if there is no oneway tagging we say the way is bidirectional despite all the + -- backward/forward crap we did above? this has to be the most common case (no oneway tag) so why on earth does it + -- reset any of the other directional tagging that is parsed above? elseif oneway_norm == nil or oneway_norm == "false" then kv["auto_backward"] = kv["auto_forward"] kv["truck_backward"] = kv["truck_forward"] @@ -1305,6 +1305,42 @@ function filter_tags_generic(kv) kv["bus_backward"] = "true" end + --let all the :forward overrides through + local mv_forward = kv["motor_vehicle:forward"] or kv["vehicle:forward"] + if mv_forward ~= nil then + kv["auto_forward"] = motor_vehicle[mv_forward] + kv["truck_forward"] = motor_vehicle[mv_forward] + kv["bus_forward"] = motor_vehicle[mv_forward] + kv["taxi_forward"] = motor_vehicle[mv_forward] + kv["moped_forward"] = motor_vehicle[mv_forward] + kv["motorcycle_forward"] = motor_vehicle[mv_forward] + end + if kv["foot:forward"] ~= nil then + kv["pedestrian_forward"] = foot[kv["foot:forward"]] + end + local bk_forward = kv["bicycle:forward"] or kv["vehicle:forward"] + if bk_forward ~= nil then + kv["bike_forward"] = bicycle[bk_forward] + end + + --let all the :backward overrides through, some of this is redundant but the code is a mess... + local mv_backward = kv["motor_vehicle:backward"] or kv["vehicle:backward"] + if mv_backward ~= nil then + kv["auto_backward"] = motor_vehicle[mv_backward] + kv["truck_backward"] = motor_vehicle[mv_backward] + kv["bus_backward"] = motor_vehicle[mv_backward] + kv["taxi_backward"] = motor_vehicle[mv_backward] + kv["moped_backward"] = motor_vehicle[mv_backward] + kv["motorcycle_backward"] = motor_vehicle[mv_backward] + end + if kv["foot:backward"] ~= nil then + kv["pedestrian_backward"] = foot[kv["foot:backward"]] + end + local bk_backward = kv["bicycle:backward"] or kv["vehicle:backward"] + if bk_backward ~= nil then + kv["bike_backward"] = bicycle[bk_backward] + end + kv["oneway_reverse"] = "false" --flip the onewayness @@ -1413,13 +1449,7 @@ function filter_tags_generic(kv) end end - --toss actual areas - if kv["area"] == "yes" then - return 1 - end - delete_tags = { 'FIXME', 'note', 'source' } - for i,k in ipairs(delete_tags) do kv[k] = nil end @@ -1619,7 +1649,9 @@ function filter_tags_generic(kv) kv["link_type"] = kv["link_type"] end - kv["private"] = private[kv["access"]] or private[kv["motor_vehicle"]] or "false" + --- TODO(nils): "private" also has directionality which we don't parse and handle yet + kv["private"] = private[kv["access"]] or private[kv["motor_vehicle"]] or private[kv["motorcar"]] or "false" + kv["private_hgv"] = private[kv["hgv"]] or kv["private"] or "false" kv["no_thru_traffic"] = no_thru_traffic[kv["access"]] or "false" kv["ferry"] = tostring(ferry) kv["rail"] = tostring(kv["auto_forward"] == "true" and (kv["railway"] == "rail" or kv["route"] == "shuttle_train")) @@ -1786,15 +1818,31 @@ function filter_tags_generic(kv) kv["maxheight"] = normalize_measurement(kv["maxheight"]) or normalize_measurement(kv["maxheight:physical"]) kv["maxwidth"] = normalize_measurement(kv["maxwidth"]) or normalize_measurement(kv["maxwidth:physical"]) kv["maxlength"] = normalize_measurement(kv["maxlength"]) - kv["maxweight"] = normalize_weight(kv["maxweight"]) kv["maxaxleload"] = normalize_weight(kv["maxaxleload"]) kv["maxaxles"] = tonumber(kv["maxaxles"]) + --forward/backward only tags + kv["maxheight_forward"] = normalize_measurement(kv["maxheight:forward"]) + kv["maxheight_backward"] = normalize_measurement(kv["maxheight:backward"]) + kv["maxlength_forward"] = normalize_measurement(kv["maxlength:forward"]) + kv["maxlength_backward"] = normalize_measurement(kv["maxlength:backward"]) + kv["maxweight_forward"] = normalize_weight(kv["maxweight:forward"]) + kv["maxweight_backward"] = normalize_weight(kv["maxweight:backward"]) + kv["maxwidth_forward"] = normalize_measurement(kv["maxwidth:forward"]) + kv["maxwidth_backward"] = normalize_measurement(kv["maxwidth:backward"]) + --TODO: hazmat really should have subcategories kv["hazmat"] = hazmat[kv["hazmat"]] or hazmat[kv["hazmat:water"]] or hazmat[kv["hazmat:A"]] or hazmat[kv["hazmat:B"]] or hazmat[kv["hazmat:C"]] or hazmat[kv["hazmat:D"]] or hazmat[kv["hazmat:E"]] + kv["hazmat_forward"] = hazmat[kv["hazmat:forward"]] or hazmat[kv["hazmat:water:forward"]] or hazmat[kv["hazmat:A:forward"]] or hazmat[kv["hazmat:B:forward"]] or + hazmat[kv["hazmat:C:forward"]] or hazmat[kv["hazmat:D:forward"]] or hazmat[kv["hazmat:E:forward"]] + kv["hazmat_backward"] = hazmat[kv["hazmat:backward"]] or hazmat[kv["hazmat:water:backward"]] or hazmat[kv["hazmat:A:backward"]] or hazmat[kv["hazmat:B:backward"]] or + hazmat[kv["hazmat:C:backward"]] or hazmat[kv["hazmat:D:backward"]] or hazmat[kv["hazmat:E:backward"]] + kv["maxspeed:hgv"] = normalize_speed(kv["maxspeed:hgv"]) + kv["maxspeed:hgv:forward"] = normalize_speed(kv["maxspeed:hgv:forward"]) + kv["maxspeed:hgv:backward"] = normalize_speed(kv["maxspeed:hgv:backward"]) if (kv["hgv:national_network"] or kv["hgv:state_network"] or kv["hgv"] == "local" or kv["hgv"] == "designated") then kv["truck_route"] = "true" @@ -2167,6 +2215,20 @@ function nodes_proc (kv, nokeys) return 0, kv end +-- useful for dumping the main kv table to see before and after tag filtering +-- function dump(o) +-- if type(o) == 'table' then +-- local s = '{ ' +-- for k,v in pairs(o) do +-- if type(k) ~= 'number' then k = '"'..k..'"' end +-- s = s .. '['..k..'] = ' .. dump(v) .. ',' +-- end +-- return s .. '} ' +-- else +-- return tostring(o) +-- end +-- end + function ways_proc (kv, nokeys) --if there were no tags passed in, ie keyvalues is empty if nokeys == 0 then @@ -2176,7 +2238,7 @@ function ways_proc (kv, nokeys) --does it at least have some interesting tags filter = filter_tags_generic(kv) - --let the caller know if its a keeper or not and give back the modified tags + --let the caller know if its a keeper or not and give back the modified tags --also tell it whether or not its a polygon or road return filter, kv, 0, 0 end @@ -2257,7 +2319,7 @@ function rels_proc (kv, nokeys) kv["restriction"] = nil return 0, kv - --has a restiction but type is not restriction...ignore + --has a restriction but type is not restriction...ignore elseif restrict ~= nil then return 1, kv else diff --git a/overlay-ports-vcpkg/liblzma/add_support_ios.patch b/overlay-ports-vcpkg/liblzma/add_support_ios.patch new file mode 100644 index 0000000000..79741639b6 --- /dev/null +++ b/overlay-ports-vcpkg/liblzma/add_support_ios.patch @@ -0,0 +1,20 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 52439b3..0b5e371 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -574,6 +574,7 @@ if(HAVE_GETOPT_LONG) + + install(TARGETS xzdec + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ++ BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}" + COMPONENT xzdec) + + if(UNIX) +@@ -701,6 +702,7 @@ if(NOT MSVC AND HAVE_GETOPT_LONG) + + install(TARGETS xz + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ++ BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}" + COMPONENT xz) + + if(UNIX) diff --git a/overlay-ports-vcpkg/liblzma/build-tools.patch b/overlay-ports-vcpkg/liblzma/build-tools.patch new file mode 100644 index 0000000000..759345ef23 --- /dev/null +++ b/overlay-ports-vcpkg/liblzma/build-tools.patch @@ -0,0 +1,20 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 03b8301..820d08e 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -584,6 +584,7 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/liblzma-config.cmake" + COMPONENT liblzma_Development) + + ++if(BUILD_TOOLS) + ############################################################################# + # getopt_long + ############################################################################# +@@ -793,6 +794,7 @@ if(NOT MSVC AND HAVE_GETOPT_LONG) + endforeach() + endif() + endif() ++endif() + + + ############################################################################# diff --git a/overlay-ports-vcpkg/liblzma/fix_config_include.patch b/overlay-ports-vcpkg/liblzma/fix_config_include.patch new file mode 100644 index 0000000000..91dc4c13ba --- /dev/null +++ b/overlay-ports-vcpkg/liblzma/fix_config_include.patch @@ -0,0 +1,12 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 34c6aca00..7b3708ab2 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -413,6 +413,7 @@ if(WIN32) + if(BUILD_SHARED_LIBS) + # Add the Windows resource file for liblzma.dll. + target_sources(liblzma PRIVATE src/liblzma/liblzma_w32res.rc) ++ target_include_directories(liblzma PRIVATE windows/vs2019) + + set_target_properties(liblzma PROPERTIES + LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/common/common_w32res.rc" diff --git a/overlay-ports-vcpkg/liblzma/portfile.cmake b/overlay-ports-vcpkg/liblzma/portfile.cmake new file mode 100644 index 0000000000..c551658fe0 --- /dev/null +++ b/overlay-ports-vcpkg/liblzma/portfile.cmake @@ -0,0 +1,86 @@ +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO xz-mirror/xz + REF "v${VERSION}" + SHA512 c28461123562564e030f3f733f078bc4c840e87598d9f4b718d4bca639120d8133f969c45d7bdc62f33f081d789ec0f14a1791fb7da18515682bfe3c0c7362e0 + HEAD_REF master + PATCHES + fix_config_include.patch + win_output_name.patch # Fix output name on Windows. Autotool build does not generate lib prefixed libraries on windows. + add_support_ios.patch # add install bundle info for support ios + build-tools.patch +) + +vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS + FEATURES + tools BUILD_TOOLS +) + +if(VCPKG_TARGET_ARCHITECTURE STREQUAL "wasm32") + set(WASM_OPTIONS -DCMAKE_C_BYTE_ORDER=LITTLE_ENDIAN -DCMAKE_CXX_BYTE_ORDER=LITTLE_ENDIAN) +endif() + +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS + ${FEATURE_OPTIONS} + ${WASM_OPTIONS} + -DBUILD_TESTING=OFF + -DCREATE_XZ_SYMLINKS=OFF + -DCREATE_LZMA_SYMLINKS=OFF + -DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT= # using flags from (vcpkg) toolchain + MAYBE_UNUSED_VARIABLES + CMAKE_MSVC_DEBUG_INFORMATION_FORMAT + CREATE_XZ_SYMLINKS + CREATE_LZMA_SYMLINKS +) +vcpkg_cmake_install() +vcpkg_copy_pdbs() + +set(exec_prefix "\${prefix}") +set(libdir "\${prefix}/lib") +set(includedir "\${prefix}/include") +set(PACKAGE_URL https://tukaani.org/xz/) +set(PACKAGE_VERSION 5.4.3) +if(NOT VCPKG_TARGET_IS_WINDOWS) + set(PTHREAD_CFLAGS -pthread) +endif() +set(prefix "${CURRENT_INSTALLED_DIR}") +configure_file("${SOURCE_PATH}/src/liblzma/liblzma.pc.in" "${CURRENT_PACKAGES_DIR}/lib/pkgconfig/liblzma.pc" @ONLY) +if (NOT VCPKG_BUILD_TYPE) + set(prefix "${CURRENT_INSTALLED_DIR}/debug") + configure_file("${SOURCE_PATH}/src/liblzma/liblzma.pc.in" "${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/liblzma.pc" @ONLY) +endif() +vcpkg_fixup_pkgconfig() + +vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/liblzma) + +if(VCPKG_LIBRARY_LINKAGE STREQUAL "static") + vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/lzma.h" "defined(LZMA_API_STATIC)" "1") +else() + vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/lzma.h" "defined(LZMA_API_STATIC)" "0") +endif() + +file(REMOVE_RECURSE + "${CURRENT_PACKAGES_DIR}/debug/include" + "${CURRENT_PACKAGES_DIR}/debug/share" + "${CURRENT_PACKAGES_DIR}/share/man" +) + +set(TOOLS xz xzdec) +foreach(_tool IN LISTS TOOLS) + if(NOT EXISTS "${CURRENT_PACKAGES_DIR}/bin/${_tool}${VCPKG_TARGET_EXECUTABLE_SUFFIX}") + list(REMOVE_ITEM TOOLS ${_tool}) + endif() +endforeach() +if(TOOLS) + vcpkg_copy_tools(TOOL_NAMES ${TOOLS} AUTO_CLEAN) +endif() + +if(VCPKG_LIBRARY_LINKAGE STREQUAL "static") + file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/bin" "${CURRENT_PACKAGES_DIR}/debug/bin") +endif() + +file(COPY "${CMAKE_CURRENT_LIST_DIR}/vcpkg-cmake-wrapper.cmake" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") +vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/COPYING") diff --git a/overlay-ports-vcpkg/liblzma/usage b/overlay-ports-vcpkg/liblzma/usage new file mode 100644 index 0000000000..b8a87394ec --- /dev/null +++ b/overlay-ports-vcpkg/liblzma/usage @@ -0,0 +1,9 @@ +liblzma is compatible with built-in CMake targets: + + find_package(LibLZMA REQUIRED) + target_link_libraries(main PRIVATE LibLZMA::LibLZMA) + +liblzma provides CMake targets: + + find_package(liblzma CONFIG REQUIRED) + target_link_libraries(main PRIVATE liblzma::liblzma) diff --git a/overlay-ports-vcpkg/liblzma/vcpkg-cmake-wrapper.cmake b/overlay-ports-vcpkg/liblzma/vcpkg-cmake-wrapper.cmake new file mode 100644 index 0000000000..826cdba065 --- /dev/null +++ b/overlay-ports-vcpkg/liblzma/vcpkg-cmake-wrapper.cmake @@ -0,0 +1,64 @@ +cmake_policy(PUSH) +cmake_policy(SET CMP0012 NEW) +cmake_policy(SET CMP0057 NEW) +set(z_vcpkg_liblzma_fixup_needed 0) +if(NOT "CONFIG" IN_LIST ARGS AND NOT "NO_MODULE" IN_LIST ARGS AND NOT CMAKE_DISABLE_FIND_PACKAGE_LibLZMA) + get_filename_component(z_vcpkg_liblzma_prefix "${CMAKE_CURRENT_LIST_DIR}" DIRECTORY) + get_filename_component(z_vcpkg_liblzma_prefix "${z_vcpkg_liblzma_prefix}" DIRECTORY) + find_path(LIBLZMA_INCLUDE_DIR NAMES lzma.h PATHS "${z_vcpkg_liblzma_prefix}/include" NO_DEFAULT_PATH) + # liblzma doesn't use a debug postfix, but FindLibLZMA.cmake expects it + find_library(LIBLZMA_LIBRARY_RELEASE NAMES lzma PATHS "${z_vcpkg_liblzma_prefix}/lib" NO_DEFAULT_PATH) + find_library(LIBLZMA_LIBRARY_DEBUG NAMES lzma PATHS "${z_vcpkg_liblzma_prefix}/debug/lib" NO_DEFAULT_PATH) + unset(z_vcpkg_liblzma_prefix) + if(CMAKE_VERSION VERSION_LESS 3.16) + # Older versions of FindLibLZMA.cmake need a single lib in LIBLZMA_LIBRARY. + set(z_vcpkg_liblzma_fixup_needed 1) + set(LIBLZMA_LIBRARY "${LIBLZMA_LIBRARY_RELEASE}" CACHE INTERNAL "") + elseif(NOT TARGET LibLZMA::LibLZMA) + set(z_vcpkg_liblzma_fixup_needed 1) + endif() + # Known values, and required. Skip expensive tests. + set(LIBLZMA_HAS_AUTO_DECODER 1 CACHE INTERNAL "") + set(LIBLZMA_HAS_EASY_ENCODER 1 CACHE INTERNAL "") + set(LIBLZMA_HAS_LZMA_PRESET 1 CACHE INTERNAL "") +endif() + +_find_package(${ARGS}) + +if(z_vcpkg_liblzma_fixup_needed) + include(SelectLibraryConfigurations) + select_library_configurations(LIBLZMA) + if(NOT TARGET LibLZMA::LibLZMA) + # Backfill LibLZMA::LibLZMA to versions of cmake before 3.14 + add_library(LibLZMA::LibLZMA UNKNOWN IMPORTED) + if(DEFINED LIBLZMA_INCLUDE_DIRS) + set_target_properties(LibLZMA::LibLZMA PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${LIBLZMA_INCLUDE_DIRS}") + endif() + set_property(TARGET LibLZMA::LibLZMA APPEND PROPERTY + IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties(LibLZMA::LibLZMA PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" + IMPORTED_LOCATION_RELEASE "${LIBLZMA_LIBRARY_RELEASE}") + if(EXISTS "${LIBLZMA_LIBRARY}") + set_target_properties(LibLZMA::LibLZMA PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${LIBLZMA_LIBRARY}") + endif() + endif() + if(LIBLZMA_LIBRARY_DEBUG) + # Backfill debug variant to versions of cmake before 3.16 + set_property(TARGET LibLZMA::LibLZMA APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) + set_target_properties(LibLZMA::LibLZMA PROPERTIES IMPORTED_LOCATION_DEBUG "${LIBLZMA_LIBRARY_DEBUG}") + endif() +endif() +if(LIBLZMA_LIBRARIES AND NOT "Threads::Threads" IN_LIST LIBLZMA_LIBRARIES) + set(THREADS_PREFER_PTHREAD_FLAG TRUE) + find_package(Threads) + list(APPEND LIBLZMA_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) + if(TARGET LibLZMA::LibLZMA) + set_property(TARGET LibLZMA::LibLZMA APPEND PROPERTY INTERFACE_LINK_LIBRARIES Threads::Threads) + endif() +endif() +unset(z_vcpkg_liblzma_fixup_needed) +cmake_policy(POP) diff --git a/overlay-ports-vcpkg/liblzma/vcpkg.json b/overlay-ports-vcpkg/liblzma/vcpkg.json new file mode 100644 index 0000000000..e9168357cc --- /dev/null +++ b/overlay-ports-vcpkg/liblzma/vcpkg.json @@ -0,0 +1,23 @@ +{ + "name": "liblzma", + "version": "5.4.4", + "description": "Compression library with an API similar to that of zlib.", + "homepage": "https://tukaani.org/xz/", + "license": null, + "dependencies": [ + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ], + "features": { + "tools": { + "description": "Build tools", + "supports": "!windows, mingw" + } + } +} diff --git a/overlay-ports-vcpkg/liblzma/win_output_name.patch b/overlay-ports-vcpkg/liblzma/win_output_name.patch new file mode 100644 index 0000000000..9a845bdbae --- /dev/null +++ b/overlay-ports-vcpkg/liblzma/win_output_name.patch @@ -0,0 +1,17 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 0c6d4b7..62a824a 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -868,8 +868,11 @@ set_target_properties(liblzma PROPERTIES + + # It's liblzma.so or liblzma.dll, not libliblzma.so or lzma.dll. + # Avoid the name lzma.dll because it would conflict with LZMA SDK. +- PREFIX "" ++ OUTPUT_NAME lzma + ) ++if(WIN32 AND NOT MINGW) ++ set_target_properties(liblzma PROPERTIES RUNTIME_OUTPUT_NAME liblzma) ++endif() + + # Create liblzma-config-version.cmake. + # diff --git a/proto/CMakeLists.txt b/proto/CMakeLists.txt index 14bbaee932..f7cfb1c898 100644 --- a/proto/CMakeLists.txt +++ b/proto/CMakeLists.txt @@ -10,7 +10,10 @@ set(protobuf_descriptors transit.proto transit_fetch.proto incidents.proto - status.proto) + status.proto + matrix.proto + isochrone.proto + expansion.proto) if(ENABLE_DATA_TOOLS) # Only mjolnir needs the OSM PBF descriptors @@ -19,6 +22,7 @@ if(ENABLE_DATA_TOOLS) ${VALHALLA_SOURCE_DIR}/third_party/OSM-binary/src/osmformat.proto) endif() + protobuf_generate_cpp(protobuf_srcs protobuf_hdrs ${protobuf_descriptors}) valhalla_module(NAME proto diff --git a/proto/api.proto b/proto/api.proto index 9154c6d33a..19b1b72e94 100644 --- a/proto/api.proto +++ b/proto/api.proto @@ -7,6 +7,9 @@ import public "trip.proto"; // the paths, filled out by thor import public "directions.proto"; // the directions, filled out by odin import public "info.proto"; // statistics about the request, filled out by loki/thor/odin import public "status.proto"; // info for status endpoint +import public "matrix.proto"; // the matrix results +import public "isochrone.proto"; // the isochrone results +import public "expansion.proto"; // the expansion results message Api { // this is the request to the api @@ -16,11 +19,11 @@ message Api { Trip trip = 2; // trace_attributes Directions directions = 3; // route, optimized_route, trace_route, centroid Status status = 4; // status - //TODO: isochrone - //TODO: matrix - //TODO: locate - //TODO: height - //TODO: expansion + Matrix matrix = 5; // sources_to_targets + Isochrone isochrone = 6; // isochrone + Expansion expansion = 7; // expansion + //TODO: locate; + //TODO: height; // here we store a bit of info about what happened during request processing (stats/errors/warnings) Info info = 20; diff --git a/proto/common.proto b/proto/common.proto index abcc1f4176..bb9b975e17 100644 --- a/proto/common.proto +++ b/proto/common.proto @@ -11,6 +11,35 @@ message LatLng { } } +message RouteLandmark { + enum Type { + kUnused = 0; + kFuel = 1; + kPostOffice = 2; + kPolice = 3; + kFireStation = 4; + kCarWash = 5; + kRestaurant = 6; + kFastFood = 7; + kCafe = 8; + kBank = 9; + kPharmacy = 10; + kKindergarten = 11; + kBar = 12; + kHospital = 13; + kPub = 14; + kClinic = 15; + kTheatre = 16; + kCinema = 17; + kCasino = 18; + } + string name = 1; + Type type = 2; + LatLng lat_lng = 3; + double distance = 4; // landmark's distance along the trip edge. in maneuvers it's landmark's distance to the maneuver + bool right = 5; // landmark is to the right of the route that comes before the maneuver +} + message BoundingBox { LatLng min_ll = 1; LatLng max_ll = 2; @@ -124,9 +153,14 @@ message Location { int32 preferred_layer = 28; } float waiting_secs = 29; // waiting period before a new leg starts, e.g. for servicing/loading goods + oneof has_street_side_cutoff { + RoadClass street_side_cutoff = 30; + } // This information will be ignored if provided in the request. Instead it will be filled in as the request is handled Correlation correlation = 90; + string time_zone_offset = 91; + string time_zone_name = 92; } message TransitEgressInfo { @@ -186,19 +220,97 @@ message TransitRouteInfo { message Pronunciation { enum Alphabet { - kIpa = 0; - kXKatakana = 1; - kXJeita = 2; - kNtSampa = 3; + kNone = 0; + kIpa = 1; + kKatakana = 2; + kJeita = 3; + kNtSampa = 4; } Alphabet alphabet = 1; string value = 2; } +enum LanguageTag { + kUnspecified = 0; + kAb = 1; + kAm = 2; + kAr = 3; + kAz = 4; + kBe = 5; + kBg = 6; + kBn = 7; + kBs = 8; + kCa = 9; + kCkb = 10; + kCs = 11; + kDa = 12; + kDe = 13; + kDv = 14; + kDz = 15; + kEl = 16; + kEn = 17; + kEs = 18; + kEt = 19; + kFa = 20; + kFi = 21; + kFr = 22; + kFy = 23; + kGl = 24; + kHe = 25; + kHr = 26; + kHu = 27; + kHy = 28; + kId = 29; + kIs = 30; + kIt = 31; + kJa = 32; + kKa = 33; + kKl = 34; + kKm = 35; + kKo = 36; + kLo = 37; + kLt = 38; + kLv = 39; + kMg = 40; + kMk = 41; + kMn = 42; + kMo = 43; + kMt = 44; + kMy = 45; + kNe = 46; + kNl = 47; + kNo = 48; + kOc = 49; + kPap = 50; + kPl = 51; + kPs = 52; + kPt = 53; + kRm = 54; + kRo = 55; + kRu = 56; + kSk = 57; + kSl = 58; + kSq = 59; + kSr = 60; + kSrLatn = 61; + kSv = 62; + kTg = 63; + kTh = 64; + kTk = 65; + kTr = 66; + kUk = 67; + kUr = 68; + kUz = 69; + kVi = 70; + kZh = 71; + kCy = 72; +} + message StreetName { string value = 1; // The actual street name value, examples: I 95 North or Derry Street bool is_route_number = 2; // true if the street name is a reference route number such as: I 81 South or US 322 West Pronunciation pronunciation = 3; // The pronunciation associated with this street name + LanguageTag language_tag = 4; } message TurnLane { @@ -232,8 +344,9 @@ message TaggedValue { kBssInfo = 3; kLevel = 4; kLevelRef = 5; + kLandmark = 6; kTunnel = 49; - kBridge = 50; + kBridge = 50; } bytes value = 1; // The actual tagged name value, examples: Ted Williams Tunnel Type type = 2; // The type of tagged name (tunnel or bridge) @@ -251,7 +364,7 @@ enum VehicleType { kCar = 0; kMotorcycle = 1; kAutoBus = 2; - kTractorTrailer = 3; + kTruck = 3; kMotorScooter = 4; } @@ -259,7 +372,7 @@ enum VehicleType { enum PedestrianType { kFoot = 0; kWheelchair = 1; - kSegway = 2; + kBlind = 2; } enum BicycleType { @@ -279,3 +392,13 @@ enum TransitType { kGondola = 6; kFunicular = 7; } + +message Summary { + float length = 1; // kilometers or miles based on units + double time = 2; // seconds + BoundingBox bbox = 3; // Bounding box of the shape + bool has_time_restrictions = 4; // Does the route contain any time restrictions? + bool has_toll = 5; + bool has_ferry = 6; + bool has_highway = 7; +} diff --git a/proto/directions.proto b/proto/directions.proto index 92487e9298..ada56686d7 100644 --- a/proto/directions.proto +++ b/proto/directions.proto @@ -6,15 +6,6 @@ import public "sign.proto"; message DirectionsLeg { - message Summary { - float length = 1; // kilometers or miles based on units - double time = 2; // seconds - BoundingBox bbox = 3; // Bounding box of the shape - bool has_time_restrictions = 4; // Does the route contain any time restrictions? - bool has_toll = 5; - bool has_ferry = 6; - bool has_highway = 7; - } message GuidanceView { enum Type{ @@ -140,6 +131,7 @@ message DirectionsLeg { BikeShareStationInfo bss_info = 38; // Bike Share Station Info bool portions_highway = 39; // has portions highway bool portions_ferry = 40; // has portions ferry + repeated RouteLandmark landmarks = 41; // landmarks correlated with the maneuver } uint64 trip_id = 1; diff --git a/proto/expansion.proto b/proto/expansion.proto new file mode 100644 index 0000000000..60454202a3 --- /dev/null +++ b/proto/expansion.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +option optimize_for = LITE_RUNTIME; +package valhalla; + +message Expansion { + + message Geometry { + repeated sint32 coords = 1 [packed=true]; + } + + enum EdgeStatus { + connected = 0; + settled = 1; + reached = 2; + } + + enum ExpansionType { + forward = 0; + reverse = 1; + } + + repeated uint32 costs = 1 [packed=true]; + repeated uint32 durations = 2 [packed=true]; + repeated uint32 distances = 3 [packed=true]; + repeated EdgeStatus edge_status = 4; + repeated uint32 edge_id = 5 [packed=true]; + repeated uint32 pred_edge_id = 6 [packed=true]; + repeated ExpansionType expansion_type = 8; + + repeated Geometry geometries = 7; +} \ No newline at end of file diff --git a/proto/info.proto b/proto/info.proto index 5cdc690674..5b5559d7a9 100644 --- a/proto/info.proto +++ b/proto/info.proto @@ -29,7 +29,7 @@ message CodedDescription { message Info { repeated Statistic statistics = 1; // stats that we collect during request processing - repeated CodedDescription errors = 2; // errors that occured during request processing - repeated CodedDescription warnings = 3; // warnings that occured during request processing + repeated CodedDescription errors = 2; // errors that occurred during request processing + repeated CodedDescription warnings = 3; // warnings that occurred during request processing bool is_service = 4; // was this a service request/response rather than a direct call to the library } diff --git a/proto/isochrone.proto b/proto/isochrone.proto new file mode 100644 index 0000000000..31a1ac27f8 --- /dev/null +++ b/proto/isochrone.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; + +option optimize_for = LITE_RUNTIME; +package valhalla; + +message Isochrone { + + enum metric_type { + time = 0; + distance = 1; + } + + message Geometry { + repeated sint32 coords = 1 [packed=true]; + } + + message Contour { + repeated Geometry geometries = 2; // if polygon first one is outer rest are inners, though this is a problem when we allow multi location isochrones + } + + message Interval { + metric_type metric = 1; // time or distance enum + float metric_value = 2; // the target metric, eg 15min + repeated Contour contours = 3; + } + + repeated Interval intervals = 1; +} \ No newline at end of file diff --git a/proto/matrix.proto b/proto/matrix.proto new file mode 100644 index 0000000000..54c47c0a1c --- /dev/null +++ b/proto/matrix.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; +option optimize_for = LITE_RUNTIME; +package valhalla; +import public "common.proto"; + +message Matrix { + enum Algorithm { + TimeDistanceMatrix = 0; + CostMatrix = 1; + TimeDistanceBSSMatrix = 2; + } + + repeated uint32 distances = 2; + repeated float times = 3; + repeated uint32 from_indices = 4; + repeated uint32 to_indices = 5; + repeated string date_times = 6; + Algorithm algorithm = 7; + repeated string shapes = 8; + repeated string time_zone_offsets = 9; + repeated string time_zone_names = 10; + repeated bool second_pass = 11; +} diff --git a/proto/options.proto b/proto/options.proto index 9dd308aa92..3f3795e8fd 100644 --- a/proto/options.proto +++ b/proto/options.proto @@ -41,6 +41,7 @@ enum ShapeFormat { polyline6 = 0; polyline5 = 1; geojson = 2; + no_shape = 3; // to omit returning the geometry, e.g. default in matrix } // use this to select which top level fields should be present in the pbf output @@ -50,12 +51,12 @@ message PbfFieldSelector { bool trip = 2; // /trace_attributes bool directions = 3; // /route /trace_route /optimized_route /centroid bool status = 4; // /status + bool matrix = 5; // sources_to_targets + bool isochrone = 6; + bool expansion = 9; // TODO: enable these once we have objects for them - // bool isochrone = 5; - // bool matrix = 6; // bool locate = 7; // bool height = 8; - // bool expansion = 9; } message AvoidEdge { @@ -311,6 +312,11 @@ message Costing { uint32 axle_count = 81; float use_lit = 82; bool disable_hierarchy_pruning = 83; + bool ignore_non_vehicular_restrictions = 84; + oneof has_hgv_no_access_penalty { + float hgv_no_access_penalty = 85; + } + float use_truck_route = 86; } oneof has_options { @@ -339,6 +345,7 @@ message Options { gpx = 1; osrm = 2; pbf = 3; + geotiff = 4; } enum Action { @@ -366,11 +373,13 @@ message Options { } enum ExpansionProperties { - costs = 0; - durations = 1; - distances = 2; - statuses = 3; - edge_ids = 4; + cost = 0; + duration = 1; + distance = 2; + edge_status = 3; + edge_id = 4; + pred_edge_id = 5; + expansion_type = 6; } Units units = 1; // kilometers or miles @@ -444,7 +453,9 @@ message Options { oneof has_use_timestamps { bool use_timestamps = 37; // Use timestamps to compute elapsed time for trace_route and trace_attributes [default = false] } - ShapeFormat shape_format = 38; // Shape format (defaults to polyline6 encoding) + oneof has_shape_format { + ShapeFormat shape_format = 38; // Shape format, defaults to polyline6 encoding for OSRM/height, and no_shape for matrix + } oneof has_alternates { uint32 alternates = 39; // Maximum number of alternate routes that can be returned } @@ -482,4 +493,9 @@ message Options { uint32 matrix_locations = 54; // a one to many or many to one time distance matrix. Does not affect } // sources_to_targets when either sources or targets has more than 1 location // or when CostMatrix is the selected matrix mode. + bool banner_instructions = 55; // Whether to return bannerInstructions in the OSRM serializer response + float elevation_interval = 56; // Interval for sampling elevation along the route path. [default = 0.0]; + bool voice_instructions = 57; // Whether to return voiceInstructions in the OSRM serializer response + bool dedupe = 58; // Keep track of edges and override their properties during expansion, + // ensuring that each edge appears in the output only once. [default = false] } diff --git a/proto/sign.proto b/proto/sign.proto index 80a9bdb085..e5f81b1a84 100644 --- a/proto/sign.proto +++ b/proto/sign.proto @@ -8,6 +8,7 @@ message TripSignElement { bool is_route_number = 2; // true if sign element is a reference route number such as: I 81 South or US 322 West uint32 consecutive_count = 3; // The frequency of this sign element within a set a consecutive signs Pronunciation pronunciation = 4; // The pronunciation associated with this sign element + LanguageTag language_tag = 5; } message TripSign { diff --git a/proto/trip.proto b/proto/trip.proto index 68451da61e..493a5031f3 100644 --- a/proto/trip.proto +++ b/proto/trip.proto @@ -176,6 +176,11 @@ message TripLeg { SacScale sac_scale = 51; bool shoulder = 52; bool indoor = 53; + repeated RouteLandmark landmarks = 54; // landmarks in the trip leg + repeated StreetName tunnel_name = 55; + float elevation_sampling_interval = 56; + repeated float elevation = 57; + bool country_crossing = 58; } message IntersectingEdge { @@ -189,6 +194,7 @@ message TripLeg { RoadClass road_class = 8; uint32 lane_count = 9; TripSign sign = 10; + repeated StreetName name = 21; // street names } message Cost { @@ -232,6 +238,7 @@ message TripLeg { PathCost cost = 12; // how much cost did it take at this node in the path repeated PathCost recosts = 13; // how much cost did it take at this node in the path for recostings BikeShareStationInfo bss_info = 14; + bool traffic_signal = 21; } message Admin { @@ -280,6 +287,7 @@ message TripLeg { repeated Incident incidents = 11; repeated string algorithms = 12; repeated Closure closures = 13; + Summary summary = 14; } message TripRoute { diff --git a/run_route_scripts/batch.sh b/run_route_scripts/batch.sh index 30b9c55df2..4b19120d26 100755 --- a/run_route_scripts/batch.sh +++ b/run_route_scripts/batch.sh @@ -58,7 +58,7 @@ cat "${TMP}" | parallel --progress -k -C '\|' -P "${CONCURRENCY}" "valhalla_run_ # "-e" suffix, even though -i arg is empty. The wildcard below will ensure that # that file is deleted as well. rm -f ${TMP}* -echo "orgLat, orgLng, destLat, destLng, result, #Passes, runtime, trip time, length, arcDistance, #Manuevers, elapsedCostSeconds, elapsedCostCost" > ${RESULTS_OUTDIR}/statistics.csv +echo "orgLat, orgLng, destLat, destLng, result, #Passes, runtime, trip time, length, arcDistance, #Maneuvers, elapsedCostSeconds, elapsedCostCost" > ${RESULTS_OUTDIR}/statistics.csv cat `ls -1v ${RESULTS_OUTDIR}/*_statistics.csv` >> ${RESULTS_OUTDIR}/statistics.csv rm -f ${RESULTS_OUTDIR}/*_statistics.csv diff --git a/run_route_scripts/gen_requests_matrix.py b/run_route_scripts/gen_requests_matrix.py new file mode 100755 index 0000000000..b6a4dc1b13 --- /dev/null +++ b/run_route_scripts/gen_requests_matrix.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +import argparse +import json +from collections import namedtuple +from pathlib import Path +from random import uniform +import sys + +Bbox = namedtuple("Bbox", "min_x min_y max_x max_y") + +# parse some program arguments +parser = argparse.ArgumentParser(description="Generates matrix request payloads from random coordinates within a bbox and writes to a single file.") +parser.add_argument("-o", "--output-file", type=Path, help='the output file path', required=True) +parser.add_argument("-b", "--bbox", type=str, help="bbox for random coords in format minx,miny,maxx, maxy", required=True) +parser.add_argument("-l", "--locations", type=int, help="amount of sources/targets, default 20", default=20) +parser.add_argument("-r", "--runs", type=int, help="how many requests to generate", default=100) +parser.add_argument("-c", "--costing", type=str, help="the costing model, default auto", default="auto") +args = parser.parse_args() + + +if __name__ == "__main__": + try: + bbox = Bbox(*[float(x) for x in args.bbox.split(",")]) + except ValueError: + print(f"BBOX {args.bbox} is not a comma-separated string of coordinates.", file=sys.stderr) + sys.exit(1) + + req = { + "costing": args.costing, + "verbose": False + } + + if args.output_file.exists(): + args.output_file.unlink() + + for run in range(args.runs): + locations = list() + for loc in range(args.locations): + x = round(uniform(bbox.min_x, bbox.max_x), 6) + y = round(uniform(bbox.min_y, bbox.max_y), 6) + locations.append({"lon": x, "lat": y}) + + req["sources"] = locations + req["targets"] = locations + + with args.output_file.open('a') as f: + json.dump(req, f) + f.write("\n") diff --git a/run_route_scripts/results/combine_route_stats.py b/run_route_scripts/results/combine_route_stats.py index 46b71c3d3d..8cfb369a59 100755 --- a/run_route_scripts/results/combine_route_stats.py +++ b/run_route_scripts/results/combine_route_stats.py @@ -5,7 +5,7 @@ import sys -STATS_TO_DIFF = ['#Passes', 'runtime', 'trip time', 'length', '#Manuevers', 'elapsedCostSeconds', 'elapsedCostCost'] +STATS_TO_DIFF = ['#Passes', 'runtime', 'trip time', 'length', '#Maneuvers', 'elapsedCostSeconds', 'elapsedCostCost'] def main(old_stats_file, new_stats_file, output_file): diff --git a/run_route_scripts/results/total_run_stats.sh b/run_route_scripts/results/total_run_stats.sh index 0113933076..6a97ed00e2 100755 --- a/run_route_scripts/results/total_run_stats.sh +++ b/run_route_scripts/results/total_run_stats.sh @@ -29,7 +29,7 @@ then fi ### Example input -###1:orgLat, 2:orgLng, 3:destLat, 4:destLng, 5:result, 6:#Passes, 7:runtime, 8:trip time, 9:length, 10:arcDistance, 11:#Manuevers +###1:orgLat, 2:orgLng, 3:destLat, 4:destLng, 5:result, 6:#Passes, 7:runtime, 8:trip time, 9:length, 10:arcDistance, 11:#Maneuvers ###34.854443,40.608334,36.366665,36.983334,success,1,81,20031,273.763855,229.100693,44 # Write total stats header diff --git a/run_route_scripts/run_with_server.py b/run_route_scripts/run_with_server.py index 2e1cd94e58..96f40d6cc6 100755 --- a/run_route_scripts/run_with_server.py +++ b/run_route_scripts/run_with_server.py @@ -4,6 +4,7 @@ import requests import json import os +from pathlib import Path import shutil import datetime import time @@ -21,7 +22,7 @@ def get_post_bodies(filename): yield post_body def initialize(args_,response_count_): - # for persistant connections + # for persistent connections global session session = requests.Session() # so each process knows the options provided @@ -120,3 +121,17 @@ def make_request(post_body): progress = int(next_progress / increment) if progress != 100 / increment: print('100%') + + # print total duration + if parsed_args.format == "json": + output_dir_path = Path(parsed_args.output_dir) + duration = 0 + for out_f in output_dir_path.iterdir(): + if out_f.is_file() and out_f.suffix == ".json": + with open(out_f) as f: + try: + duration += json.load(f)["performance"]["response_time"] + except json.decoder.JSONDecodeError: + continue + + print(f"Requests took {duration} seconds") diff --git a/run_route_scripts/run_with_server_matrix.py b/run_route_scripts/run_with_server_matrix.py new file mode 100755 index 0000000000..06a2d391dc --- /dev/null +++ b/run_route_scripts/run_with_server_matrix.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 + +import argparse +import multiprocessing +from pathlib import Path + +import requests +import json +import time + + +# generator for post bodies from the file +def get_post_bodies(filename): + with open(filename, 'r') as f: + line_number = 0 + for line in f: + line_number += 1 + line = line[line.find('{'):] + line = line[0:line.rfind('}') + 1] + post_body = json.loads(line) + post_body['id'] = str(line_number) + yield post_body + + +def initialize(args_, response_count_): + # for persistent connections + global session + session = requests.Session() + # so each process knows the options provided + global args + args = args_ + # so each process can signal completing a request + global response_count + response_count = response_count_ + + +# post a request +def make_request(post_body): + # make request + try: + start = time.time() + response = session.post(args.url, json=post_body, headers=args.headers) + stop = time.time() + elapsed = stop - start + response = response.json() + with response_count.get_lock(): + response_count.value += 1 + except Exception as e: + return {"error": e, "line": post_body["id"]} + + return {"response": response, "line": post_body["id"], "perf": elapsed, "sources": post_body["sources"], "targets": post_body["targets"]} + + +if __name__ == "__main__": + # parse some program arguments + parser = argparse.ArgumentParser(description="Runs payloads generated by gen_requests_matrix.py against a Valhalla server and writes the results to a single file for diffing purposes. Also prints out the total time.") + parser.add_argument('--test-file', type=Path, help='The file with the test requests', required=True) + parser.add_argument('--url', type=str, help='The url to which you want to POST the request bodies', + default='http://localhost:8002/route') + parser.add_argument('--output-file', type=Path, help='The output file path') + parser.add_argument('--concurrency', type=int, help='The number of processes to use to make requests', + default=multiprocessing.cpu_count()) + parser.add_argument('--headers', type=str, + help='Additional http headers to send with the requests. Follows the http header spec, eg. some-header-name: some-header-value', + action='append', nargs='*', default=[]) + args = parser.parse_args() + + # setup http headers + args.headers = {k: v for k, v in [h.split(': ') for hs in args.headers for h in hs]} + # track progress with a count of finished requests + response_count = multiprocessing.Value('i', 0) + # make a worker pool to work on the requests + work = [body for body in get_post_bodies(args.test_file)] + # Note: workers also call initialize for themselves + results = list() + with multiprocessing.Pool(initializer=initialize, initargs=(args, response_count), + processes=args.concurrency) as pool: + for result in pool.imap_unordered(make_request, work): + if result.get("error"): + print(f"line {result}: {result['error']}") + continue + results.append(result) + + with args.output_file.open("w") as f: + total_time = 0 + for result in sorted(results, key=lambda i: i["line"]): + if not result["response"].get("sources_to_targets"): + print(f"line {result['line']}: {result['response']}") + continue + for source in range(len(result["sources"])): + for target in range(len(result["targets"])): + duration = result["response"]["sources_to_targets"]["durations"][source][target] + distance = result["response"]["sources_to_targets"]["distances"][source][target] + f.write(f"line {result['line']}, distance {distance}, [{json.dumps(result['sources'][source])}, {json.dumps(result['targets'][target])}]\n") + total_time += result["perf"] + + print(f"Total time: {total_time}") diff --git a/run_route_scripts/visual_compare.sh b/run_route_scripts/visual_compare.sh index 9f430da05f..fda4746a5a 100755 --- a/run_route_scripts/visual_compare.sh +++ b/run_route_scripts/visual_compare.sh @@ -4,7 +4,7 @@ shopt -s expand_aliases if [ -z $4 ]; then echo "You can visually diff two runs of RAD with differing request parameters in the following way. - First create two RAD request files with the apropriate request parameter changes. Run RAD: + First create two RAD request files with the appropriate request parameter changes. Run RAD: ./run_with_server.py --test-file a.txt --url http://localhost:8002/route --concurrency 24 --format csv ./run_with_server.py --test-file b.txt --url http://localhost:8002/route --concurrency 24 --format csv This will create two directories, 20210125_103436_a 20210125_103550_b respectively. @@ -22,7 +22,7 @@ input_file2=${4} # this will urlencode strings from pipe alias urlencode='python3 -c "import sys; import os; import requests; print(*(requests.utils.quote(line.strip()) for line in sys.stdin), sep=os.linesep)"' -# diff to get the file names, these have the line number in the orginal input +# diff to get the file names, these have the line number in the original input for i in $(diff -qr ${output_dir1} ${output_dir2} | sed -e "s/.*\///g" -e "s/\..*$//g" | sort -n | grep -v "statistics"); do anchor_before=$(echo -e "[$(sed "${i}q;d" ${input_file1} | sed -e "s/^[^{]*//g" -e "s/}[^}]*$/}/g" | jq -rc '. + {id: "770000"}')," | urlencode) anchor_after=$(echo -e "$(sed "${i}q;d" ${input_file2} | sed -e "s/^[^{]*//g" -e "s/}[^}]*$/}/g" | jq -rc '. + {id: "007700"}')]" | urlencode) diff --git a/scripts/format.sh b/scripts/format.sh index ac38fd9bff..5d9b68248c 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -10,6 +10,10 @@ set -o errexit -o pipefail -o nounset readonly CLANG_FORMAT_VERSION=11.0.0 +if [[ $(uname -i) == 'aarch64' ]]; then + echo 'Formatting is disabled on arm for the time being' + exit +fi source scripts/bash_utils.sh setup_mason @@ -19,13 +23,18 @@ readonly CLANG_FORMAT=$(pwd)/mason_packages/.link/bin/clang-format echo "Using clang-format $CLANG_FORMAT_VERSION from ${CLANG_FORMAT}" -find src valhalla test bench -type f -name '*.h' -o -name '*.cc' \ +find src valhalla test -type f -name '*.h' -o -name '*.cc' \ | xargs -I{} -P ${NPROC} ${CLANG_FORMAT} -i -style=file {} - # Python setup py=$(setup_python) -${py} -m pip install black==22.10.0 flake8==5.0.4 +if [[ $(python3 -m pip list | grep -c "black\|flake8") -ne 2 ]]; then + if [[ $(python3 -c 'import sys; print(int(sys.base_prefix != sys.prefix or hasattr(sys, "real_prefix")))') -eq 1 ]]; then + ${py} -m pip install black==22.10.0 flake8==5.0.4 + else + sudo PIP_BREAK_SYSTEM_PACKAGES=1 ${py} -m pip install black==22.10.0 flake8==5.0.4 + fi +fi python_sources=$(LANG=C find scripts src/bindings/python -type f -exec file {} \; | grep -F "Python script" | sed 's/:.*//') # Python formatter diff --git a/scripts/install-linux-deps.sh b/scripts/install-linux-deps.sh index d8dfa59e84..de81c7c215 100755 --- a/scripts/install-linux-deps.sh +++ b/scripts/install-linux-deps.sh @@ -4,8 +4,8 @@ set -x -o errexit -o pipefail -o nounset # Now, go through and install the build dependencies -apt-get update --assume-yes -env DEBIAN_FRONTEND=noninteractive apt-get install --yes --quiet \ +sudo apt-get update --assume-yes +env DEBIAN_FRONTEND=noninteractive sudo apt install --yes --quiet \ autoconf \ automake \ ccache \ @@ -19,8 +19,10 @@ env DEBIAN_FRONTEND=noninteractive apt-get install --yes --quiet \ git \ jq \ lcov \ + libboost-all-dev \ libcurl4-openssl-dev \ libczmq-dev \ + libgdal-dev \ libgeos++-dev \ libgeos-dev \ libluajit-5.1-dev \ @@ -37,10 +39,11 @@ env DEBIAN_FRONTEND=noninteractive apt-get install --yes --quiet \ make \ osmium-tool \ parallel \ - pkg-config \ + pkgconf \ protobuf-compiler \ python3-all-dev \ python3-shapely \ + python3-requests \ python3-pip \ spatialite-bin \ unzip \ @@ -48,13 +51,10 @@ env DEBIAN_FRONTEND=noninteractive apt-get install --yes --quiet \ # build prime_server from source # readonly primeserver_version=0.7.0 -readonly primeserver_dir=/usr/local/src/prime_server +readonly primeserver_dir=/tmp/prime_server git clone --recurse-submodules https://github.com/kevinkreiser/prime_server $primeserver_dir pushd $primeserver_dir -./autogen.sh && ./configure && \ -make -j${CONCURRENCY:-$(nproc)} install && \ -popd && \ -rm -r $primeserver_dir - -# for boost -python3 -m pip install --upgrade "conan<2.0.0" requests shapely +./autogen.sh && ./configure +make -j${CONCURRENCY:-$(nproc)} +sudo make install +popd && rm -rf $primeserver_dir diff --git a/scripts/needs_ci_run b/scripts/needs_ci_run deleted file mode 100755 index fbd6b51132..0000000000 --- a/scripts/needs_ci_run +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env python3 -import fnmatch -from os import path, environ -import requests -import sys -import subprocess - -CIRCLE_JOB_STATUS_SUCCESS = 'success' - -''' -Returns a tuple of status & gitsha of the PR that triggered the build. -status is True if all jobs of the last build were in 'success' state -otherwise False - -Each push to a branch triggers a new CI run, and any previous run is cancelled. -A CI run starts 4 jobs (see JOBS above) and each job has a unique "workflow ID" -attached to it. This workflow ID changes for every CI run. In order to get the -status of the last run, we do the following: -- Get status of last 8 jobs from the PR branch - 4 jobs will be from the - current run and 4 from the previous run -- Ignore jobs of the current workflow (using workflow ID) -- The last run is successful if ALL jobs had a status of 'success' -- Store the gitsha of the last successful run. This is later used to fetch the - list of files changed since last CI run -''' - - -def last_build_successful(): - branch = environ.get('CIRCLE_BRANCH') - current_workflow_id = environ.get('CIRCLE_WORKFLOW_ID') - token = environ.get('CIRCLE_API_TOKEN') - - if not branch: - print('CIRCLE_BRANCH env not set! Unable to do CI skip check', file=sys.stderr) - return False, None - if not current_workflow_id: - print('CIRCLE_WORKFLOW_ID env not set! Unable to do CI skip check', file=sys.stderr) - return False, None - if not token: - print('CIRCLE_API_TOKEN env not set! Unable to do CI skip check', file=sys.stderr) - return False, None - - # jobs defined in .circleci/config.yml - JOBS = ['lint-build-debug', 'build-release', 'build-osx'] - - # Get twice the number of jobs configured. There would be len(JOBS) from this - # run, while the rest will be from the last run - cci_api_url = f'https://circleci.com/api/v1.1/project/github/valhalla/valhalla/tree/{branch}?circle-token={token}&shallow=true&limit={len(JOBS)*2}' - - resp = requests.get(cci_api_url) - resp.raise_for_status() - - builds = resp.json() - # print(builds, file=sys.stderr) - - # this should never happen. since this script is run from within a CI run - # there should be at least 1 entry (i.e this run) - if not builds: - print('ERROR! No previous builds found! Not skipping build', file=sys.stderr) - return False, None - - # build a dict of {job_name: (job_status, gitsha)} - job_status = {} - for build in builds: - # ignore this build's status. All jobs in a build have the same - # workflow ID - if build['workflows']['workflow_id'] == current_workflow_id: - continue - job_status[build['workflows']['job_name']] = (build['status'], build['vcs_revision']) - - if sorted(job_status.keys()) != sorted(JOBS): - print( - f'Unexpected job name fetched ({sorted(job_status)}). Expected {sorted(JOBS)}', - file=sys.stderr, - ) - return False, None - - # Return False if any of the jobs were not in 'success' state - for status, gitsha in job_status.values(): - if status != 'success': - return False, gitsha - - return True, job_status[JOBS[0]][1] - - -CI_IGNORE_FILE = '.circleci/ciignore' -SKIP_CI = "True" - -if not path.isfile(CI_IGNORE_FILE): - # no CI ignore rules configured, run CI - print('needs CI: no ciignore file', file=sys.stderr) - sys.exit(0) - -# Check if a previous build of this branch was successful or not -status, last_pr_gitsha = last_build_successful() -print(f'last build successful?, gitsha: {status}, {last_pr_gitsha}', file=sys.stderr) - -# if last build was not successfull, dont skip -# TODO: even on first build we shouldn't run CI for irrelevant files.. -if not status: - print('needs CI: last build was not successful', file=sys.stderr) - sys.exit(0) - -# if commit does not exist anymore, dont skip -git_type = subprocess.run( - ['git', 'cat-file', '-t', f'{last_pr_gitsha}'], capture_output=True, text=True -).stdout -if 'commit' not in git_type: - print('needs CI: last commit doesnt exist anymore', file=sys.stderr) - sys.exit(0) - - -# Get all changes since the last succesful CI run -GIT_CMD = ['git', 'diff', f'HEAD..{last_pr_gitsha}', '--name-only'] - -# Get list of changed files staged for commit -changed_files = subprocess.run(GIT_CMD, capture_output=True, text=True).stdout.splitlines() -# Get CI ignore rules -with open(CI_IGNORE_FILE, 'r') as f: - CI_IGNORE_RULES = f.readlines() -CI_IGNORE_RULES = [x.strip() for x in CI_IGNORE_RULES] - -ignored_files = [] -for changed_file in changed_files: - for ignore_rule in CI_IGNORE_RULES: - if fnmatch.fnmatch(changed_file, ignore_rule): - ignored_files.append(changed_file) - -print(f"changed files: {changed_files}", file=sys.stderr) -print(f"ignored files: {ignored_files}", file=sys.stderr) - -if sorted(ignored_files) == sorted(changed_files): - # everything was ignored, skip CI - print( - 'skipping CI because the changed stuff and the stuff we want to ignore is the same', - file=sys.stderr, - ) - print('skip CI') -else: - # Not everything was ignored, run CI - print('needs CI', file=sys.stderr) diff --git a/scripts/update_public_server.sh b/scripts/update_public_server.sh new file mode 100644 index 0000000000..9777434f46 --- /dev/null +++ b/scripts/update_public_server.sh @@ -0,0 +1,42 @@ +# updates Valhalla on the FOSSGIS servers +# accepts one argument, either "builder" or "service" + +set -e + +server=$1 +src_dir="/src/valhalla" + +git config --global --add safe.directory /src/valhalla + +git -C "${src_dir}" checkout master +git -C "${src_dir}" pull +git submodule update --init --recursive + +# remove the build folder first +rm -r "${src_dir}"/build + +if [[ $server == "builder" ]]; then + cmake -S "${src_dir}" -B "${src_dir}/build" \ + -DENABLE_TOOLS=OFF \ + -DENABLE_SERVICES=OFF \ + -DENABLE_HTTP=OFF \ + -DENABLE_PYTHON_BINDINGS=OFF \ + -DENABLE_TESTS=OFF \ + -DENABLE_SINGLE_FILES_WERROR=OFF \ + -DENABLE_GDAL=OFF + + sudo make -C "${src_dir}/build" -j$(nproc) install + # config is updated by the build script on the server +else + cmake -S "${src_dir}" -B "${src_dir}/build" \ + -DENABLE_DATA_TOOLS=OFF \ + -DENABLE_SERVICES=ON \ + -DENABLE_HTTP=ON \ + -DENABLE_PYTHON_BINDINGS=OFF \ + -DENABLE_TESTS=OFF \ + -DENABLE_SINGLE_FILES_WERROR=OFF + + sudo make -C "${src_dir}/build" -j$(nproc) install + # Update the configs + /opt/valhalla/runner_build_config.sh 8000 && /opt/valhalla/runner_build_config.sh 8001 +fi diff --git a/scripts/valhalla_build_config b/scripts/valhalla_build_config index a858319b9b..caea2e863e 100755 --- a/scripts/valhalla_build_config +++ b/scripts/valhalla_build_config @@ -38,6 +38,7 @@ config = { 'incident_log': Optional(str), 'shortcut_caching': Optional(bool), 'admin': '/data/valhalla/admin.sqlite', + 'landmarks': '/data/valhalla/landmarks.sqlite', 'timezone': '/data/valhalla/tz_world.sqlite', 'transit_dir': '/data/valhalla/transit', 'transit_feeds_dir': '/data/valhalla/transit_feeds', @@ -60,6 +61,7 @@ config = { 'infer_internal_intersections': True, 'infer_turn_channels': True, 'apply_country_overrides': True, + 'grid_divisions_within_tile': 32, 'use_admin_db': True, 'use_direction_on_ways': False, 'allow_alt_name': False, @@ -116,6 +118,9 @@ config = { 'max_reserved_labels_count_bidir_astar': 1000000, 'max_reserved_labels_count_dijkstras': 4000000, 'max_reserved_labels_count_bidir_dijkstras': 2000000, + 'costmatrix_check_reverse_connection': False, + 'costmatrix_allow_second_pass': False, + 'max_reserved_locations_costmatrix': 25, 'clear_reserved_memory': False, 'extended_search': False, }, @@ -294,11 +299,12 @@ help_text = { 'traffic_extract': 'Location to read traffic from tar', 'incident_dir': 'Location to read incident tiles from', 'incident_log': 'Location to read change events of incident tiles', - 'shortcut_caching': 'Precaches the superceded edges of all shortcuts in the graph. Defaults to false', + 'shortcut_caching': 'Precaches the superseded edges of all shortcuts in the graph. Defaults to false', 'admin': 'Location of sqlite file holding admin polygons created with valhalla_build_admins', + 'landmarks': 'Location of sqlite file holding landmark POI created with valhalla_build_landmarks', 'timezone': 'Location of sqlite file holding timezone information created with valhalla_build_timezones', 'transit_dir': 'Location of intermediate transit tiles created with valhalla_build_transit', - 'transit_feeds_dir': 'Location of GTFS transit feeds', + 'transit_feeds_dir': 'Location of all GTFS transit feeds, needs to contain one subdirectory per feed', 'transit_bounding_box': 'Add comma separated bounding box values to only download transit data inside the given bounding box', 'transit_pbf_limit': 'Limit individual PBF files to this many trips (needed for PBF\'s stupid size limit)', 'hierarchy': 'bool indicating whether road hierarchy is to be built - default to True', @@ -318,6 +324,7 @@ help_text = { 'infer_internal_intersections': 'bool indicating whether or not to infer internal intersections during the graph enhancer phase or use the internal_intersection key from the pbf', 'infer_turn_channels': 'bool indicating whether or not to infer turn channels during the graph enhancer phase or use the turn_channel key from the pbf', 'apply_country_overrides': 'bool indicating whether or not to apply country overrides during the graph enhancer phase', + 'grid_divisions_within_tile': 'number of grid subdivisions within a tile. Used for spatial sorting of nodes within a tile. Set to 0 to disable spatial sorting of nodes', 'use_admin_db': 'bool indicating whether or not to use the administrative database during the graph enhancer phase or use the admin keys from the pbf that are set on the node', 'use_direction_on_ways': 'bool indicating whether or not to process the direction key on the ways or utilize the guidance relation tags during the parsing phase', 'allow_alt_name': 'bool indicating whether or not to process the alt_name key on the ways during the parsing phase', @@ -362,12 +369,15 @@ help_text = { 'file_name': 'Output log file for the file logger', 'long_request': 'Value used in processing to determine whether it took too long', }, - 'source_to_target_algorithm': 'TODO: which matrix algorithm should be used', + 'source_to_target_algorithm': 'Which matrix algorithm should be used, one of "timedistancematrix" or "costmatrix". If blank, the optimal will be selected.', + 'costmatrix_check_reverse_connection': 'Whether to check for expansion connections on the reverse tree, which has an adverse effect on performance', + 'costmatrix_allow_second_pass': "Whether to allow a second pass for unfound CostMatrix connections, where we turn off destination-only, relax hierarchies and expand into 'semi-islands'b", 'service': {'proxy': 'IPC linux domain socket file location'}, 'max_reserved_labels_count_astar': 'Maximum capacity allowed to keep reserved for unidirectional A*.', 'max_reserved_labels_count_bidir_astar': 'Maximum capacity allowed to keep reserved for bidirectional A*.', 'max_reserved_labels_count_dijkstras': 'Maximum capacity allowed to keep reserved for unidirectional Dijkstras.', 'max_reserved_labels_count_bidir_dijkstras': 'Maximum capacity allowed to keep reserved for bidirectional Dijkstras.', + 'max_reserved_locations_costmatrix': 'Maximum amount of locations allowed to to keep reserved between requests for CostMatrix', 'clear_reserved_memory': 'If True clean reserved memory in path algorithms', 'extended_search': 'If True and 1 side of the bidirectional search is exhausted, causes the other side to continue if the starting location of that side began on a not_thru or closed edge', }, @@ -575,7 +585,7 @@ def add_leaf_args( # its either required and is the right type or optional and has a type to use # lists are supported as comma separated and bools support a bunch of conventions - # the rest of the primatives (strings and numbers) parse automatically + # the rest of the primitives (strings and numbers) parse automatically if isinstance(tree, Optional): t = tree.type elif isinstance(tree, list): @@ -586,7 +596,10 @@ def add_leaf_args( t = type(tree) arg = '--' + path.replace('_', '-').replace('\0', '-') - parser_.add_argument(arg, type=t, help=h, default=tree) + if t == list: + parser_.add_argument(arg, nargs="+", help=h, default=tree) + else: + parser_.add_argument(arg, type=t, help=h, default=tree) leaves_.append(path) diff --git a/scripts/valhalla_build_elevation b/scripts/valhalla_build_elevation index 009830058d..5f219e0001 100755 --- a/scripts/valhalla_build_elevation +++ b/scripts/valhalla_build_elevation @@ -13,9 +13,10 @@ from multiprocessing.dummy import Pool as ThreadPool import os from pathlib import Path import sys +from time import sleep from typing import List, Iterable, Set from urllib import request -from urllib.error import URLError +from urllib.error import URLError, HTTPError # hack so ArgumentParser can accept negative numbers # see https://github.com/valhalla/valhalla/issues/3426 @@ -53,6 +54,12 @@ class TileCompression(Enum): return ".lz4" +class DownloadStatus(Enum): + OK = 1 + FAILED = 2 + CORRUPTED = 3 + + parser = argparse.ArgumentParser(description=description) method = parser.add_mutually_exclusive_group() method.add_argument( @@ -81,6 +88,13 @@ parser.add_argument( "If present, can be used for download location and bbox.", type=Path, ) +parser.add_argument( + "-i", + "--inline-config", + help="Inline JSON config, will override --config JSON if present", + type=str, + default='{}', +) parser.add_argument( "-o", "--outdir", @@ -109,7 +123,7 @@ group.add_argument( "-z", "--lz4", help="If set, downloaded files will be recompressed with LZ4. Requires lz4. " - "Requires ~12% more disk space (216GB total) vs gzip. While you pay upfront in extra CPU and space on the " + "Requires ~12%% more disk space (216GB total) vs gzip. While you pay upfront in extra CPU and space on the " "initial download, tiles will be several times faster to decompress vz gzip.", action="store_true", ) @@ -225,7 +239,7 @@ def get_tiles_with_graph(graph_dir: Path) -> Set[Tile]: return tile_infos -def download(tile: Tile, output_dir, compression: TileCompression): +def download(tile: Tile, output_dir, compression: TileCompression) -> bool: dest_directory = Path(output_dir, tile.dir) dest_directory.mkdir(parents=True, exist_ok=True) @@ -237,39 +251,71 @@ def download(tile: Tile, output_dir, compression: TileCompression): url = f"https://elevation-tiles-prod.s3.us-east-1.amazonaws.com/skadi/{tile.dir}/{tile.name}.gz" LOGGER.info(f"Downloading tile {tile.name}") - try: - with request.urlopen(url) as res, open(filepath, "wb") as f: - if compression is TileCompression.GZIP: - f.write(res.read()) - else: - with gzip.GzipFile(fileobj=res, mode="rb") as gz: - uncompressed = gz.read() - if compression is TileCompression.UNCOMPRESSED: - f.write(uncompressed) - elif compression is TileCompression.LZ4: - # Compression level 6 was chosen after some benchmarking as the approx efficient frontier - # between compression time and space savings (decompression time is roughly constant regardless - # of level). The end result is larger than the maximally gzipped tiles from AWS, but only - # by around 12%. - import lz4.frame - - with lz4.frame.LZ4FrameCompressor( - block_size=lz4.frame.BLOCKSIZE_MAX4MB, compression_level=6 - ) as compressor: - # Optimization: we know the exact size of every uncompressed hgt file - f.write(compressor.begin(25934402)) - f.write(compressor.compress(uncompressed)) - f.write(compressor.flush()) - - LOGGER.debug(f"Successfully downloaded tile {tile.name}") - - return True - except URLError as e: - LOGGER.critical(f"Download failed of elevation tile {tile.dir}/{tile.name}: {e.reason}") + + download_status = DownloadStatus.FAILED + for i in range(5): + try: + LOGGER.debug(f"Downloading tile {tile.dir}/{tile.name} for the {i}th time.") + + # tries up to 24 mins with exponentially increasing sleeps in between, starting with 5 seconds + sleep((i**2) / 2 * 30) + with request.urlopen(url) as res, open(filepath, "wb") as f: + + if compression is TileCompression.GZIP: + f.write(res.read()) + else: + with gzip.GzipFile(fileobj=res, mode="rb") as gz: + try: + uncompressed = gz.read() + except Exception as e: + download_status = DownloadStatus.CORRUPTED + LOGGER.error( + f"Decompression error on tile {tile.dir}/{tile.name}: {e}. Likely a corrupted download." + ) + continue + if compression is TileCompression.UNCOMPRESSED: + f.write(uncompressed) + elif compression is TileCompression.LZ4: + # Compression level 6 was chosen after some benchmarking as the approx efficient frontier + # between compression time and space savings (decompression time is roughly constant regardless + # of level). The end result is larger than the maximally gzipped tiles from AWS, but only + # by around 12%. + import lz4.frame + + with lz4.frame.LZ4FrameCompressor( + block_size=lz4.frame.BLOCKSIZE_MAX4MB, compression_level=6 + ) as compressor: + # Optimization: we know the exact size of every uncompressed hgt file + f.write(compressor.begin(25934402)) + f.write(compressor.compress(uncompressed)) + f.write(compressor.flush()) + + download_status = DownloadStatus.OK + LOGGER.debug(f"Successfully downloaded tile {tile.dir}/{tile.name}") + break + except HTTPError as e: + LOGGER.error(f"Download failed with HTTP error {e.code}: {e.reason}.\nTrying again...") + continue + except URLError as e: + LOGGER.error( + f"Download failed of elevation tile {tile.dir}/{tile.name}: {e.reason}.\nTrying again.." + ) + continue + except ImportError: + LOGGER.critical( + "Could not import lz4. Please install lz4 or use another compression format." + ) + sys.exit(1) + + if download_status == DownloadStatus.CORRUPTED: + LOGGER.error(f"Tile {tile.dir}/{tile.name} was corrupted, removing it...") + filepath.unlink() return False - except ImportError: - LOGGER.critical("Could not import lz4. Please install lz4 or use another compression format.") - sys.exit(1) + elif download_status == DownloadStatus.FAILED: + LOGGER.error(f"Tile {tile.dir}/{tile.name} couldn't be downloaded...") + return False + + return True if __name__ == "__main__": @@ -283,14 +329,17 @@ if __name__ == "__main__": elif args.verbosity >= 2: LOGGER.setLevel(logging.DEBUG) - config = None + config = dict() if args.config: with open(args.config) as f: config = json.load(f) + if args.inline_config: + config.update(**json.loads(args.inline_config)) + if args.outdir: elevation_fp = args.outdir - elif config is not None: + elif config: elevation_fp = Path(config["additional_data"]["elevation"] or "elevation") else: LOGGER.critical("Either config or outdir is required.") @@ -301,7 +350,7 @@ if __name__ == "__main__": elif args.from_bbox: tiles = get_tiles_with_bbox(args.from_bbox) elif args.from_tiles: - if config is None: + if not config: LOGGER.critical("--from-tiles requires a config to be specified.") sys.exit(1) tiles = get_tiles_with_graph(Path(config["mjolnir"]["tile_dir"])) diff --git a/scripts/valhalla_build_extract b/scripts/valhalla_build_extract index 7eac911ffa..236822513c 100755 --- a/scripts/valhalla_build_extract +++ b/scripts/valhalla_build_extract @@ -13,7 +13,7 @@ import sys import tarfile from tarfile import BLOCKSIZE from time import time -from typing import List, Tuple, Union, Set +from typing import List, Tuple, Optional # "<" prefix means little-endian and no alignment # order is important! if uint64_t is not first, c++ will use padding bytes to unpack @@ -22,8 +22,10 @@ INDEX_BIN_SIZE = struct.calcsize(INDEX_BIN_FORMAT) INDEX_FILE = "index.bin" # skip the first 40 bytes of the tile header GRAPHTILE_SKIP_BYTES = struct.calcsize(' Tuple[float, float, float, float]: return tile_base_x, tile_base_y, tile_base_x + tile_size, tile_base_y + tile_size -def get_tiles_with_geojson(all_tile_paths: List[Path], geojson_dir: Path, tiles_dir_: Path) -> Set[Path]: +def get_tiles_with_geojson(tile_resolver_: TileResolver, geojson_dir: Path): """Returns all tile paths intersecting with the GeoJSON (multi)polygons""" try: from shapely.geometry import Polygon, box @@ -144,19 +199,15 @@ def get_tiles_with_geojson(all_tile_paths: List[Path], geojson_dir: Path, tiles_ polygons.append(Polygon(single_polygon[0])) return polygons - tile_paths_ = set() geojson_polys = get_outer_rings(geojson_dir) - for tile_path in all_tile_paths: - tile_path_id = str(tile_path.relative_to(tiles_dir_)) - tile_bbox = box(*get_tile_bbox(tile_path_id)) + for tile_path in tile_resolver_.normalized_tile_paths: + tile_bbox = box(*get_tile_bbox(str(tile_path))) for poly in geojson_polys: if poly.intersects(tile_bbox): - tile_paths_.add(tile_path) - - return tile_paths_ + tile_resolver_.matched_paths.append(tile_path) -def get_tiles_with_bbox(all_tile_paths: List[Path], bbox_str: str, tiles_dir_: Path) -> Set[Path]: +def get_tiles_with_bbox(tile_resolver_: TileResolver, bbox_str: str): """Returns all tile paths intersecting with the bbox""" try: bbox = Bbox(*[float(x) for x in bbox_str.split(",")]) @@ -171,11 +222,9 @@ def get_tiles_with_bbox(all_tile_paths: List[Path], bbox_str: str, tiles_dir_: P LOGGER.critical(f"Bbox invalid: {list(bbox)}") sys.exit(1) - tile_paths_ = set() - for tile_path in all_tile_paths: - tile_path_id = str(tile_path.relative_to(tiles_dir_)) - tile_bbox = Bbox(*get_tile_bbox(tile_path_id)) - # check if tile_bbox is outside of bbox + for tile_path in tile_resolver_.normalized_tile_paths: + tile_bbox = Bbox(*get_tile_bbox(str(tile_path))) + # check if tile_bbox is outside bbox if not any( [ tile_bbox.min_x < bbox.min_x and tile_bbox.max_x < bbox.min_x, # left of bbox @@ -184,9 +233,7 @@ def get_tiles_with_bbox(all_tile_paths: List[Path], bbox_str: str, tiles_dir_: P tile_bbox.min_y > bbox.max_y and tile_bbox.max_y > bbox.max_y, # above bbox ] ): - tile_paths_.add(tile_path) - - return tile_paths_ + tile_resolver_.matched_paths.append(tile_path) def get_tile_level_id(path: str) -> List[str]: @@ -232,19 +279,11 @@ def write_index_to_tar(tar_fp_: Path): tar.write(struct.pack(INDEX_BIN_FORMAT, *entry)) -def create_extracts(config_: dict, do_traffic: bool, tile_paths_: Union[Set[Path], List[Path]]): +def create_extracts(config_: dict, do_traffic: bool, tile_resolver_: TileResolver, extract_fp: Path): """Actually creates the tar ball. Break out of main function for testability.""" - tiles_fp: Path = Path(config_["mjolnir"].get("tile_dir", '/dev/null')) - extract_fp: Path = Path( - config_["mjolnir"].get("tile_extract") or tiles_fp.parent.joinpath('tiles.tar') - ) - traffic_fp: Path = Path( - config_["mjolnir"].get("traffic_extract") or tiles_fp.parent.joinpath('traffic.tar') - ) - - tiles_count = len(tile_paths_) + tiles_count = len(tile_resolver_.matched_paths) if not tiles_count: - LOGGER.critical(f"Directory {tiles_fp.resolve()} does not contain any usable graph tiles.") + LOGGER.critical(f"Couldn't find usable tiles in {tile_resolver_.path}") sys.exit(1) # write the in-memory index file @@ -254,12 +293,10 @@ def create_extracts(config_: dict, do_traffic: bool, tile_paths_: Union[Set[Path # first add the index file, then the sorted tiles to the tarfile # TODO: come up with a smarter strategy to cluster the tiles in the tar + extract_fp.parent.mkdir(parents=True, exist_ok=True) with tarfile.open(extract_fp, 'w') as tar: tar.addfile(get_tar_info(INDEX_FILE, index_size), index_fd) - for t in tile_paths_: - rel_path = str(t.relative_to(tiles_fp)) - LOGGER.debug(f"Adding tile {rel_path} to the path") - tar.add(str(t.resolve()), arcname=rel_path) + tile_resolver_.add_to_tar(tar) write_index_to_tar(extract_fp) @@ -271,6 +308,9 @@ def create_extracts(config_: dict, do_traffic: bool, tile_paths_: Union[Set[Path sys.exit(0) LOGGER.info("Start creating traffic extract...") + traffic_fp: Path = Path( + config_["mjolnir"].get("traffic_extract") or extract_fp.parent.joinpath("traffic.tar") + ) # we already have the right size of the index file, simply reset it index_fd.seek(0) @@ -296,11 +336,27 @@ def create_extracts(config_: dict, do_traffic: bool, tile_paths_: Union[Set[Path b.readinto(tile_header) b.close() + # create the traffic header + header_bytes = struct.pack( + TRAFFIC_HEADER_FORMAT, + get_tile_id(tile_in.name), # numerical tile_id + 0, # timestamp + tile_header.directededgecount_, # edge count + TRAFFIC_VERSION, # tile version + 0, # spare2 + 0, # spare3 + ) + # create the traffic tile - traffic_size = TRAFFIC_HEADER_SIZE + TRAFFIC_SPEED_SIZE * tile_header.directededgecount_ - tar_traffic.addfile(get_tar_info(tile_in.name, traffic_size), BytesIO(b'\0' * traffic_size)) + traffic_size = TRAFFIC_SPEED_SIZE * tile_header.directededgecount_ + tar_traffic.addfile( + get_tar_info(tile_in.name, len(header_bytes) + traffic_size), + BytesIO(header_bytes + b'\0' * traffic_size), + ) - LOGGER.debug(f"Tile {tile_in.name} has {tile_header.directededgecount_} directed edges") + LOGGER.debug( + f"Traffic tile {tile_in.name} has {tile_header.directededgecount_} directed edges" + ) write_index_to_tar(traffic_fp) @@ -310,6 +366,14 @@ def create_extracts(config_: dict, do_traffic: bool, tile_paths_: Union[Set[Path if __name__ == '__main__': args = parser.parse_args() + # set the right logger level + if args.verbosity == 0: + LOGGER.setLevel(logging.CRITICAL) + elif args.verbosity == 1: + LOGGER.setLevel(logging.INFO) + elif args.verbosity >= 2: + LOGGER.setLevel(logging.DEBUG) + if not args.config and not args.inline_config: LOGGER.critical("No valid config file or inline config used.") sys.exit(1) @@ -324,27 +388,39 @@ if __name__ == '__main__': # override with inline-config config.update(**json.loads(args.inline_config)) - # get and validate the tiles directory + # if output file exists and not --overwrite specified, fail + tiles_extract_out = args.extract_tar or config["mjolnir"].get("tile_extract") + if not tiles_extract_out: + LOGGER.critical("No output file path specified in 'mjolnir.tile_extract' or --extract-tar.") + sys.exit(1) + + tiles_extract_out = Path(tiles_extract_out) + if tiles_extract_out.is_file() and not args.overwrite: + LOGGER.critical(f"File exists. Specify --overwrite to overwrite {tiles_extract_out}") + sys.exit(1) + + # prefer the tar file to extract from, fall back to the tile dir tiles_dir: Path = Path(config["mjolnir"].get("tile_dir", '/dev/null')) - if not tiles_dir.is_dir(): + # optionally use the tile extract to find the tiles + if args.extract_tar and (tiles_extract_in := Path(config["mjolnir"].get("tile_extract"))).is_file(): + tile_resolver = TileResolver(tiles_extract_in) + LOGGER.debug("Using tar file to extract tiles") + # else get and validate the tiles directory + elif tiles_dir.is_dir(): + tile_resolver = TileResolver(tiles_dir) + LOGGER.debug("Using graph dir to extract tiles") + else: LOGGER.critical( - f"Directory 'mjolnir.tile_dir': {tiles_dir.resolve()} was not found on the filesystem." + f"'Can't find valid paths for 'mjolnir.tile_dir' or 'mjolnir.tile_extract' in {args.config}" ) sys.exit(1) - # get the tile paths which intersect with the geom, if any - tile_paths: Union[Set[Path], List[Path]] = sorted(tiles_dir.rglob('*.gph')) + # get the tile paths which intersect with the geom, if any, and write to TileResolver if args.bbox: - tile_paths = get_tiles_with_bbox(tile_paths, args.bbox, tiles_dir) + get_tiles_with_bbox(tile_resolver, args.bbox) elif args.geojson_dir: - tile_paths = get_tiles_with_geojson(tile_paths, args.geojson_dir, tiles_dir) - - # set the right logger level - if args.verbosity == 0: - LOGGER.setLevel(logging.CRITICAL) - elif args.verbosity == 1: - LOGGER.setLevel(logging.INFO) - elif args.verbosity >= 2: - LOGGER.setLevel(logging.DEBUG) + get_tiles_with_geojson(tile_resolver, args.geojson_dir) + else: + tile_resolver.matched_paths = tile_resolver.normalized_tile_paths - create_extracts(config, args.with_traffic, tile_paths) + create_extracts(config, args.with_traffic, tile_resolver, tiles_extract_out) diff --git a/scripts/valhalla_build_timezones b/scripts/valhalla_build_timezones index aea0276942..fb47dbb9fb 100755 --- a/scripts/valhalla_build_timezones +++ b/scripts/valhalla_build_timezones @@ -21,230 +21,18 @@ fi rm -rf dist rm -f ./timezones-with-oceans.shapefile.zip -url="https://github.com/evansiroky/timezone-boundary-builder/releases/download/2018d/timezones-with-oceans.shapefile.zip" - -cat << EOF > alias_tz.csv -Pacific/Ponape,Pacific/Pohnpei -Pacific/Midway,Pacific/Pago_Pago -Pacific/Samoa,Pacific/Pago_Pago -US/Samoa,Pacific/Pago_Pago -Kwajalein,Pacific/Kwajalein -Pacific/Johnston,Pacific/Honolulu -US/Hawaii,Pacific/Honolulu -Pacific/Saipan,Pacific/Guam -Chile/EasterIsland,Pacific/Easter -Pacific/Truk,Pacific/Chuuk -Pacific/Yap,Pacific/Chuuk -NZ-CHAT,Pacific/Chatham -Antarctica/McMurdo,Pacific/Auckland -Antarctica/South_Pole,Pacific/Auckland -NZ,Pacific/Auckland -Europe/Busingen,Europe/Zurich -Europe/Vaduz,Europe/Zurich -Poland,Europe/Warsaw -Europe/San_Marino,Europe/Rome -Europe/Vatican,Europe/Rome -Europe/Bratislava,Europe/Prague -Arctic/Longyearbyen,Europe/Oslo -Atlantic/Jan_Mayen,Europe/Oslo -W-SU,Europe/Moscow -Europe/Belfast,Europe/London -Europe/Guernsey,Europe/London -Europe/Isle_of_Man,Europe/London -Europe/Jersey,Europe/London -GB,Europe/London -GB-Eire,Europe/London -Portugal,Europe/Lisbon -Asia/Istanbul,Europe/Istanbul -Turkey,Europe/Istanbul -Europe/Mariehamn,Europe/Helsinki -Eire,Europe/Dublin -Europe/Tiraspol,Europe/Chisinau -Europe/Ljubljana,Europe/Belgrade -Europe/Podgorica,Europe/Belgrade -Europe/Sarajevo,Europe/Belgrade -Europe/Skopje,Europe/Belgrade -Europe/Zagreb,Europe/Belgrade -UTC,Etc/UTC -Etc/Universal,Etc/UTC -Etc/Zulu,Etc/UTC -Universal,Etc/UTC -Zulu,Etc/UTC -UCT,Etc/UCT -Etc/GMT+0,Etc/GMT -Etc/GMT0,Etc/GMT -Etc/GMT-0,Etc/GMT -GMT,Etc/GMT -Etc/Greenwich,Etc/GMT -GMT+0,Etc/GMT -GMT0,Etc/GMT -GMT−0,Etc/GMT -Greenwich,Etc/GMT -Australia/Canberra,Australia/Sydney -Australia/ACT,Australia/Sydney -Australia/NSW,Australia/Sydney -Australia/West,Australia/Perth -Australia/Victoria,Australia/Melbourne -Australia/LHI,Australia/Lord_Howe -Australia/Tasmania,Australia/Hobart -Australia/North,Australia/Darwin -Australia/Yancowinna,Australia/Broken_Hill -Australia/Queensland,Australia/Brisbane -Australia/South,Australia/Adelaide -Iceland,Atlantic/Reykjavik -Atlantic/Faeroe,Atlantic/Faroe -Asia/Rangoon,Asia/Yangon -Asia/Kashgar,Asia/Urumqi -Asia/Ulan_Bator,Asia/Ulaanbaatar -Japan,Asia/Tokyo -Asia/Thimbu,Asia/Thimphu -Iran,Asia/Tehran -ROC,Asia/Taipei -Singapore,Asia/Singapore -Asia/Chongqing,Asia/Shanghai -Asia/Chungking,Asia/Shanghai -Asia/Harbin,Asia/Shanghai -PRC,Asia/Shanghai -ROK,Asia/Seoul -Asia/Aden,Asia/Riyadh -Asia/Kuwait,Asia/Riyadh -Asia/Bahrain,Asia/Qatar -Asia/Ujung_Pandang,Asia/Makassar -Asia/Macao,Asia/Macau -Asia/Calcutta,Asia/Kolkata -Asia/Katmandu,Asia/Kathmandu -Asia/Tel_Aviv,Asia/Jerusalem -Israel,Asia/Jerusalem -Hongkong,Asia/Hong_Kong -Asia/Saigon,Asia/Ho_Chi_Minh -Asia/Muscat,Asia/Dubai -Asia/Dacca,Asia/Dhaka -Asia/Phnom_Penh,Asia/Bangkok -Asia/Vientiane,Asia/Bangkok -Asia/Ashkhabad,Asia/Ashgabat -Canada/Central,America/Winnipeg -Canada/Yukon,America/Whitehorse -Canada/Pacific,America/Vancouver -America/Montreal,America/Toronto -Canada/Eastern,America/Toronto -America/Ensenada,America/Tijuana -America/Santa_Isabel,America/Tijuana -Mexico/BajaNorte,America/Tijuana -Canada/Newfoundland,America/St_Johns -Brazil/East,America/Sao_Paulo -Chile/Continental,America/Santiago -America/Porto_Acre,America/Rio_Branco -Brazil/Acre,America/Rio_Branco -Canada/Saskatchewan,America/Regina -America/Anguilla,America/Port_of_Spain -America/Antigua,America/Port_of_Spain -America/Dominica,America/Port_of_Spain -America/Grenada,America/Port_of_Spain -America/Guadeloupe,America/Port_of_Spain -America/Marigot,America/Port_of_Spain -America/Montserrat,America/Port_of_Spain -America/St_Barthelemy,America/Port_of_Spain -America/St_Kitts,America/Port_of_Spain -America/St_Lucia,America/Port_of_Spain -America/St_Thomas,America/Port_of_Spain -America/St_Vincent,America/Port_of_Spain -America/Tortola,America/Port_of_Spain -America/Virgin,America/Port_of_Spain -US/Arizona,America/Phoenix -America/Cayman,America/Panama -Brazil/DeNoronha,America/Noronha -US/Eastern,America/New_York -Mexico/General,America/Mexico_City -Mexico/BajaSur,America/Mazatlan -Brazil/West,America/Manaus -US/Pacific,America/Los_Angeles -US/Pacific-New,America/Los_Angeles -America/Louisville,America/Kentucky/Louisville -Jamaica,America/Jamaica -America/Knox_IN,America/Indiana/Knox -US/Indiana-Starke,America/Indiana/Knox -America/Fort_Wayne,America/Indiana/Indianapolis -America/Indianapolis,America/Indiana/Indianapolis -US/East-Indiana,America/Indiana/Indianapolis -Cuba,America/Havana -Canada/Atlantic,America/Halifax -Canada/Mountain,America/Edmonton -US/Michigan,America/Detroit -America/Shiprock,America/Denver -Navajo,America/Denver -US/Mountain,America/Denver -America/Aruba,America/Curacao -America/Kralendijk,America/Curacao -America/Lower_Princes,America/Curacao -US/Central,America/Chicago -America/Coral_Harbour,America/Atikokan -America/Mendoza,America/Argentina/Mendoza -America/Jujuy,America/Argentina/Jujuy -America/Cordoba,America/Argentina/Cordoba -America/Rosario,America/Argentina/Cordoba -America/Argentina/ComodRivadavia,America/Argentina/Catamarca -America/Catamarca,America/Argentina/Catamarca -America/Buenos_Aires,America/Argentina/Buenos_Aires -US/Alaska,America/Anchorage -America/Atka,America/Adak -US/Aleutian,America/Adak -Libya,Africa/Tripoli -Africa/Addis_Ababa,Africa/Nairobi -Africa/Asmara,Africa/Nairobi -Africa/Dar_es_Salaam,Africa/Nairobi -Africa/Djibouti,Africa/Nairobi -Africa/Kampala,Africa/Nairobi -Africa/Mogadishu,Africa/Nairobi -Indian/Antananarivo,Africa/Nairobi -Indian/Comoro,Africa/Nairobi -Indian/Mayotte,Africa/Nairobi -Africa/Blantyre,Africa/Maputo -Africa/Bujumbura,Africa/Maputo -Africa/Gaborone,Africa/Maputo -Africa/Harare,Africa/Maputo -Africa/Kigali,Africa/Maputo -Africa/Lubumbashi,Africa/Maputo -Africa/Lusaka,Africa/Maputo -Africa/Bangui,Africa/Lagos -Africa/Brazzaville,Africa/Lagos -Africa/Douala,Africa/Lagos -Africa/Kinshasa,Africa/Lagos -Africa/Libreville,Africa/Lagos -Africa/Luanda,Africa/Lagos -Africa/Malabo,Africa/Lagos -Africa/Niamey,Africa/Lagos -Africa/Porto-Novo,Africa/Lagos -Africa/Sao_Tome,Africa/Lagos -Africa/Maseru,Africa/Johannesburg -Africa/Mbabane,Africa/Johannesburg -Egypt,Africa/Cairo -Africa/Bamako,Africa/Abidjan -Africa/Banjul,Africa/Abidjan -Africa/Conakry,Africa/Abidjan -Africa/Dakar,Africa/Abidjan -Africa/Freetown,Africa/Abidjan -Africa/Lome,Africa/Abidjan -Africa/Nouakchott,Africa/Abidjan -Africa/Ouagadougou,Africa/Abidjan -Africa/Timbuktu,Africa/Abidjan -Atlantic/St_Helena,Africa/Abidjan -EOF +url="https://github.com/evansiroky/timezone-boundary-builder/releases/download/2024a/timezones-with-oceans.shapefile.zip" echo "downloading timezone polygon file." 1>&2 curl -L -s -o ./timezones-with-oceans.shapefile.zip ${url} || error_exit "curl failed for ${url}" unzip ./timezones-with-oceans.shapefile.zip 1>&2 || error_exit "unzip failed" tz_file=$(mktemp) -spatialite_tool -i -shp ./dist/combined-shapefile-with-oceans -d ${tz_file} -t tz_world -s 4326 -g geom -c UTF-8 1>&2 || error_exit "spatialite_tool import failed" +spatialite_tool -i -shp "./combined-shapefile-with-oceans" -d "${tz_file}" -t tz_world -s 4326 -g geom -c UTF-8 1>&2 || error_exit "spatialite_tool import failed" spatialite ${tz_file} "SELECT CreateSpatialIndex('tz_world', 'geom');" 1>&2 || error_exit "SpatialIndex failed" - -spatialite ${tz_file} "DROP TABLE IF EXISTS "alias";" 1>&2 || error_exit "drop table alias failed" -spatialite ${tz_file} "CREATE TABLE "alias" (alias_TZID text,alias_new_TZID text);" 1>&2 || error_exit "create table alias failed" -spatialite -csv ${tz_file} ".import alias_tz.csv alias" 1>&2 || error_exit "alias table import failed" -spatialite ${tz_file} "update tz_world set TZID = (select alias_new_TZID from alias where TZID = alias_TZID) where TZID in (select alias_TZID from alias);" 1>&2 || error_exit "updating tz_world failed" spatialite ${tz_file} "VACUUM;" 1>&2 || error_exit "VACUUM failed" spatialite ${tz_file} "ANALYZE;" 1>&2 || error_exit "ANALYZE failed" -rm -rf dist +rm -rf ./combined-shapefile-with-oceans* rm -f ./timezones-with-oceans.shapefile.zip alias_tz.csv cat ${tz_file} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bf75b62c3e..c8e9a7940c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,47 +1,14 @@ include(GNUInstallDirs) +include(ValhallaVersion) +include(ValhallaWarnings) -list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") - -## Set relative to the current source and binary paths +## Set relative binary path set(VALHALLA_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) -if(NOT VALHALLA_SOURCE_DIR) - set(VALHALLA_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) -endif() -## Get Valhalla version -file(STRINGS "${VALHALLA_SOURCE_DIR}/valhalla/valhalla.h" version_lines REGEX "VALHALLA_VERSION_(MAJOR|MINOR|PATCH)") -foreach(line ${version_lines}) - if("${line}" MATCHES "(VALHALLA_VERSION_(MAJOR|MINOR|PATCH))[\t ]+([0-9]+)") - set(${CMAKE_MATCH_1} ${CMAKE_MATCH_3}) - set(${CMAKE_MATCH_1} ${CMAKE_MATCH_3} PARENT_SCOPE) - endif() -endforeach() -if(DEFINED VALHALLA_VERSION_MAJOR) - set(VERSION "${VALHALLA_VERSION_MAJOR}") - if(DEFINED VALHALLA_VERSION_MINOR) - set(VERSION "${VERSION}.${VALHALLA_VERSION_MINOR}") - if(DEFINED VALHALLA_VERSION_PATCH) - set(VERSION "${VERSION}.${VALHALLA_VERSION_PATCH}") - endif() - endif() -else() - message(FATAL_ERROR "No Valhalla major version") +if (ENABLE_COMPILER_WARNINGS) + message(STATUS "Enabling compiler warning flags") endif() -## Declare C++ build configuration variables as part of HandleLibcxxFlags. -# -# - LIBCXX_COMPILE_FLAGS: flags used to compile libc++ -# - LIBCXX_LINK_FLAGS: flags used to link libc++ -# - LIBCXX_LIBRARIES: libraries to link libc++ to -set(LIBCXX_COMPILE_FLAGS "") -set(LIBCXX_LINK_FLAGS "") -set(LIBCXX_LIBRARIES "") - -# Inlclude build macros for updating configuration variables -include(HandleLibcxxFlags) - -## Define libvalhalla sub-modules as OBJECT libraries - # TODO: Remove workaround after switching to CMake 3.11 # with fixed https://gitlab.kitware.com/cmake/cmake/issues/14778 # and merged https://gitlab.kitware.com/cmake/cmake/merge_requests/1524 @@ -53,20 +20,7 @@ set(libvalhalla_include_directories "" CACHE INTERNAL "") set(libvalhalla_link_objects "" CACHE INTERNAL "") set(libvalhalla_link_libraries "" CACHE INTERNAL "") -# Setup warning flags. Note that these are only added to libvalhalla_compile_options if -# ENABLE_COMPILER_WARNINGS=ON. Note that this needs to be done before the definition of -# valhalla_module, otherwise LIBCXX_COMPILE_FLAGS will not available for downstream targets. - -if (ENABLE_COMPILER_WARNINGS) - message(STATUS "Enabling compiler warning flags") -endif() - -function (cxx_add_warning_flags target) - target_add_compile_flags_if_supported(${target} PRIVATE -Wall -Wextra) - if (ENABLE_WERROR) - target_add_compile_flags_if_supported(${target} PRIVATE -Werror) - endif() -endfunction() +## Define libvalhalla sub-modules as OBJECT libraries function(valhalla_module) set(oneValueArgs NAME) @@ -82,6 +36,7 @@ function(valhalla_module) endif() list(APPEND MODULE_SOURCES ${MODULE_SOURCES_WITH_WARNINGS}) + # add object or static library set(library valhalla-${MODULE_NAME}) if (ENABLE_STATIC_LIBRARY_MODULES) add_library(${library} STATIC ${MODULE_SOURCES} ${MODULE_HEADERS}) @@ -161,10 +116,6 @@ function(valhalla_module) endif() endfunction() -add_subdirectory(${VALHALLA_SOURCE_DIR}/third_party/robin-hood-hashing ${CMAKE_BINARY_DIR}/third_party/robin-hood-hashing) -SET(CPP_STATSD_STANDALONE OFF CACHE BOOL "ignore test targets" FORCE) -add_subdirectory(${VALHALLA_SOURCE_DIR}/third_party/cpp-statsd-client ${CMAKE_BINARY_DIR}/third_party/cpp-statsd-client) - add_subdirectory(../proto ${CMAKE_CURRENT_BINARY_DIR}/valhalla/proto) add_subdirectory(baldr) add_subdirectory(loki) @@ -184,15 +135,17 @@ endif() set(valhalla_hdrs ${VALHALLA_SOURCE_DIR}/valhalla/valhalla.h ${VALHALLA_SOURCE_DIR}/valhalla/worker.h + ${VALHALLA_SOURCE_DIR}/valhalla/config.h ${VALHALLA_SOURCE_DIR}/valhalla/filesystem.h ${VALHALLA_SOURCE_DIR}/valhalla/proto_conversions.h + ${VALHALLA_SOURCE_DIR}/valhalla/config.h ) set(valhalla_src + config.cc worker.cc filesystem.cc proto_conversions.cc - ${VALHALLA_SOURCE_DIR}/valhalla/config.h ${valhalla_hdrs} ${libvalhalla_link_objects}) @@ -229,28 +182,40 @@ endif() target_include_directories(valhalla PUBLIC ${VALHALLA_SOURCE_DIR} + ${VALHALLA_SOURCE_DIR}/src # For private headers in /src ${VALHALLA_SOURCE_DIR}/valhalla # TODO: this path must be removed and changed to #include in headers ${VALHALLA_BINARY_DIR} ${VALHALLA_BINARY_DIR}/valhalla # TODO: this path must be removed and changed to #include in headers - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include - ${VALHALLA_SOURCE_DIR}/third_party/date/include + ${rapidjson_include_dir} + ${date_include_dir} ${VALHALLA_SOURCE_DIR}/third_party/cpp-statsd-client/include - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> + $<$:${dirent_include_dir}> PRIVATE ${libvalhalla_include_directories}) target_link_libraries(valhalla PUBLIC ${libvalhalla_link_libraries} + $<$:ws2_32> PRIVATE $<$:gcov> - Threads::Threads - cpp-statsd-client) + Threads::Threads) set_target_properties(valhalla PROPERTIES VERSION "${VERSION}" SOVERSION "${VALHALLA_VERSION_MAJOR}") +# pkg-config installation +if(PKG_CONFIG_FOUND) + include(ValhallaPkgConfig) + + configure_valhalla_pc() + + install(FILES + ${CMAKE_BINARY_DIR}/libvalhalla.pc + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") +endif() + install(TARGETS valhalla LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT shared NAMELINK_SKIP @@ -281,20 +246,3 @@ install(FILES ${VALHALLA_SOURCE_DIR}/COPYING ${VALHALLA_SOURCE_DIR}/CHANGELOG.md install(FILES ${valhalla_hdrs} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/valhalla" COMPONENT development) - -if(PKG_CONFIG_FOUND) - ## Configure libvalhalla.pc file with valhalla target linking options via PkgConfig linker - set(CMAKE_PkgConfig_LINK_EXECUTABLE " -DINPUT=${VALHALLA_SOURCE_DIR}/libvalhalla.pc.in -DOUTPUT= -DVERSION=${VERSION} -Dprefix=${CMAKE_INSTALL_PREFIX} -Dexec_prefix=${CMAKE_INSTALL_PREFIX} -Dlibdir=${CMAKE_INSTALL_LIBDIR} -Dincludedir=${CMAKE_INSTALL_INCLUDEDIR} -Ddeplibs=\" \" -P ${CMAKE_SOURCE_DIR}/cmake/PkgConfig.cmake") - - add_executable(libvalhalla.pc EXCLUDE_FROM_ALL ../libvalhalla.pc.in) - target_link_libraries(libvalhalla.pc valhalla) - set_target_properties(libvalhalla.pc PROPERTIES LINKER_LANGUAGE PkgConfig) - set_target_properties(libvalhalla.pc PROPERTIES FOLDER "Library") - install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} --build . --target libvalhalla.pc OUTPUT_QUIET ERROR_VARIABLE _err RESULT_VARIABLE _res) - if(NOT \${_res} EQUAL 0) - message(FATAL_ERROR \"Configuring libvalhalla.pc failed: \${_err}\") - endif()") - install(FILES ${VALHALLA_BINARY_DIR}/libvalhalla.pc - DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" - COMPONENT development) -endif() diff --git a/src/argparse_utils.h b/src/argparse_utils.h new file mode 100644 index 0000000000..e102bad15d --- /dev/null +++ b/src/argparse_utils.h @@ -0,0 +1,81 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include + +/** + * Parses common command line arguments across executables. It + * - alters the config ptree and sets the concurrency config, where it favors the command line arg, + * then falls back to the config and finally to all threads + * - sets the logging configuration + * + * @param program The executable's name + * @param opts The command line options + * @param result The parsed result + * @param config The config which will be populated here + * @param log The logging config node's key. If empty, logging will not be configured. + * @param use_threads Whether this program multi-threads + * @param extra_help Optional function pointer to print more stuff to the end of the help message. + * + * @returns true if the program should continue, false if we should EXIT_SUCCESS + * @throws cxxopts::exceptions::exception Thrown if there's no valid configuration + */ +bool parse_common_args(const std::string& program, + const cxxopts::Options& opts, + const cxxopts::ParseResult& result, + boost::property_tree::ptree& conf, + const std::string& log, + const bool use_threads = false, + std::function extra_help = nullptr) { + if (result.count("help")) { + std::cout << opts.help() << "\n"; + if (extra_help) { + extra_help(); + }; + return false; + } + + if (result.count("version")) { + std::cout << std::string(program) << " " << VALHALLA_VERSION << "\n"; + return false; + } + + // Read the config file + if (result.count("inline-config")) { + conf = valhalla::config(result["inline-config"].as()); + } else if (result.count("config") && + filesystem::is_regular_file(result["config"].as())) { + conf = valhalla::config(result["config"].as()); + } else { + throw cxxopts::exceptions::exception("Configuration is required\n\n" + opts.help() + "\n\n"); + } + + // configure logging + auto logging_subtree = conf.get_child_optional(log); + if (!log.empty() && logging_subtree) { + auto logging_config = + valhalla::midgard::ToMap>(logging_subtree.get()); + valhalla::midgard::logging::Configure(logging_config); + } + + if (use_threads) { + // override concurrency config if specified as arg + auto num_threads = std::max(1U, result.count("concurrency") + ? result["concurrency"].as() + : conf.get("mjolnir.concurrency", + std::thread::hardware_concurrency())); + conf.put("mjolnir.concurrency", num_threads); + + LOG_INFO("Running " + std::string(program) + " with " + std::to_string(num_threads) + + " thread(s)."); + } + + return true; +} diff --git a/src/baldr/CMakeLists.txt b/src/baldr/CMakeLists.txt index d568ffd16c..893011d014 100644 --- a/src/baldr/CMakeLists.txt +++ b/src/baldr/CMakeLists.txt @@ -1,24 +1,31 @@ ## Process the IANA Time Zone Database files set(tz_db_files africa antarctica asia australasia backward etcetera europe - pacificnew northamerica southamerica systemv leapseconds) + northamerica southamerica leapseconds) + +# Process the leap seconds file (it's in .gitignore of the tz repo) +add_custom_command(OUTPUT ${VALHALLA_SOURCE_DIR}/third_party/tz/leapseconds +COMMAND make leapseconds +WORKING_DIRECTORY ${VALHALLA_SOURCE_DIR}/third_party/tz +COMMENT "Compiling third_party/tz/leapseconds with awk" +DEPENDS ${VALHALLA_SOURCE_DIR}/third_party/tz/leapseconds.awk) foreach(tz_db_file ${tz_db_files}) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/date_time_${tz_db_file}.h - COMMAND ${CMAKE_COMMAND} -DMSVC=${MSVC} -P ${VALHALLA_SOURCE_DIR}/cmake/Binary2Header.cmake - ${VALHALLA_SOURCE_DIR}/date_time/${tz_db_file} + COMMAND ${CMAKE_COMMAND} -DMSVC=${MSVC} -P ${VALHALLA_SOURCE_DIR}/cmake/ValhallaBin2Header.cmake + ${VALHALLA_SOURCE_DIR}/third_party/tz/${tz_db_file} ${CMAKE_CURRENT_BINARY_DIR}/date_time_${tz_db_file}.h --variable-name date_time_${tz_db_file} --skip 1 --raw WORKING_DIRECTORY ${VALHALLA_SOURCE_DIR} - COMMENT "Compiling date_time/${tz_db_file} to date_time_${tz_db_file}.h" - DEPENDS ${VALHALLA_SOURCE_DIR}/date_time/${tz_db_file}) + COMMENT "Compiling third_party/tz/${tz_db_file} to date_time_${tz_db_file}.h" + DEPENDS ${VALHALLA_SOURCE_DIR}/third_party/tz/${tz_db_file} ${VALHALLA_SOURCE_DIR}/third_party/tz/leapseconds) endforeach() -## Process the windowsZones.xml file +# Process the windowsZones.xml file add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/date_time_windows_zones.h - COMMAND ${CMAKE_COMMAND} -DMSVC=${MSVC} -P ${VALHALLA_SOURCE_DIR}/cmake/Binary2Header.cmake - ${VALHALLA_SOURCE_DIR}/date_time/windowsZones.xml - ${CMAKE_CURRENT_BINARY_DIR}/date_time_windows_zones.h - --variable-name date_time_windows_zones_xml --skip 1 --raw + COMMAND ${CMAKE_COMMAND} -DMSVC=${MSVC} -P ${VALHALLA_SOURCE_DIR}/cmake/ValhallaBin2Header.cmake + ${VALHALLA_SOURCE_DIR}/date_time/windowsZones.xml + ${CMAKE_CURRENT_BINARY_DIR}/date_time_windows_zones.h + --variable-name date_time_windows_zones_xml --skip 1 --raw WORKING_DIRECTORY ${VALHALLA_SOURCE_DIR} COMMENT "Compiling date_time/windowsZones.xml to date_time_windows_zones.h" DEPENDS ${VALHALLA_SOURCE_DIR}/date_time/windowsZones.xml) @@ -28,16 +35,13 @@ file(GLOB headers ${VALHALLA_SOURCE_DIR}/valhalla/baldr/*.h) set(includes ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include ${CMAKE_CURRENT_BINARY_DIR}/src/baldr ) -# treat date library as system -set(system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include) -if(APPLE) - list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) -endif() +set(system_includes + ${date_include_dir} + $<$:${dirent_include_dir}> + ${rapidjson_include_dir}) set(sources accessrestriction.cc @@ -91,9 +95,7 @@ list(APPEND sources ${CMAKE_CURRENT_BINARY_DIR}/date_time_europe.h ${CMAKE_CURRENT_BINARY_DIR}/date_time_leapseconds.h ${CMAKE_CURRENT_BINARY_DIR}/date_time_northamerica.h - ${CMAKE_CURRENT_BINARY_DIR}/date_time_pacificnew.h ${CMAKE_CURRENT_BINARY_DIR}/date_time_southamerica.h - ${CMAKE_CURRENT_BINARY_DIR}/date_time_systemv.h ${CMAKE_CURRENT_BINARY_DIR}/date_time_windows_zones.h) list(APPEND sources_with_warnings @@ -126,5 +128,5 @@ valhalla_module(NAME baldr valhalla::proto ${valhalla_protobuf_targets} Boost::boost - CURL::CURL - ZLIB::ZLIB) + ${curl_targets} + PkgConfig::ZLIB) diff --git a/src/baldr/accessrestriction.cc b/src/baldr/accessrestriction.cc index 2012016bc8..7de44ec4fc 100644 --- a/src/baldr/accessrestriction.cc +++ b/src/baldr/accessrestriction.cc @@ -1,6 +1,24 @@ #include "baldr/accessrestriction.h" +#include "baldr/timedomain.h" #include +namespace vb = valhalla::baldr; + +namespace { +const std::unordered_map type_to_string = { + {vb::AccessType::kHazmat, "hazmat"}, + {vb::AccessType::kMaxHeight, "max_height"}, + {vb::AccessType::kMaxWidth, "max_width"}, + {vb::AccessType::kMaxLength, "max_length"}, + {vb::AccessType::kMaxWeight, "max_weight"}, + {vb::AccessType::kMaxAxleLoad, "max_axle_load"}, + {vb::AccessType::kTimedAllowed, "timed_allowed"}, + {vb::AccessType::kTimedDenied, "timed_denied"}, + {vb::AccessType::kDestinationAllowed, "destination_allowed"}, + {vb::AccessType::kMaxAxles, "max_axles"}, +}; +} + namespace valhalla { namespace baldr { @@ -43,17 +61,54 @@ void AccessRestriction::set_value(const uint64_t v) { value_ = v; } +const json::MapPtr AccessRestriction::json() const { + auto maybe_found = type_to_string.find(type()); + std::string restriction_type = "unsupported"; + if (maybe_found != type_to_string.cend()) { + restriction_type = maybe_found->second; + } + auto map = json::map({{"type", restriction_type}, + {"edge_index", static_cast(edgeindex())}, + {"bus", static_cast(modes_ & kBusAccess)}, + {"car", static_cast(modes_ & kAutoAccess)}, + {"emergency", static_cast(modes_ & kEmergencyAccess)}, + {"HOV", static_cast(modes_ & kHOVAccess)}, + {"pedestrian", static_cast(modes_ & kPedestrianAccess)}, + {"taxi", static_cast(modes_ & kTaxiAccess)}, + {"truck", static_cast(modes_ & kTruckAccess)}, + {"wheelchair", static_cast(modes_ & kWheelchairAccess)}, + {"moped", static_cast(modes_ & kMopedAccess)}, + {"motorcycle", static_cast(modes_ & kMotorcycleAccess)}}); + + switch (type()) { + case AccessType::kTimedAllowed: + case AccessType::kTimedDenied: + case AccessType::kDestinationAllowed: + // TODO(nils): turn the time domain into a proper map + map->emplace("value", json::map({{std::string("time_domain"), value()}})); + break; + case AccessType::kMaxAxles: + map->emplace("value", value()); + break; + default: + map->emplace("value", json::fixed_t{static_cast(value()) * 0.01, 2}); + } + + return map; +} + // operator < - for sorting. Sort by route Id. bool AccessRestriction::operator<(const AccessRestriction& other) const { if (edgeindex() == other.edgeindex()) { if (modes() == other.modes()) { - return value() < other.value(); - } else { - return modes() < other.modes(); + if (type() == other.type()) { + return value() < other.value(); + } + return type() < other.type(); } - } else { - return edgeindex() < other.edgeindex(); + return modes() < other.modes(); } + return edgeindex() < other.edgeindex(); } } // namespace baldr diff --git a/src/baldr/admin.cc b/src/baldr/admin.cc index 704193e9b8..c301a414d8 100644 --- a/src/baldr/admin.cc +++ b/src/baldr/admin.cc @@ -1,4 +1,5 @@ #include "baldr/admin.h" +#include namespace { diff --git a/src/baldr/attributes_controller.cc b/src/baldr/attributes_controller.cc index 26fc6a555b..2f972dd841 100644 --- a/src/baldr/attributes_controller.cc +++ b/src/baldr/attributes_controller.cc @@ -67,6 +67,7 @@ const std::unordered_map AttributesController::kDefaultAttrib {kEdgeLaneConnectivity, true}, {kEdgeCycleLane, true}, {kEdgeBicycleNetwork, true}, + {kEdgeElevation, false}, {kEdgeSacScale, true}, {kEdgeShoulder, true}, {kEdgeSidewalk, true}, @@ -79,6 +80,8 @@ const std::unordered_map AttributesController::kDefaultAttrib {kEdgeIsUrban, false}, {kEdgeTaggedValues, true}, {kEdgeIndoor, true}, + {kEdgeLandmarks, true}, + {kEdgeCountryCrossing, true}, // Node keys {kIncidents, false}, @@ -170,6 +173,9 @@ AttributesController::AttributesController(const Options& options, bool is_stric default: break; } + + // Set the edge elevation attributes based on elevation interval being set + attributes.at(kEdgeElevation) = options.elevation_interval() > 0.0f; } void AttributesController::disable_all() { diff --git a/src/baldr/connectivity_map.cc b/src/baldr/connectivity_map.cc index 95d8b260fa..8dd90785c7 100644 --- a/src/baldr/connectivity_map.cc +++ b/src/baldr/connectivity_map.cc @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -181,10 +180,9 @@ std::unordered_set connectivity_map_t::get_colors(const baldr::TileLevel // Get a list of tiles required within the radius of the projected point const auto& ll = edge.projected; DistanceApproximator approximator(ll); - float latdeg = (radius / kMetersPerDegreeLat); - float lngdeg = (radius / DistanceApproximator::MetersPerLngDegree(ll.lat())); - AABB2 bbox(Point2(ll.lng() - lngdeg, ll.lat() - latdeg), - Point2(ll.lng() + lngdeg, ll.lat() + latdeg)); + auto latdeg = (radius / kMetersPerDegreeLat); + auto lngdeg = (radius / DistanceApproximator::MetersPerLngDegree(ll.lat())); + AABB2 bbox(ll.lng() - lngdeg, ll.lat() - latdeg, ll.lng() + lngdeg, ll.lat() + latdeg); std::vector tilelist = hierarchy_level.tiles.TileList(bbox); for (const auto& id : tilelist) { auto color = level->second.find(id); diff --git a/src/baldr/curler.cc b/src/baldr/curler.cc index c1358609da..6b4dae4847 100644 --- a/src/baldr/curler.cc +++ b/src/baldr/curler.cc @@ -6,7 +6,7 @@ #include #include -#ifdef CURL_STATICLIB +#ifdef ENABLE_HTTP #if defined(_MSC_VER) && !defined(NOGDI) #define NOGDI // prevents winsock2.h drag in wingdi.h diff --git a/src/baldr/datetime.cc b/src/baldr/datetime.cc index 91598e4ba7..86b436dcf6 100644 --- a/src/baldr/datetime.cc +++ b/src/baldr/datetime.cc @@ -1,11 +1,9 @@ #include -#include -#include -#include #include #include #include +#include #include "baldr/datetime.h" #include "baldr/graphconstants.h" @@ -13,7 +11,670 @@ #include "midgard/logging.h" #include "midgard/util.h" -namespace { +namespace { // NOTE, the below timezone maps are indexed for compatibility reasons. We put the value + // (index) +const valhalla::baldr::DateTime::dt_info_t INVALID_DT = {"", "", ""}; +// into the tiles, so it needs to be stable. We have 2 timezone maps here: +// - tz_name_to_id: +// - before 2023c: currently and past official timezone names +// - after 2023c: we point to the old timezone using a bit shift which is resolved in NodeInfo +// to keep forward-compatibility +// - tz_new_to_old_id: +// - before 2023c: deprecated timezone names to their current names +// - after 2023c: new name of renamed/merged timezone(s) to their previous/deprecated name to keep +// forward-compatibility +// When updating the timezone release and new timezones were added, add them to the end and keep +// incrementing the index. NEVER remove any entry in either map. +const std::unordered_map tz_name_to_id = { + {"Africa/Abidjan", 1}, // start timezones release 2018d; + {"Africa/Accra", 2}, + {"Africa/Algiers", 3}, + {"Africa/Bissau", 4}, + {"Africa/Cairo", 5}, + {"Africa/Casablanca", 6}, + {"Africa/Ceuta", 7}, + {"Africa/El_Aaiun", 8}, + {"Africa/Johannesburg", 9}, + {"Africa/Juba", 10}, + {"Africa/Khartoum", 11}, + {"Africa/Lagos", 12}, + {"Africa/Maputo", 13}, + {"Africa/Monrovia", 14}, + {"Africa/Nairobi", 15}, + {"Africa/Ndjamena", 16}, + {"Africa/Sao_Tome", 17}, + {"Africa/Tripoli", 18}, + {"Africa/Tunis", 19}, + {"Africa/Windhoek", 20}, + {"America/Adak", 21}, + {"America/Anchorage", 22}, + {"America/Araguaina", 23}, + {"America/Argentina/Buenos_Aires", 24}, + {"America/Argentina/Catamarca", 25}, + {"America/Argentina/Cordoba", 26}, + {"America/Argentina/Jujuy", 27}, + {"America/Argentina/La_Rioja", 28}, + {"America/Argentina/Mendoza", 29}, + {"America/Argentina/Rio_Gallegos", 30}, + {"America/Argentina/Salta", 31}, + {"America/Argentina/San_Juan", 32}, + {"America/Argentina/San_Luis", 33}, + {"America/Argentina/Tucuman", 34}, + {"America/Argentina/Ushuaia", 35}, + {"America/Asuncion", 36}, + {"America/Atikokan", 37}, + {"America/Bahia", 38}, + {"America/Bahia_Banderas", 39}, + {"America/Barbados", 40}, + {"America/Belem", 41}, + {"America/Belize", 42}, + {"America/Blanc-Sablon", 43}, + {"America/Boa_Vista", 44}, + {"America/Bogota", 45}, + {"America/Boise", 46}, + {"America/Cambridge_Bay", 47}, + {"America/Campo_Grande", 48}, + {"America/Cancun", 49}, + {"America/Caracas", 50}, + {"America/Cayenne", 51}, + {"America/Chicago", 52}, + {"America/Chihuahua", 53}, + {"America/Costa_Rica", 54}, + {"America/Creston", 55}, + {"America/Cuiaba", 56}, + {"America/Curacao", 57}, + {"America/Danmarkshavn", 58}, + {"America/Dawson", 59}, + {"America/Dawson_Creek", 60}, + {"America/Denver", 61}, + {"America/Detroit", 62}, + {"America/Edmonton", 63}, + {"America/Eirunepe", 64}, + {"America/El_Salvador", 65}, + {"America/Fort_Nelson", 66}, + {"America/Fortaleza", 67}, + {"America/Glace_Bay", 68}, + {"America/Godthab", 69}, + {"America/Goose_Bay", 70}, + {"America/Grand_Turk", 71}, + {"America/Guatemala", 72}, + {"America/Guayaquil", 73}, + {"America/Guyana", 74}, + {"America/Halifax", 75}, + {"America/Havana", 76}, + {"America/Hermosillo", 77}, + {"America/Indiana/Indianapolis", 78}, + {"America/Indiana/Knox", 79}, + {"America/Indiana/Marengo", 80}, + {"America/Indiana/Petersburg", 81}, + {"America/Indiana/Tell_City", 82}, + {"America/Indiana/Vevay", 83}, + {"America/Indiana/Vincennes", 84}, + {"America/Indiana/Winamac", 85}, + {"America/Inuvik", 86}, + {"America/Iqaluit", 87}, + {"America/Jamaica", 88}, + {"America/Juneau", 89}, + {"America/Kentucky/Louisville", 90}, + {"America/Kentucky/Monticello", 91}, + {"America/La_Paz", 92}, + {"America/Lima", 93}, + {"America/Los_Angeles", 94}, + {"America/Maceio", 95}, + {"America/Managua", 96}, + {"America/Manaus", 97}, + {"America/Martinique", 98}, + {"America/Matamoros", 99}, + {"America/Mazatlan", 100}, + {"America/Menominee", 101}, + {"America/Merida", 102}, + {"America/Metlakatla", 103}, + {"America/Mexico_City", 104}, + {"America/Miquelon", 105}, + {"America/Moncton", 106}, + {"America/Monterrey", 107}, + {"America/Montevideo", 108}, + {"America/Nassau", 109}, + {"America/New_York", 110}, + {"America/Nipigon", 111}, + {"America/Nome", 112}, + {"America/Noronha", 113}, + {"America/North_Dakota/Beulah", 114}, + {"America/North_Dakota/Center", 115}, + {"America/North_Dakota/New_Salem", 116}, + {"America/Ojinaga", 117}, + {"America/Panama", 118}, + {"America/Pangnirtung", 119}, + {"America/Paramaribo", 120}, + {"America/Phoenix", 121}, + {"America/Port-au-Prince", 122}, + {"America/Port_of_Spain", 123}, + {"America/Porto_Velho", 124}, + {"America/Puerto_Rico", 125}, + {"America/Punta_Arenas", 126}, + {"America/Rainy_River", 127}, + {"America/Rankin_Inlet", 128}, + {"America/Recife", 129}, + {"America/Regina", 130}, + {"America/Resolute", 131}, + {"America/Rio_Branco", 132}, + {"America/Santarem", 133}, + {"America/Santiago", 134}, + {"America/Santo_Domingo", 135}, + {"America/Sao_Paulo", 136}, + {"America/Scoresbysund", 137}, + {"America/Sitka", 138}, + {"America/St_Johns", 139}, + {"America/Swift_Current", 140}, + {"America/Tegucigalpa", 141}, + {"America/Thule", 142}, + {"America/Thunder_Bay", 143}, + {"America/Tijuana", 144}, + {"America/Toronto", 145}, + {"America/Vancouver", 146}, + {"America/Whitehorse", 147}, + {"America/Winnipeg", 148}, + {"America/Yakutat", 149}, + {"America/Yellowknife", 150}, + {"Antarctica/Casey", 151}, + {"Antarctica/Davis", 152}, + {"Antarctica/DumontDUrville", 153}, + {"Antarctica/Macquarie", 154}, + {"Antarctica/Mawson", 155}, + {"Antarctica/Palmer", 156}, + {"Antarctica/Rothera", 157}, + {"Antarctica/Syowa", 158}, + {"Antarctica/Troll", 159}, + {"Antarctica/Vostok", 160}, + {"Asia/Almaty", 161}, + {"Asia/Amman", 162}, + {"Asia/Anadyr", 163}, + {"Asia/Aqtau", 164}, + {"Asia/Aqtobe", 165}, + {"Asia/Ashgabat", 166}, + {"Asia/Atyrau", 167}, + {"Asia/Baghdad", 168}, + {"Asia/Baku", 169}, + {"Asia/Bangkok", 170}, + {"Asia/Barnaul", 171}, + {"Asia/Beirut", 172}, + {"Asia/Bishkek", 173}, + {"Asia/Brunei", 174}, + {"Asia/Chita", 175}, + {"Asia/Choibalsan", 176}, + {"Asia/Colombo", 177}, + {"Asia/Damascus", 178}, + {"Asia/Dhaka", 179}, + {"Asia/Dili", 180}, + {"Asia/Dubai", 181}, + {"Asia/Dushanbe", 182}, + {"Asia/Famagusta", 183}, + {"Asia/Gaza", 184}, + {"Asia/Hebron", 185}, + {"Asia/Ho_Chi_Minh", 186}, + {"Asia/Hong_Kong", 187}, + {"Asia/Hovd", 188}, + {"Asia/Irkutsk", 189}, + {"Asia/Jakarta", 190}, + {"Asia/Jayapura", 191}, + {"Asia/Jerusalem", 192}, + {"Asia/Kabul", 193}, + {"Asia/Kamchatka", 194}, + {"Asia/Karachi", 195}, + {"Asia/Kathmandu", 196}, + {"Asia/Khandyga", 197}, + {"Asia/Kolkata", 198}, + {"Asia/Krasnoyarsk", 199}, + {"Asia/Kuala_Lumpur", 200}, + {"Asia/Kuching", 201}, + {"Asia/Macau", 202}, + {"Asia/Magadan", 203}, + {"Asia/Makassar", 204}, + {"Asia/Manila", 205}, + {"Asia/Nicosia", 206}, + {"Asia/Novokuznetsk", 207}, + {"Asia/Novosibirsk", 208}, + {"Asia/Omsk", 209}, + {"Asia/Oral", 210}, + {"Asia/Pontianak", 211}, + {"Asia/Pyongyang", 212}, + {"Asia/Qatar", 213}, + {"Asia/Qyzylorda", 214}, + {"Asia/Riyadh", 215}, + {"Asia/Sakhalin", 216}, + {"Asia/Samarkand", 217}, + {"Asia/Seoul", 218}, + {"Asia/Shanghai", 219}, + {"Asia/Singapore", 220}, + {"Asia/Srednekolymsk", 221}, + {"Asia/Taipei", 222}, + {"Asia/Tashkent", 223}, + {"Asia/Tbilisi", 224}, + {"Asia/Tehran", 225}, + {"Asia/Thimphu", 226}, + {"Asia/Tokyo", 227}, + {"Asia/Tomsk", 228}, + {"Asia/Ulaanbaatar", 229}, + {"Asia/Urumqi", 230}, + {"Asia/Ust-Nera", 231}, + {"Asia/Vladivostok", 232}, + {"Asia/Yakutsk", 233}, + {"Asia/Yangon", 234}, + {"Asia/Yekaterinburg", 235}, + {"Asia/Yerevan", 236}, + {"Atlantic/Azores", 237}, + {"Atlantic/Bermuda", 238}, + {"Atlantic/Canary", 239}, + {"Atlantic/Cape_Verde", 240}, + {"Atlantic/Faroe", 241}, + {"Atlantic/Madeira", 242}, + {"Atlantic/Reykjavik", 243}, + {"Atlantic/South_Georgia", 244}, + {"Atlantic/Stanley", 245}, + {"Australia/Adelaide", 246}, + {"Australia/Brisbane", 247}, + {"Australia/Broken_Hill", 248}, + {"Australia/Currie", 249}, + {"Australia/Darwin", 250}, + {"Australia/Eucla", 251}, + {"Australia/Hobart", 252}, + {"Australia/Lindeman", 253}, + {"Australia/Lord_Howe", 254}, + {"Australia/Melbourne", 255}, + {"Australia/Perth", 256}, + {"Australia/Sydney", 257}, + {"CET", 258}, + {"CST6CDT", 259}, + {"EET", 260}, + {"EST", 261}, + {"EST5EDT", 262}, + {"Etc/GMT", 263}, + {"Etc/GMT+1", 264}, + {"Etc/GMT+10", 265}, + {"Etc/GMT+11", 266}, + {"Etc/GMT+12", 267}, + {"Etc/GMT+2", 268}, + {"Etc/GMT+3", 269}, + {"Etc/GMT+4", 270}, + {"Etc/GMT+5", 271}, + {"Etc/GMT+6", 272}, + {"Etc/GMT+7", 273}, + {"Etc/GMT+8", 274}, + {"Etc/GMT+9", 275}, + {"Etc/GMT-1", 276}, + {"Etc/GMT-10", 277}, + {"Etc/GMT-11", 278}, + {"Etc/GMT-12", 279}, + {"Etc/GMT-13", 280}, + {"Etc/GMT-14", 281}, + {"Etc/GMT-2", 282}, + {"Etc/GMT-3", 283}, + {"Etc/GMT-4", 284}, + {"Etc/GMT-5", 285}, + {"Etc/GMT-6", 286}, + {"Etc/GMT-7", 287}, + {"Etc/GMT-8", 288}, + {"Etc/GMT-9", 289}, + {"Etc/UCT", 290}, + {"Etc/UTC", 291}, + {"Europe/Amsterdam", 292}, + {"Europe/Andorra", 293}, + {"Europe/Astrakhan", 294}, + {"Europe/Athens", 295}, + {"Europe/Belgrade", 296}, + {"Europe/Berlin", 297}, + {"Europe/Brussels", 298}, + {"Europe/Bucharest", 299}, + {"Europe/Budapest", 300}, + {"Europe/Chisinau", 301}, + {"Europe/Copenhagen", 302}, + {"Europe/Dublin", 303}, + {"Europe/Gibraltar", 304}, + {"Europe/Helsinki", 305}, + {"Europe/Istanbul", 306}, + {"Europe/Kaliningrad", 307}, + {"Europe/Kiev", 308}, + {"Europe/Kirov", 309}, + {"Europe/Lisbon", 310}, + {"Europe/London", 311}, + {"Europe/Luxembourg", 312}, + {"Europe/Madrid", 313}, + {"Europe/Malta", 314}, + {"Europe/Minsk", 315}, + {"Europe/Monaco", 316}, + {"Europe/Moscow", 317}, + {"Europe/Oslo", 318}, + {"Europe/Paris", 319}, + {"Europe/Prague", 320}, + {"Europe/Riga", 321}, + {"Europe/Rome", 322}, + {"Europe/Samara", 323}, + {"Europe/Saratov", 324}, + {"Europe/Simferopol", 325}, + {"Europe/Sofia", 326}, + {"Europe/Stockholm", 327}, + {"Europe/Tallinn", 328}, + {"Europe/Tirane", 329}, + {"Europe/Ulyanovsk", 330}, + {"Europe/Uzhgorod", 331}, + {"Europe/Vienna", 332}, + {"Europe/Vilnius", 333}, + {"Europe/Volgograd", 334}, + {"Europe/Warsaw", 335}, + {"Europe/Zaporozhye", 336}, + {"Europe/Zurich", 337}, + {"HST", 338}, + {"Indian/Chagos", 339}, + {"Indian/Christmas", 340}, + {"Indian/Cocos", 341}, + {"Indian/Kerguelen", 342}, + {"Indian/Mahe", 343}, + {"Indian/Maldives", 344}, + {"Indian/Mauritius", 345}, + {"Indian/Reunion", 346}, + {"MET", 347}, + {"MST", 348}, + {"MST7MDT", 349}, + {"PST8PDT", 350}, + {"Pacific/Apia", 351}, + {"Pacific/Auckland", 352}, + {"Pacific/Bougainville", 353}, + {"Pacific/Chatham", 354}, + {"Pacific/Chuuk", 355}, + {"Pacific/Easter", 356}, + {"Pacific/Efate", 357}, + {"Pacific/Enderbury", 358}, + {"Pacific/Fakaofo", 359}, + {"Pacific/Fiji", 360}, + {"Pacific/Funafuti", 361}, + {"Pacific/Galapagos", 362}, + {"Pacific/Gambier", 363}, + {"Pacific/Guadalcanal", 364}, + {"Pacific/Guam", 365}, + {"Pacific/Honolulu", 366}, + {"Pacific/Kiritimati", 367}, + {"Pacific/Kosrae", 368}, + {"Pacific/Kwajalein", 369}, + {"Pacific/Majuro", 370}, + {"Pacific/Marquesas", 371}, + {"Pacific/Nauru", 372}, + {"Pacific/Niue", 373}, + {"Pacific/Norfolk", 374}, + {"Pacific/Noumea", 375}, + {"Pacific/Pago_Pago", 376}, + {"Pacific/Palau", 377}, + {"Pacific/Pitcairn", 378}, + {"Pacific/Pohnpei", 379}, + {"Pacific/Port_Moresby", 380}, + {"Pacific/Rarotonga", 381}, + {"Pacific/Tahiti", 382}, + {"Pacific/Tarawa", 383}, + {"Pacific/Tongatapu", 384}, + {"Pacific/Wake", 385}, + {"Pacific/Wallis", 386}, + {"WET", 387}, // end timezones release 2018d; + {"America/Ciudad_Juarez", 117 | (1 << 9)}, // start timezones release 2023c; was America/Ojinaga + {"Asia/Qostanay", 214 | (1 << 9)}, // end timezones release 2023c; was Asia/Qyzylorda +}; + +// this holds: +// - before 2023c: deprecated timezone names to their current names +// - after 2023c: new name of renamed/merged timezone(s) to their previous/deprecated name to keep +// forward-compatibility +const std::unordered_map tz_new_to_old_id = { + {"Africa/Addis_Ababa", 15}, // start deprecated timezones 2018d + {"Africa/Asmara", 15}, + {"Africa/Asmera", 15}, + {"Africa/Bamako", 1}, + {"Africa/Bangui", 12}, + {"Africa/Banjul", 1}, + {"Africa/Blantyre", 13}, + {"Africa/Brazzaville", 12}, + {"Africa/Bujumbura", 13}, + {"Africa/Conakry", 1}, + {"Africa/Dakar", 1}, + {"Africa/Dar_es_Salaam", 15}, + {"Africa/Djibouti", 15}, + {"Africa/Douala", 12}, + {"Africa/Freetown", 1}, + {"Africa/Gaborone", 13}, + {"Africa/Harare", 13}, + {"Africa/Kampala", 15}, + {"Africa/Kigali", 13}, + {"Africa/Kinshasa", 12}, + {"Africa/Libreville", 12}, + {"Africa/Lome", 1}, + {"Africa/Luanda", 12}, + {"Africa/Lubumbashi", 13}, + {"Africa/Lusaka", 13}, + {"Africa/Malabo", 12}, + {"Africa/Maseru", 9}, + {"Africa/Mbabane", 9}, + {"Africa/Mogadishu", 15}, + {"Africa/Niamey", 12}, + {"Africa/Nouakchott", 1}, + {"Africa/Ouagadougou", 1}, + {"Africa/Porto-Novo", 12}, + {"Africa/Timbuktu", 1}, + {"America/Anguilla", 123}, + {"America/Antigua", 123}, + {"America/Argentina/ComodRivadavia", 25}, + {"America/Aruba", 57}, + {"America/Atka", 21}, + {"America/Buenos_Aires", 24}, + {"America/Catamarca", 25}, + {"America/Cayman", 118}, + {"America/Coral_Harbour", 37}, + {"America/Cordoba", 26}, + {"America/Dominica", 123}, + {"America/Ensenada", 144}, + {"America/Fort_Wayne", 78}, + {"America/Grenada", 123}, + {"America/Guadeloupe", 123}, + {"America/Indianapolis", 78}, + {"America/Jujuy", 27}, + {"America/Knox_IN", 79}, + {"America/Kralendijk", 57}, + {"America/Louisville", 90}, + {"America/Lower_Princes", 57}, + {"America/Marigot", 123}, + {"America/Mendoza", 29}, + {"America/Montreal", 145}, + {"America/Montserrat", 123}, + {"America/Porto_Acre", 132}, + {"America/Rosario", 26}, + {"America/Santa_Isabel", 144}, + {"America/Shiprock", 61}, + {"America/St_Barthelemy", 123}, + {"America/St_Kitts", 123}, + {"America/St_Lucia", 123}, + {"America/St_Thomas", 123}, + {"America/St_Vincent", 123}, + {"America/Tortola", 123}, + {"America/Virgin", 123}, + {"Antarctica/McMurdo", 352}, + {"Antarctica/South_Pole", 352}, + {"Arctic/Longyearbyen", 318}, + {"Asia/Aden", 215}, + {"Asia/Ashkhabad", 166}, + {"Asia/Bahrain", 213}, + {"Asia/Calcutta", 198}, + {"Asia/Chongqing", 219}, + {"Asia/Chungking", 219}, + {"Asia/Dacca", 179}, + {"Asia/Harbin", 219}, + {"Asia/Istanbul", 306}, + {"Asia/Kashgar", 230}, + {"Asia/Katmandu", 196}, + {"Asia/Kuwait", 215}, + {"Asia/Macao", 202}, + {"Asia/Muscat", 181}, + {"Asia/Phnom_Penh", 170}, + {"Asia/Rangoon", 234}, + {"Asia/Saigon", 186}, + {"Asia/Tel_Aviv", 192}, + {"Asia/Thimbu", 226}, + {"Asia/Ujung_Pandang", 204}, + {"Asia/Ulan_Bator", 229}, + {"Asia/Vientiane", 170}, + {"Atlantic/Faeroe", 241}, + {"Atlantic/Jan_Mayen", 318}, + {"Atlantic/St_Helena", 1}, + {"Australia/ACT", 257}, + {"Australia/Canberra", 257}, + {"Australia/LHI", 254}, + {"Australia/NSW", 257}, + {"Australia/North", 250}, + {"Australia/Queensland", 247}, + {"Australia/South", 246}, + {"Australia/Tasmania", 252}, + {"Australia/Victoria", 255}, + {"Australia/West", 256}, + {"Australia/Yancowinna", 248}, + {"Brazil/Acre", 132}, + {"Brazil/DeNoronha", 113}, + {"Brazil/East", 136}, + {"Brazil/West", 97}, + {"Canada/Atlantic", 75}, + {"Canada/Central", 148}, + {"Canada/Eastern", 145}, + {"Canada/Mountain", 63}, + {"Canada/Newfoundland", 139}, + {"Canada/Pacific", 146}, + {"Canada/Saskatchewan", 130}, + {"Canada/Yukon", 147}, + {"Chile/Continental", 134}, + {"Chile/EasterIsland", 356}, + {"Cuba", 76}, + {"Egypt", 5}, + {"Eire", 303}, + {"Etc/GMT+0", 263}, + {"Etc/GMT-0", 263}, + {"Etc/GMT0", 263}, + {"Etc/Greenwich", 263}, + {"Etc/Universal", 291}, + {"Etc/Zulu", 291}, + {"Europe/Belfast", 311}, + {"Europe/Bratislava", 320}, + {"Europe/Busingen", 337}, + {"Europe/Guernsey", 311}, + {"Europe/Isle_of_Man", 311}, + {"Europe/Jersey", 311}, + {"Europe/Ljubljana", 296}, + {"Europe/Mariehamn", 305}, + {"Europe/Nicosia", 206}, + {"Europe/Podgorica", 296}, + {"Europe/San_Marino", 322}, + {"Europe/Sarajevo", 296}, + {"Europe/Skopje", 296}, + {"Europe/Tiraspol", 301}, + {"Europe/Vaduz", 337}, + {"Europe/Vatican", 322}, + {"Europe/Zagreb", 296}, + {"GB", 311}, + {"GB-Eire", 311}, + {"GMT", 263}, + {"GMT+0", 263}, + {"GMT-0", 263}, + {"GMT0", 263}, + {"Greenwich", 263}, + {"Hongkong", 187}, + {"Iceland", 243}, + {"Indian/Antananarivo", 15}, + {"Indian/Comoro", 15}, + {"Indian/Mayotte", 15}, + {"Iran", 225}, + {"Israel", 192}, + {"Jamaica", 88}, + {"Japan", 227}, + {"Kwajalein", 369}, + {"Libya", 18}, + {"Mexico/BajaNorte", 144}, + {"Mexico/BajaSur", 100}, + {"Mexico/General", 104}, + {"NZ", 352}, + {"NZ-CHAT", 354}, + {"Navajo", 61}, + {"PRC", 219}, + {"Pacific/Johnston", 366}, + {"Pacific/Midway", 376}, + {"Pacific/Ponape", 379}, + {"Pacific/Saipan", 365}, + {"Pacific/Samoa", 376}, + {"Pacific/Truk", 355}, + {"Pacific/Yap", 355}, + {"Poland", 335}, + {"Portugal", 310}, + {"ROC", 222}, + {"ROK", 218}, + {"Singapore", 220}, + {"Turkey", 306}, + {"UCT", 290}, + {"US/Alaska", 22}, + {"US/Aleutian", 21}, + {"US/Arizona", 121}, + {"US/Central", 52}, + {"US/East-Indiana", 78}, + {"US/Eastern", 110}, + {"US/Hawaii", 366}, + {"US/Indiana-Starke", 79}, + {"US/Michigan", 62}, + {"US/Mountain", 61}, + {"US/Pacific", 94}, + {"US/Pacific-New", 94}, + {"US/Samoa", 376}, + {"UTC", 291}, + {"Universal", 291}, + {"W-SU", 317}, + {"Zulu", 291}, // end deprecated timezones 2018d + {"America/Nuuk", 69}, // start renamed timezones 2023c + {"Europe/Kyiv", 308}, + {"Pacific/Kanton", 358}, // end renamed timezones 2023c +}; + +// checks the integrity of the static tz maps, which will fail in case of +// tzdb updates. this function pretty-prints missing tzs for convenience +const std::string check_tz_map(const date::tzdb& db) { + std::vector new_zones_msg, new_links_msg; + std::unordered_set new_zones_names; + + // first find the link's targets, then the new timezones, to not get dups + for (const auto& link : db.links) { + // if we already know both link & target tz, we can skip; most likely + // means that existing timezones were merged which is fine for us + if (tz_name_to_id.find(link.target()) != tz_name_to_id.end() || + tz_new_to_old_id.find(link.target()) != tz_new_to_old_id.end() || + !new_zones_names.insert(link.target()).second) { + continue; + } + + // correlate the new timezone with the old tz ID for compat + auto old_tz = tz_name_to_id.find(link.name()); + new_links_msg.emplace_back("{\"" + link.target() + "\", " + std::to_string(old_tz->second) + + "},"); + } + + for (const auto& tz : db.zones) { + // only add entirely new zones if we didn't see them as link target yet + if (tz_name_to_id.find(tz.name()) == tz_name_to_id.end() && + tz_new_to_old_id.find(tz.name()) == tz_new_to_old_id.end() && + std::find(new_zones_names.begin(), new_zones_names.end(), tz.name()) == + new_zones_names.end()) { + new_zones_msg.emplace_back(tz.name()); + } + } + + std::string result; + if (new_links_msg.size()) { + result += "\nNew links: \n" + boost::algorithm::join(new_links_msg, "\n"); + } + if (new_zones_msg.size()) { + result += + "\nNew timezones to be manually resolved: \n" + boost::algorithm::join(new_zones_msg, "\n"); + } + + return result; +} // use a cache to store already constructed sys_info's since they aren't cheap template const date::sys_info& @@ -48,28 +709,33 @@ namespace valhalla { namespace baldr { namespace DateTime { -tz_db_t::tz_db_t() : db(date::get_tzdb()) { - // NOTE: outside of this class 0 is reserved for invalid timezone - // so we offset each index by 1 to get into the valid range 1-300 or so - size_t idx{0}; - for (const auto& zone : db.zones) { - names.emplace(zone.name(), ++idx); +tz_db_t::tz_db_t() { + const auto& db = date::get_tzdb(); + + // update timezones & run the tests will fail here if new timezones were added + if (const std::string& msg = check_tz_map(db); msg.size()) { + throw std::runtime_error("Update timezone map at " + std::string(__FILE__) + ": " + msg); + } + + zones.reserve(tz_name_to_id.size()); + for (const auto& tz_pair : tz_name_to_id) { + // we find either the official timezone or we get the target timezone of a link + auto* tz = db.locate_zone(tz_pair.first); + zones[tz_pair.second] = &*tz; } } size_t tz_db_t::to_index(const std::string& zone) const { - auto it = names.find(zone); - if (it == names.cend()) { - return 0; + auto it = tz_name_to_id.find(zone); + if (it != tz_name_to_id.cend() || (it = tz_new_to_old_id.find(zone)) != tz_new_to_old_id.end()) { + return it->second; } - return it->second; + return 0; } const date::time_zone* tz_db_t::from_index(size_t index) const { - if (index < 1 || index > db.zones.size()) { - return nullptr; - } - return &db.zones[index - 1]; + auto it = zones.find(index); + return it != zones.end() ? it->second : nullptr; } const tz_db_t& get_tz_db() { @@ -524,6 +1190,25 @@ uint32_t second_of_week(uint32_t epoch_time, const date::time_zone* time_zone) { return day * midgard::kSecondsPerDay + since_midnight.count(); } +dt_info_t +offset_date(const std::string& in_dt, const uint32_t in_tz, const uint32_t out_tz, float offset) { + if (in_dt.empty()) { + return INVALID_DT; + } // get the input UTC time, add the offset and translate to the out timezone + + auto iepoch = DateTime::seconds_since_epoch(in_dt, DateTime::get_tz_db().from_index(in_tz)); + + auto oepoch = + static_cast(static_cast(iepoch) + static_cast(offset + .5f)); + auto tz = DateTime::get_tz_db().from_index(out_tz); + auto dt = DateTime::seconds_to_date(oepoch, tz, true); + + // dt can be empty if time zones are invalid + if (dt.empty()) { + return INVALID_DT; + }; + return {dt.substr(0, 16), dt.substr(16), tz->name()}; +} } // namespace DateTime } // namespace baldr } // namespace valhalla diff --git a/src/baldr/directededge.cc b/src/baldr/directededge.cc index f60e07c3aa..6c7f28f2ff 100644 --- a/src/baldr/directededge.cc +++ b/src/baldr/directededge.cc @@ -165,12 +165,12 @@ void DirectedEdge::set_dismount(const bool dismount) { dismount_ = dismount; } -// Set if a sidepath should be preffered when cycling over this one +// Set if a sidepath should be preferred when cycling over this one void DirectedEdge::set_use_sidepath(const bool use_sidepath) { use_sidepath_ = use_sidepath; } -// Set the flag indicating the edge is a dead end (no other driveable +// Set the flag indicating the edge is a dead end (no other drivable // roads at the end node of this edge). void DirectedEdge::set_deadend(const bool d) { deadend_ = d; @@ -193,6 +193,13 @@ void DirectedEdge::set_dest_only(const bool destonly) { dest_only_ = destonly; } +// Sets the destination only (private) flag for HGV. This indicates the edge should +// allow access only to locations that are destinations and not allow +// "through" traffic for HGV +void DirectedEdge::set_dest_only_hgv(const bool destonly_hgv) { + dest_only_hgv_ = destonly_hgv; +} + // Sets the flag indicating this edge has is a tunnel of part of a tunnel. void DirectedEdge::set_tunnel(const bool tunnel) { tunnel_ = tunnel; @@ -619,6 +626,7 @@ json::MapPtr DirectedEdge::json() const { {"sidewalk_left", static_cast(sidewalk_left_)}, {"sidewalk_right", static_cast(sidewalk_right_)}, {"sac_scale", to_string(static_cast(sac_scale_))}, + {"deadend", static_cast(deadend_)}, {"geo_attributes", json::map({ {"length", static_cast(length_)}, diff --git a/src/baldr/edgeinfo.cc b/src/baldr/edgeinfo.cc index a8ec5e2f8d..6c75dd686f 100644 --- a/src/baldr/edgeinfo.cc +++ b/src/baldr/edgeinfo.cc @@ -1,17 +1,17 @@ #include "baldr/edgeinfo.h" #include "baldr/graphconstants.h" -#include "midgard/encoded.h" +#include "midgard/elevation_encoding.h" using namespace valhalla::baldr; namespace { -// should return true for any tags which we should consider "named" -// do not return TaggedValue::kPronunciation -bool IsNameTag(char ch) { - static const std::unordered_set kNameTags = {TaggedValue::kBridge, - TaggedValue::kTunnel}; +// should return true if not equal to TaggedValue::kLinguistic +bool IsNonLiguisticTagValue(char ch) { + static const std::unordered_set kNameTags = + {TaggedValue::kBridge, TaggedValue::kTunnel, TaggedValue::kBssInfo, TaggedValue::kLayer, + TaggedValue::kLevel, TaggedValue::kLevelRef, TaggedValue::kLandmark}; return kNameTags.count(static_cast(static_cast(ch))) > 0; } @@ -32,6 +32,27 @@ json::ArrayPtr names_json(const std::vector& names) { return a; } +// per tag parser. each returned string includes the leading TaggedValue. +std::vector parse_tagged_value(const char* ptr) { + switch (static_cast(ptr[0])) { + case TaggedValue::kLayer: + case TaggedValue::kBssInfo: + case TaggedValue::kLevel: + case TaggedValue::kLevelRef: + case TaggedValue::kTunnel: + case TaggedValue::kBridge: + return {std::string(ptr)}; + case TaggedValue::kLandmark: { + std::string landmark_name = ptr + 10; + size_t landmark_size = landmark_name.size() + 10; + return {std::string(ptr, landmark_size)}; + } + case TaggedValue::kLinguistic: + default: + return {}; + } +} + } // namespace namespace valhalla { @@ -61,6 +82,9 @@ EdgeInfo::EdgeInfo(char* ptr, const char* names_list, const size_t names_list_le extended_wayid3_ = static_cast(*ptr); ptr += sizeof(uint8_t); } + + // Set encoded elevation pointer + encoded_elevation_ = reinterpret_cast(ptr); } EdgeInfo::~EdgeInfo() { @@ -109,9 +133,9 @@ std::vector> EdgeInfo::GetNames(bool include_tagged } if (ni->tagged_) { if (ni->name_offset_ < names_list_length_) { - std::string name = names_list_ + ni->name_offset_; - if (IsNameTag(name[0])) { - name_type_pairs.push_back({name.substr(1), false}); + const char* name = names_list_ + ni->name_offset_; + if (IsNonLiguisticTagValue(name[0])) { + name_type_pairs.push_back({std::string(name + 1), false}); } } else throw std::runtime_error("GetNames: offset exceeds size of text list"); @@ -124,10 +148,11 @@ std::vector> EdgeInfo::GetNames(bool include_tagged return name_type_pairs; } -// Get a list of tagged names -std::vector EdgeInfo::GetTaggedValues(bool only_pronunciations) const { +// Get the linguistic, tagged names for an edge +std::vector EdgeInfo::GetLinguisticTaggedValues() const { // Get each name std::vector names; + names.reserve(name_count()); const NameInfo* ni = name_info_list_; for (uint32_t i = 0; i < name_count(); i++, ni++) { @@ -138,22 +163,15 @@ std::vector EdgeInfo::GetTaggedValues(bool only_pronunciations) con const auto* name = names_list_ + ni->name_offset_; try { TaggedValue tv = static_cast(name[0]); - if (tv == baldr::TaggedValue::kPronunciation) { - if (!only_pronunciations) - continue; - - size_t pos = 1; - while (pos < strlen(name)) { - const auto header = midgard::unaligned_read(name + pos); - pos += 3; - names.emplace_back((std::string(reinterpret_cast(&header), 3) + - std::string((name + pos), header.length_))); - - pos += header.length_; + if (tv == baldr::TaggedValue::kLinguistic) { + name += 1; + while (*name != '\0') { + const auto header = midgard::unaligned_read(name); + names.emplace_back( + std::string(reinterpret_cast(&header), kLinguisticHeaderSize) + + std::string((name + kLinguisticHeaderSize), header.length_)); + name += header.length_ + kLinguisticHeaderSize; } - - } else if (!only_pronunciations) { - names.push_back(name); } } catch (const std::invalid_argument& arg) { LOG_DEBUG("invalid_argument thrown for name: " + std::string(name)); @@ -168,9 +186,9 @@ std::vector EdgeInfo::GetTaggedValues(bool only_pronunciations) con // Get a list of names std::vector> EdgeInfo::GetNamesAndTypes(bool include_tagged_values) const { - // Get each name std::vector> name_type_pairs; + name_type_pairs.reserve(name_count()); const NameInfo* ni = name_info_list_; for (uint32_t i = 0; i < name_count(); i++, ni++) { @@ -178,11 +196,13 @@ EdgeInfo::GetNamesAndTypes(bool include_tagged_values) const { if (ni->tagged_ && !include_tagged_values) { continue; } + if (ni->tagged_) { if (ni->name_offset_ < names_list_length_) { - std::string name = names_list_ + ni->name_offset_; - if (IsNameTag(name[0])) { - name_type_pairs.push_back({name.substr(1), false, static_cast(name.at(0))}); + const char* name = names_list_ + ni->name_offset_; + auto tag = name[0]; + if (IsNonLiguisticTagValue(tag)) { + name_type_pairs.push_back({std::string(name + 1), false, static_cast(tag)}); } } else throw std::runtime_error("GetNamesAndTypes: offset exceeds size of text list"); @@ -195,6 +215,38 @@ EdgeInfo::GetNamesAndTypes(bool include_tagged_values) const { return name_type_pairs; } +// Get a list of tagged values. We do not return linguistic tagged values here. Use +// GetLinguisticTaggedValues to obtain those +std::vector EdgeInfo::GetTaggedValues() const { + // Get each name + std::vector tagged_values; + tagged_values.reserve(name_count()); + const NameInfo* ni = name_info_list_; + for (uint32_t i = 0; i < name_count(); i++, ni++) { + if (!ni->tagged_) + continue; + + if (ni->name_offset_ < names_list_length_) { + const char* value = names_list_ + ni->name_offset_; + try { + TaggedValue tv = static_cast(value[0]); + if (tv == baldr::TaggedValue::kLinguistic) { + continue; + } + + // add a per tag parser that returns 0 or more strings, parser skips tags it doesnt know + std::vector contents = parse_tagged_value(value); + std::move(contents.begin(), contents.end(), std::back_inserter(tagged_values)); + } catch (const std::invalid_argument& arg) { + LOG_DEBUG("invalid_argument thrown for tagged value: " + std::string(value)); + } + } else { + throw std::runtime_error("GetTaggedNames: offset exceeds size of text list"); + } + } + return tagged_values; +} + // Get a list of tagged names const std::multimap& EdgeInfo::GetTags() const { // we could check `tag_cache_.empty()` here, but many edges contain no tags @@ -207,12 +259,23 @@ const std::multimap& EdgeInfo::GetTags() const { // Skip any non tagged names if (ni->tagged_) { if (ni->name_offset_ < names_list_length_) { - std::string name = names_list_ + ni->name_offset_; + const char* value = names_list_ + ni->name_offset_; try { - TaggedValue tv = static_cast(name[0]); - if (tv != baldr::TaggedValue::kPronunciation) - tag_cache_.emplace(tv, name.substr(1)); - } catch (const std::logic_error& arg) { LOG_DEBUG("logic_error thrown for name: " + name); } + // no pronunciations for some reason... + TaggedValue tv = static_cast(value[0]); + if (tv == baldr::TaggedValue::kLinguistic) { + continue; + } + // get whatever tag value was in there + // add a per tag parser that returns 0 or more strings, parser skips tags it doesnt know + auto contents = parse_tagged_value(value); + for (const std::string& c : contents) { + // remove the leading TaggedValue byte from the content + tag_cache_.emplace(tv, c.substr(1)); + } + } catch (const std::logic_error& arg) { + LOG_DEBUG("logic_error thrown for tagged value: " + std::string(value)); + } } else { throw std::runtime_error("GetTags: offset exceeds size of text list"); } @@ -226,9 +289,10 @@ const std::multimap& EdgeInfo::GetTags() const { return tag_cache_; } -std::unordered_map> EdgeInfo::GetPronunciationsMap() const { - std::unordered_map> index_pronunciation_map; - index_pronunciation_map.reserve(name_count()); +std::unordered_map> +EdgeInfo::GetLinguisticMap() const { + std::unordered_map> index_linguistic_map; + index_linguistic_map.reserve(name_count()); const NameInfo* ni = name_info_list_; for (uint32_t i = 0; i < name_count(); i++, ni++) { if (!ni->tagged_) @@ -238,38 +302,48 @@ std::unordered_map> EdgeInfo::GetPronun const auto* name = names_list_ + ni->name_offset_; try { TaggedValue tv = static_cast(name[0]); - if (tv == baldr::TaggedValue::kPronunciation) { - size_t pos = 1; - while (pos < strlen(name)) { - const auto header = midgard::unaligned_read(name + pos); - pos += 3; - std::unordered_map>::iterator iter = - index_pronunciation_map.find(header.name_index_); - - if (iter == index_pronunciation_map.end()) - index_pronunciation_map.emplace( - std::make_pair(header.name_index_, - std::make_pair(header.phonetic_alphabet_, - std::string((name + pos), header.length_)))); - else { - if (header.phonetic_alphabet_ > (iter->second).first) { - iter->second = std::make_pair(header.phonetic_alphabet_, - std::string((name + pos), header.length_)); - } + if (tv == baldr::TaggedValue::kLinguistic) { + name += 1; + while (*name != '\0') { + std::tuple liguistic_attributes; + uint8_t name_index = 0; + const auto header = midgard::unaligned_read(name); + + std::get(liguistic_attributes) = + header.phonetic_alphabet_; + std::get(liguistic_attributes) = header.language_; + + std::get(liguistic_attributes) = + std::string(name + kLinguisticHeaderSize, header.length_); + name += header.length_ + kLinguisticHeaderSize; + name_index = header.name_index_; + + auto iter = index_linguistic_map.insert(std::make_pair(name_index, liguistic_attributes)); + + // Edge case. Sometimes when phonemes exist but the language for that phoneme is not + // supported in that area, we toss the phoneme but add the default language for that + // name/destination key. We only want to return the highest ranking phoneme type + // over the language. + if (!iter.second && + (std::get(liguistic_attributes) > + std::get(iter.first->second)) && + (std::get(liguistic_attributes) != + static_cast(PronunciationAlphabet::kNone)) && + (std::get(liguistic_attributes) == + std::get(iter.first->second))) { + iter.first->second = liguistic_attributes; } - - pos += header.length_; } } } catch (const std::invalid_argument& arg) { LOG_DEBUG("invalid_argument thrown for name: " + std::string(name)); } } else { - throw std::runtime_error("GetPronunciationsMap: offset exceeds size of text list"); + throw std::runtime_error("GetLinguisticMap: offset exceeds size of text list"); } } - return index_pronunciation_map; + return index_linguistic_map; } // Get the types. Are these names route numbers or not? @@ -299,6 +373,24 @@ std::string EdgeInfo::encoded_shape() const { : std::string(encoded_shape_, ei_.encoded_shape_size_); } +// Returns the encoded elevation along the edge as well as the sampling interval. +// The sampling interval is uniform (based on the length of the edge). +std::vector EdgeInfo::encoded_elevation(const uint32_t length, double& interval) const { + if (!has_elevation()) { + // If no elevation then the edge length is shorter than the sampling interval...set the + // interval to the edge length. The elevation at the nodes will be used in this case. + interval = length; + return std::vector(); + } + + // Set the sampling interval + interval = midgard::sampling_interval(length); + + // Number of elevations encoded (start and end of the edge are stored in NodeInfo) + uint32_t n = midgard::encoded_elevation_count(length); + return std::vector(encoded_elevation_, encoded_elevation_ + n); +} + int8_t EdgeInfo::layer() const { const auto& tags = GetTags(); auto itr = tags.find(TaggedValue::kLayer); diff --git a/src/baldr/graphid.cc b/src/baldr/graphid.cc index a9c41106f3..35c6685884 100644 --- a/src/baldr/graphid.cc +++ b/src/baldr/graphid.cc @@ -1,5 +1,3 @@ -#include - #include "baldr/graphid.h" namespace valhalla { diff --git a/src/baldr/graphreader.cc b/src/baldr/graphreader.cc index 0fe628b7e9..b4086c73a7 100644 --- a/src/baldr/graphreader.cc +++ b/src/baldr/graphreader.cc @@ -1,5 +1,3 @@ -#include -#include #include #include #include @@ -105,7 +103,7 @@ GraphReader::tile_extract_t::tile_extract_t(const boost::property_tree::ptree& p } // couldn't load it if (tiles.empty()) { - LOG_WARN("Tile extract contained no usuable tiles"); + LOG_WARN("Tile extract contained no usable tiles"); archive.reset(); } // loaded ok but with possibly bad blocks else { @@ -145,7 +143,7 @@ GraphReader::tile_extract_t::tile_extract_t(const boost::property_tree::ptree& p } // couldn't load it if (traffic_tiles.empty()) { - LOG_WARN("Traffic tile extract contained no usuable tiles"); + LOG_WARN("Traffic tile extract contained no usable tiles"); archive.reset(); } // loaded ok but with possibly bad blocks else { @@ -786,10 +784,19 @@ GraphId GraphReader::GetShortcut(const GraphId& id) { GraphId edgeid = id; const NodeInfo* node = nullptr; const DirectedEdge* cont_de = nullptr; + const DirectedEdge* first_de = GetOpposingEdge(id); while (true) { // Get the continuing directed edge. Initial case is to use the opposing // directed edge. - cont_de = (node == nullptr) ? GetOpposingEdge(id) : continuing_edge(tile, edgeid, node); + if (node) { + cont_de = continuing_edge(tile, edgeid, node); + if (cont_de == first_de) { + LOG_DEBUG("GraphReader::GetShortcut edges are in a loop and found no shortcut among them"); + break; + } + } else { + cont_de = first_de; + } if (cont_de == nullptr) { LOG_DEBUG("GraphReader::GetShortcut found no clear continuing edge"); break; @@ -975,6 +982,16 @@ int GraphReader::GetTimezone(const baldr::GraphId& node, graph_tile_ptr& tile) { return (tile == nullptr) ? 0 : tile->node(node)->timezone(); } +int GraphReader::GetTimezoneFromEdge(const baldr::GraphId& edge, graph_tile_ptr& tile) { + auto nodes = GetDirectedEdgeNodes(edge, tile); + if (const auto* node = nodeinfo(nodes.first, tile)) + return node->timezone(); + else if (const auto* node = nodeinfo(nodes.second, tile)) + return node->timezone(); + + return 0; +} + std::shared_ptr GraphReader::GetIncidentTile(const GraphId& tile_id) const { return enable_incidents_ ? incident_singleton_t::get(tile_id.Tile_Base()) diff --git a/src/baldr/graphtile.cc b/src/baldr/graphtile.cc index bbe0c150d8..265ee200c9 100644 --- a/src/baldr/graphtile.cc +++ b/src/baldr/graphtile.cc @@ -18,8 +18,6 @@ #include #include #include -#include -#include #include #include #include @@ -753,10 +751,9 @@ std::string GraphTile::GetName(const uint32_t textlist_offset) const { } } -// Convenience method to process the signs for an edge given the -// directed edge or node index. +// Return the signs for a given directed edge or node index. std::vector GraphTile::GetSigns(const uint32_t idx, bool signs_on_node) const { - uint32_t count = header_->signcount(); + const int32_t count = header_->signcount(); std::vector signs; if (count == 0) { return signs; @@ -767,7 +764,7 @@ std::vector GraphTile::GetSigns(const uint32_t idx, bool signs_on_node int32_t low = 0; int32_t high = count - 1; int32_t mid; - auto found = count; + int32_t found = count; while (low <= high) { mid = (low + high) / 2; const auto& sign = signs_[mid]; @@ -788,31 +785,51 @@ std::vector GraphTile::GetSigns(const uint32_t idx, bool signs_on_node for (; found < count && signs_[found].index() == idx; ++found) { if (signs_[found].text_offset() < textlist_size_) { - std::string text = (textlist_ + signs_[found].text_offset()); + const char* text = (textlist_ + signs_[found].text_offset()); + + bool isLinguistic = (signs_[found].type() == Sign::Type::kLinguistic); + + bool is_node_sign_type = signs_[found].type() == Sign::Type::kJunctionName || + signs_[found].type() == Sign::Type::kTollName; // only add named signs when asking for signs at the node and // only add edge signs when asking for signs at the edges. // is_route_num_type indicates if this phonome is for a node or not; therefore, - // we only return a node phoneme when is_route_num_type and signs_on_node are both true and - // we only return an edge phoneme when is_route_num_type and signs_on_node are both false - if (((signs_[found].type() == Sign::Type::kJunctionName || - (signs_[found].type() == Sign::Type::kPronunciation && - signs_[found].is_route_num_type())) && + // we only return a node phoneme when is_route_num_type and signs_on_node are both true + // and we only return an edge phoneme when is_route_num_type and signs_on_node are both + // false + if (((is_node_sign_type || (isLinguistic && signs_[found].is_route_num_type())) && signs_on_node) || - (((signs_[found].type() != Sign::Type::kJunctionName && - signs_[found].type() != Sign::Type::kPronunciation) || - (signs_[found].type() == Sign::Type::kPronunciation && - !signs_[found].is_route_num_type())) && - !signs_on_node)) + (((!is_node_sign_type && !isLinguistic) || + (isLinguistic && !signs_[found].is_route_num_type())) && + !signs_on_node)) { + std::string sign_text = text; + if (isLinguistic) { + sign_text.clear(); + while (*text != '\0') { + if (signs_[found].type() == Sign::Type::kLinguistic) { + const auto header = midgard::unaligned_read(text); + sign_text.append( + std::string(reinterpret_cast(&header), kLinguisticHeaderSize) + + std::string((text + kLinguisticHeaderSize), header.length_)); + + text += header.length_ + kLinguisticHeaderSize; + } + } + } + signs.emplace_back(signs_[found].type(), signs_[found].is_route_num_type(), - signs_[found].tagged(), false, 0, 0, text); + signs_[found].tagged(), false, 0, 0, sign_text); + } } else { throw std::runtime_error("GetSigns: offset exceeds size of text list"); } } + if (signs.size() == 0) { LOG_ERROR("No signs found for idx = " + std::to_string(idx)); } + return signs; } @@ -820,21 +837,20 @@ std::vector GraphTile::GetSigns(const uint32_t idx, bool signs_on_node // directed edge index. std::vector GraphTile::GetSigns( const uint32_t idx, - std::unordered_map>& index_pronunciation_map, + std::unordered_map>& index_linguistic_map, bool signs_on_node) const { - uint32_t count = header_->signcount(); + const int32_t count = header_->signcount(); std::vector signs; if (count == 0) { return signs; } - index_pronunciation_map.reserve(count); // Signs are sorted by edge index. // Binary search to find a sign with matching edge index. int32_t low = 0; int32_t high = count - 1; int32_t mid; - auto found = count; + int32_t found = count; while (low <= high) { mid = (low + high) / 2; const auto& sign = signs_[mid]; @@ -856,43 +872,62 @@ std::vector GraphTile::GetSigns( if (signs_[found].text_offset() < textlist_size_) { const auto* text = (textlist_ + signs_[found].text_offset()); - if (signs_[found].tagged() && signs_[found].type() == Sign::Type::kPronunciation) { + if (signs_[found].tagged() && signs_[found].type() == Sign::Type::kLinguistic) { // is_route_num_type indicates if this phonome is for a node or not if ((signs_[found].is_route_num_type() && signs_on_node) || (!signs_[found].is_route_num_type() && !signs_on_node)) { - size_t pos = 0; - while (pos < strlen(text)) { - const auto header = midgard::unaligned_read(text + pos); - pos += 3; - - auto iter = index_pronunciation_map.insert( - std::make_pair(header.name_index_, - std::make_pair(header.phonetic_alphabet_, - std::string((text + pos), header.length_)))); + while (*text != '\0') { + std::tuple liguistic_attributes; + uint8_t name_index = 0; + if (signs_[found].type() == Sign::Type::kLinguistic) { + const auto header = midgard::unaligned_read(text); + + std::get(liguistic_attributes) = + header.phonetic_alphabet_; + std::get(liguistic_attributes) = header.language_; + + std::get(liguistic_attributes) = + std::string(text + kLinguisticHeaderSize, header.length_); + text += header.length_ + kLinguisticHeaderSize; + name_index = header.name_index_; + + } else + continue; + + // Edge case. Sometimes when phonemes exist but the language for that phoneme is not + // supported in that area, we toss the phoneme but add the default language for that + // name/destination key. We only want to return the highest ranking phoneme type + // over the language. + auto iter = index_linguistic_map.insert(std::make_pair(name_index, liguistic_attributes)); if (!iter.second) { - if (header.phonetic_alphabet_ > iter.first->second.first) { - iter.first->second = std::make_pair(header.phonetic_alphabet_, - std::string((text + pos), header.length_)); + if ((std::get(liguistic_attributes) > + std::get(iter.first->second)) && + (std::get(liguistic_attributes) != + static_cast(PronunciationAlphabet::kNone)) && + (std::get(liguistic_attributes) == + std::get(iter.first->second))) { + iter.first->second = liguistic_attributes; } } - - pos += header.length_; } } continue; } + bool is_node_sign_type = signs_[found].type() == Sign::Type::kJunctionName || + signs_[found].type() == Sign::Type::kTollName; + // only add named signs when asking for signs at the node and // only add edge signs when asking for signs at the edges. - if ((signs_[found].type() == Sign::Type::kJunctionName && signs_on_node) || - (signs_[found].type() != Sign::Type::kJunctionName && !signs_on_node)) + if ((is_node_sign_type && signs_on_node) || (!is_node_sign_type && !signs_on_node)) signs.emplace_back(signs_[found].type(), signs_[found].is_route_num_type(), signs_[found].tagged(), false, 0, 0, text); } else { throw std::runtime_error("GetSigns: offset exceeds size of text list"); } } + if (signs.size() == 0) { LOG_ERROR("No signs found for idx = " + std::to_string(idx)); } @@ -1159,7 +1194,7 @@ std::vector GraphTile::GetAccessRestrictions(const uint32_t i } // Access restriction are sorted by edge Id. - // Binary search to find a access restriction with matching edge Id. + // Binary search to find an access restriction with matching edge Id. int32_t low = 0; int32_t high = count - 1; int32_t mid; diff --git a/src/baldr/incident_singleton.h b/src/baldr/incident_singleton.h index 39dd800a06..b5c5f36c4f 100644 --- a/src/baldr/incident_singleton.h +++ b/src/baldr/incident_singleton.h @@ -111,7 +111,7 @@ struct incident_singleton_t { return {}; } - // hand back something that isnt modifyable + // hand back something that isnt modifiable return std::const_pointer_cast(tile); } @@ -176,7 +176,7 @@ struct incident_singleton_t { * equal to the timestamp of the last scan that was performed will be read into the incident cache. * Tiles which are in the cache but were not found on the disk in the last scan will b epurged as * they have been removed from the log. If a static tileset was provided any tiles which are found - * in the log but are nto part of the tileset will be ignored. When the timestamp for the last check + * in the log but are not part of the tileset will be ignored. When the timestamp for the last check * is older than a timestamp for a given file that file is replaced with whatever its contents are * on disk. * diff --git a/src/baldr/location.cc b/src/baldr/location.cc index 954f62a640..bd9b9e4066 100644 --- a/src/baldr/location.cc +++ b/src/baldr/location.cc @@ -1,5 +1,3 @@ -#include - #include "baldr/location.h" #include "baldr/rapidjson_utils.h" #include "midgard/logging.h" @@ -27,12 +25,14 @@ Location::Location(const midgard::PointLL& latlng, unsigned int min_inbound_reach, unsigned long radius, const PreferredSide& side, + valhalla::RoadClass street_side_cutoff, const SearchFilter& search_filter, std::optional preferred_layer) : latlng_(latlng), stoptype_(stoptype), min_outbound_reach_(min_outbound_reach), min_inbound_reach_(min_inbound_reach), radius_(radius), preferred_side_(side), node_snap_tolerance_(5), heading_tolerance_(60), search_cutoff_(35000), - street_side_tolerance_(5), street_side_max_distance_(1000), search_filter_(search_filter), + street_side_tolerance_(5), street_side_max_distance_(1000), + street_side_cutoff_(street_side_cutoff), search_filter_(search_filter), preferred_layer_(std::move(preferred_layer)) { } @@ -43,6 +43,7 @@ bool Location::operator==(const Location& o) const { node_snap_tolerance_ == o.node_snap_tolerance_ && street_side_tolerance_ == o.street_side_tolerance_ && street_side_max_distance_ == o.street_side_max_distance_ && + street_side_cutoff_ == o.street_side_cutoff_ && min_outbound_reach_ == o.min_outbound_reach_ && min_inbound_reach_ == o.min_inbound_reach_ && radius_ == o.radius_ && preferred_side_ == o.preferred_side_ && display_latlng_ == o.display_latlng_ && preferred_layer_ == o.preferred_layer_; diff --git a/src/baldr/nodeinfo.cc b/src/baldr/nodeinfo.cc index 4c12578368..a474244c76 100644 --- a/src/baldr/nodeinfo.cc +++ b/src/baldr/nodeinfo.cc @@ -34,13 +34,7 @@ json::MapPtr admin_json(const AdminInfo& admin, uint16_t tz_index) { // timezone auto tz = DateTime::get_tz_db().from_index(tz_index); if (tz) { - // TODO: so much to do but posix tz has pretty much all the info - // TODO: need to include ptz.h from HowardHinnant - // m->emplace("time_zone_posix", tz->to_posix_string()); m->emplace("time_zone_name", tz->name()); - // TODO: need to include ptz.h from HowardHinnant - // if (tz->has_dst()) - // m->emplace("daylight_savings_time_zone_name", tz->dst_zone_name()); } return m; @@ -154,14 +148,15 @@ void NodeInfo::set_admin_index(const uint16_t admin_index) { } // Set the timezone index. -void NodeInfo::set_timezone(const uint32_t timezone) { - if (timezone > kMaxTimeZonesPerTile) { - // Log an error and set count to max. - LOG_ERROR("NodeInfo: timezone index exceeds max: " + std::to_string(timezone)); - timezone_ = kMaxTimeZonesPerTile; - } else { - timezone_ = timezone; - } +void NodeInfo::set_timezone(const uint32_t tz_idx) { + if (tz_idx > kMaxTimeZoneIdExt1) + throw std::runtime_error("NodeInfo: timezone index exceeds max: " + std::to_string(tz_idx)); + timezone_ = tz_idx & ((1 << 9) - 1); // first 9 bits for backwards compat + timezone_ext_1_ = + (tz_idx & (1 << 9)) >> 9; // 10th bit for new timezones carved out of old ones in 2023 + // uncomment if a new timezone ever gets created from a previously new + // timezone (reference release is 2023c) + // timezone_ext_2_ = (tz_idx & (1 << 10)) >> 10; } // Set the driveability of the local directed edge given a local @@ -189,7 +184,7 @@ void NodeInfo::set_type(const NodeType type) { type_ = static_cast(type); } -// Set the number of driveable edges on the local level. Subtract 1 so +// Set the number of drivable edges on the local level. Subtract 1 so // a value up to kMaxLocalEdgeIndex+1 can be stored. void NodeInfo::set_local_edge_count(const uint32_t n) { if (n > kMaxLocalEdgeIndex + 1) { @@ -208,6 +203,16 @@ void NodeInfo::set_drive_on_right(const bool rsd) { drive_on_right_ = rsd; } +// Set the elevation at this node. +void NodeInfo::set_elevation(const float elevation) { + if (elevation < kNodeMinElevation) { + elevation_ = 0; + } else { + uint32_t elev = static_cast((elevation - kNodeMinElevation) / kNodeElevationPrecision); + elevation_ = (elev > kNodeMaxStoredElevation) ? kNodeMaxStoredElevation : elev; + } +} + // Sets the flag indicating if access was originally tagged. void NodeInfo::set_tagged_access(const bool tagged_access) { tagged_access_ = tagged_access; @@ -264,6 +269,7 @@ json::MapPtr NodeInfo::json(const graph_tile_ptr& tile) const { auto m = json::map({ {"lon", json::fixed_t{latlng(tile->header()->base_ll()).first, 6}}, {"lat", json::fixed_t{latlng(tile->header()->base_ll()).second, 6}}, + {"elevation", json::fixed_t{elevation(), 2}}, {"edge_count", static_cast(edge_count_)}, {"access", access_json(access_)}, {"tagged_access", static_cast(tagged_access_)}, diff --git a/src/baldr/shortcut_recovery.h b/src/baldr/shortcut_recovery.h index 4164209e65..63010d7d3d 100644 --- a/src/baldr/shortcut_recovery.h +++ b/src/baldr/shortcut_recovery.h @@ -136,6 +136,10 @@ struct shortcut_recovery_t { uint32_t accumulated_length = current_edge->length(); // walk edges until we find the same ending node as the shortcut + // Use a roundoff error based on number of edges when comparing sum of individual + // edge lengths to shortcut length. + uint32_t edge_count = 1; + uint32_t roundoff_error = 1; while (current_edge->endnode() != shortcut->endnode()) { // get the node at the end of the last edge we added const NodeInfo* node = reader.GetEndNode(current_edge, tile); @@ -158,6 +162,7 @@ struct shortcut_recovery_t { edge.use() == shortcut->use() && edge.classification() == shortcut->classification() && edge.roundabout() == shortcut->roundabout() && edge.link() == shortcut->link() && edge.toll() == shortcut->toll() && edge.destonly() == shortcut->destonly() && + edge.destonly_hgv() == shortcut->destonly_hgv() && edge.unpaved() == shortcut->unpaved() && edge.surface() == shortcut->surface() && edge.use() != Use::kConstruction /*&& edge.speed() == shortcut->speed()*/) { // we are going to keep this edge @@ -173,7 +178,9 @@ struct shortcut_recovery_t { } // if we didnt add an edge or we went over the length we failed - if (current_edge == nullptr || accumulated_length > shortcut->length()) { + ++edge_count; + roundoff_error = std::round(edge_count * 0.5) + 1; + if (current_edge == nullptr || accumulated_length > (shortcut->length() + roundoff_error)) { LOG_TRACE("Unable to recover shortcut for edgeid " + std::to_string(shortcut_id) + " | accumulated_length: " + std::to_string(accumulated_length) + " | shortcut_length: " + std::to_string(shortcut->length())); @@ -182,7 +189,7 @@ struct shortcut_recovery_t { } // we somehow got to the end via a shorter path - if (accumulated_length < shortcut->length()) { + if (accumulated_length < (shortcut->length() - roundoff_error)) { LOG_TRACE("Unable to recover shortcut for edgeid (accumulated_length < shortcut->length()) " + std::to_string(shortcut_id) + " | accumulated_length: " + std::to_string(accumulated_length) + @@ -206,7 +213,7 @@ struct shortcut_recovery_t { * the reader is nullptr then the cache will not be filled and recovery will be on the fly * * @param reader the reader used to initialize the cache the first time - * @return a filled cache mapping shortcuts to superceeded edges + * @return a filled cache mapping shortcuts to superseded edges */ static shortcut_recovery_t& get_instance(valhalla::baldr::GraphReader* reader = nullptr) { static shortcut_recovery_t cache{reader}; @@ -214,11 +221,11 @@ struct shortcut_recovery_t { } /** - * returns the list of graphids of the edges superceded by the provided shortcut. saddly because we + * returns the list of graphids of the edges superseded by the provided shortcut. sadly because we * may have to recover the shortcut on the fly we cannot return const reference here * * @param shortcut_id the shortcuts edge id - * @return the list of superceded edges + * @return the list of superseded edges */ std::vector get(const valhalla::baldr::GraphId& shortcut_id, valhalla::baldr::GraphReader& reader) const { diff --git a/src/baldr/streetname.cc b/src/baldr/streetname.cc index ec764eb644..a071bc1341 100644 --- a/src/baldr/streetname.cc +++ b/src/baldr/streetname.cc @@ -1,4 +1,3 @@ -#include #include #include diff --git a/src/baldr/streetname_us.cc b/src/baldr/streetname_us.cc index fc992e0b96..b23f6253e9 100644 --- a/src/baldr/streetname_us.cc +++ b/src/baldr/streetname_us.cc @@ -1,7 +1,5 @@ -#include - -#include "baldr/streetname.h" #include "baldr/streetname_us.h" +#include "baldr/streetname.h" namespace valhalla { namespace baldr { diff --git a/src/baldr/streetnames.cc b/src/baldr/streetnames.cc index 526fc34dbf..8c1bdacce7 100644 --- a/src/baldr/streetnames.cc +++ b/src/baldr/streetnames.cc @@ -1,4 +1,3 @@ -#include #include #include diff --git a/src/baldr/streetnames_factory.cc b/src/baldr/streetnames_factory.cc index 35a0708698..37b14a07fe 100644 --- a/src/baldr/streetnames_factory.cc +++ b/src/baldr/streetnames_factory.cc @@ -1,4 +1,3 @@ -#include #include #include diff --git a/src/baldr/streetnames_us.cc b/src/baldr/streetnames_us.cc index aeb0725d57..79eaa54ab7 100644 --- a/src/baldr/streetnames_us.cc +++ b/src/baldr/streetnames_us.cc @@ -1,4 +1,3 @@ -#include #include #include diff --git a/src/baldr/tilehierarchy.cc b/src/baldr/tilehierarchy.cc index 36b941f36f..7a5417f11c 100644 --- a/src/baldr/tilehierarchy.cc +++ b/src/baldr/tilehierarchy.cc @@ -127,10 +127,10 @@ GraphId TileHierarchy::parent(const GraphId& child_tile_id) { auto parent_level = child_tile_id.level() - 1; const auto& parent_tiling = get_tiling(parent_level); const auto& child_tiling = get_tiling(child_tile_id.level()); - // grab just off of the childs corner to avoid edge cases + // grab just off of the child's corner to avoid edge cases auto corner = child_tiling.Base(child_tile_id.tileid()) + midgard::VectorXY{parent_tiling.TileSize() / 2, parent_tiling.TileSize() / 2}; - // pick the parent from the childs coordinate + // pick the parent from the child's coordinate auto parent_tile_index = parent_tiling.TileId(corner); return GraphId(parent_tile_index, parent_level, 0); } diff --git a/src/baldr/tz_alt.cpp b/src/baldr/tz_alt.cpp index 9e37047dee..0d48f38a0c 100644 --- a/src/baldr/tz_alt.cpp +++ b/src/baldr/tz_alt.cpp @@ -107,9 +107,7 @@ #include "date_time_europe.h" #include "date_time_leapseconds.h" #include "date_time_northamerica.h" -#include "date_time_pacificnew.h" #include "date_time_southamerica.h" -#include "date_time_systemv.h" #include "date_time_windows_zones.h" #ifdef __APPLE__ @@ -527,7 +525,7 @@ bool native_to_standard_timezone_name(const std::string& native_tz_name, std::string& standard_tz_name) { - // TOOD! Need be a case insensitive compare? + // TODO! Need be a case insensitive compare? if (native_tz_name == "UTC") { standard_tz_name = "Etc/UTC"; @@ -2831,8 +2829,8 @@ static std::unique_ptr curl_init() { - static const auto curl_is_now_initiailized = curl_global(); - (void)curl_is_now_initiailized; + static const auto curl_is_now_initialized = curl_global(); + (void)curl_is_now_initialized; return std::unique_ptr{::curl_easy_init()}; } @@ -2944,12 +2942,12 @@ remove_folder_and_subfolders(const std::string& folder) from.assign(folder.begin(), folder.end()); from.push_back('\0'); from.push_back('\0'); - SHFILEOPSTRUCT fo{}; // Zero initialize. - fo.wFunc = FO_DELETE; - fo.pFrom = from.data(); - fo.fFlags = FOF_NO_UI; - int ret = SHFileOperation(&fo); - if (ret == 0 && !fo.fAnyOperationsAborted) + SHFILEOPSTRUCT file_op{}; // Zero initialize. + file_op.wFunc = FO_DELETE; + file_op.pFrom = from.data(); + file_op.fFlags = FOF_NO_UI; + int ret = SHFileOperation(&file_op); + if (ret == 0 && !file_op.fAnyOperationsAborted) return true; return false; # endif // !USE_SHELL_API @@ -3462,7 +3460,7 @@ init_tzdb() CONSTDATA char*const files[] = { "africa", "antarctica", "asia", "australasia", "backward", "etcetera", "europe", - "pacificnew", "northamerica", "southamerica", "systemv", "leapseconds" + "northamerica", "southamerica", "leapseconds" }; for (const auto& filename : files) @@ -4065,13 +4063,10 @@ static std::vector get_tz_data_file_list() { tz_data_file_list.emplace_back(date_time_backward, date_time_backward + date_time_backward_len); tz_data_file_list.emplace_back(date_time_etcetera, date_time_etcetera + date_time_etcetera_len); tz_data_file_list.emplace_back(date_time_europe, date_time_europe + date_time_europe_len); - tz_data_file_list.emplace_back(date_time_pacificnew, - date_time_pacificnew + date_time_pacificnew_len); tz_data_file_list.emplace_back(date_time_northamerica, date_time_northamerica + date_time_northamerica_len); tz_data_file_list.emplace_back(date_time_southamerica, date_time_southamerica + date_time_southamerica_len); - tz_data_file_list.emplace_back(date_time_systemv, date_time_systemv + date_time_systemv_len); tz_data_file_list.emplace_back(date_time_leapseconds, date_time_leapseconds + date_time_leapseconds_len); return tz_data_file_list; diff --git a/src/baldr/verbal_text_formatter_us.cc b/src/baldr/verbal_text_formatter_us.cc index 1b4dfc45d0..cf49e912d9 100644 --- a/src/baldr/verbal_text_formatter_us.cc +++ b/src/baldr/verbal_text_formatter_us.cc @@ -1,8 +1,5 @@ -#include -#include - -#include "baldr/verbal_text_formatter.h" #include "baldr/verbal_text_formatter_us.h" +#include "baldr/verbal_text_formatter.h" #include "midgard/util.h" namespace valhalla { diff --git a/src/baldr/verbal_text_formatter_us_co.cc b/src/baldr/verbal_text_formatter_us_co.cc index 2c7dcf10ff..5fcd3158e5 100644 --- a/src/baldr/verbal_text_formatter_us_co.cc +++ b/src/baldr/verbal_text_formatter_us_co.cc @@ -1,9 +1,6 @@ -#include -#include - +#include "baldr/verbal_text_formatter_us_co.h" #include "baldr/verbal_text_formatter.h" #include "baldr/verbal_text_formatter_us.h" -#include "baldr/verbal_text_formatter_us_co.h" #include "midgard/util.h" namespace valhalla { diff --git a/src/baldr/verbal_text_formatter_us_tx.cc b/src/baldr/verbal_text_formatter_us_tx.cc index c6cb11638a..7000fa5d2d 100644 --- a/src/baldr/verbal_text_formatter_us_tx.cc +++ b/src/baldr/verbal_text_formatter_us_tx.cc @@ -1,9 +1,6 @@ -#include -#include - +#include "baldr/verbal_text_formatter_us_tx.h" #include "baldr/verbal_text_formatter.h" #include "baldr/verbal_text_formatter_us.h" -#include "baldr/verbal_text_formatter_us_tx.h" #include "midgard/util.h" namespace valhalla { diff --git a/src/bindings/python/CMakeLists.txt b/src/bindings/python/CMakeLists.txt index e4ce5f31fd..7bd677bca1 100644 --- a/src/bindings/python/CMakeLists.txt +++ b/src/bindings/python/CMakeLists.txt @@ -1,4 +1,13 @@ -add_subdirectory(${VALHALLA_SOURCE_DIR}/third_party/pybind11 ${CMAKE_BINARY_DIR}/third_party/pybind11) +if (PREFER_EXTERNAL_DEPS) + find_package(pybind11 QUIET) + if (NOT pybind11_FOUND) + message(WARNING "No pybind11 found in system libraries, using vendored pybind11...") + endif() +endif() + +if (NOT TARGET pybind11::pybind11_headers) + add_subdirectory(${VALHALLA_SOURCE_DIR}/third_party/pybind11 ${CMAKE_BINARY_DIR}/third_party/pybind11) +endif() pybind11_add_module(python_valhalla python.cc) target_link_libraries(python_valhalla PUBLIC valhalla) @@ -13,8 +22,8 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/actor.py ${CMAKE_CURRENT_BINARY_DIR}/ message(STATUS "Installing python modules to ${Python_SITEARCH}") install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/valhalla DESTINATION "${Python_SITEARCH}" -COMPONENT python_valhalla +COMPONENT runtime FILES_MATCHING PATTERN "*.py") install(TARGETS python_valhalla DESTINATION "${Python_SITEARCH}/valhalla" -COMPONENT python_valhalla) +COMPONENT runtime) diff --git a/src/bindings/python/python.cc b/src/bindings/python/python.cc index 3701cf6d73..63dda05ff0 100644 --- a/src/bindings/python/python.cc +++ b/src/bindings/python/python.cc @@ -4,7 +4,6 @@ #include #include #include -#include #include #include "baldr/rapidjson_utils.h" diff --git a/src/config.cc b/src/config.cc new file mode 100644 index 0000000000..36c2ce6d9b --- /dev/null +++ b/src/config.cc @@ -0,0 +1,47 @@ +#include "config.h" + +namespace { +struct config_singleton_t { +protected: + boost::property_tree::ptree config_; + + config_singleton_t() = delete; + + config_singleton_t(const std::string& config_file_or_inline) { + if (config_file_or_inline.empty()) { + throw std::runtime_error("Config singleton was not initialized before usage"); + } + + try { + if (std::filesystem::is_regular_file(config_file_or_inline)) { + rapidjson::read_json(config_file_or_inline, config_); + } else { + auto inline_config = std::stringstream(config_file_or_inline); + rapidjson::read_json(inline_config, config_); + } + } catch (const std::filesystem::filesystem_error& e) { + if (e.code() == std::errc::filename_too_long) { + auto inline_config = std::stringstream(config_file_or_inline); + rapidjson::read_json(inline_config, config_); + } else { + throw e; + } + } + } + +public: + config_singleton_t(config_singleton_t const&) = delete; + void operator=(const config_singleton_t&) = delete; + friend const boost::property_tree::ptree& + valhalla::config(const std::string& config_file_or_inline); +}; +} // namespace + +namespace valhalla { + +const boost::property_tree::ptree& config(const std::string& config_file_or_inline) { + static config_singleton_t instance(config_file_or_inline); + return instance.config_; +} + +}; // namespace valhalla diff --git a/src/loki/CMakeLists.txt b/src/loki/CMakeLists.txt index 21eeb6a648..24ba810bcb 100644 --- a/src/loki/CMakeLists.txt +++ b/src/loki/CMakeLists.txt @@ -21,11 +21,9 @@ set(sources_with_warnings trace_route_action.cc worker.cc) -# treat date library as system -set(system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include) -if(APPLE) - list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) -endif() +set(system_includes + ${date_include_dir} + $<$:${dirent_include_dir}>) valhalla_module(NAME loki SOURCES ${sources} @@ -35,9 +33,8 @@ valhalla_module(NAME loki PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include + ${rapidjson_include_dir} ${CMAKE_BINARY_DIR} SYSTEM_INCLUDE_DIRECTORIES PUBLIC @@ -48,4 +45,4 @@ valhalla_module(NAME loki valhalla::proto ${valhalla_protobuf_targets} Boost::boost - libprime_server) + ${libprime_server_targets}) diff --git a/src/loki/matrix_action.cc b/src/loki/matrix_action.cc index 9513d99d3a..19f869c041 100644 --- a/src/loki/matrix_action.cc +++ b/src/loki/matrix_action.cc @@ -59,7 +59,7 @@ namespace loki { void loki_worker_t::init_matrix(Api& request) { // we require sources and targets auto& options = *request.mutable_options(); - if (options.action() == Options::sources_to_targets) { + if (options.action() == Options::sources_to_targets || options.action() == Options::expansion) { parse_locations(options.mutable_sources(), valhalla_exception_t{112}); parse_locations(options.mutable_targets(), valhalla_exception_t{112}); } // optimized route uses locations but needs to do a matrix @@ -78,15 +78,9 @@ void loki_worker_t::init_matrix(Api& request) { if (options.sources_size() < 1) { throw valhalla_exception_t{121}; }; - for (auto& s : *options.mutable_sources()) { - s.clear_heading(); - } if (options.targets_size() < 1) { throw valhalla_exception_t{122}; }; - for (auto& t : *options.mutable_targets()) { - t.clear_heading(); - } // no locations! options.clear_locations(); diff --git a/src/loki/reach.cc b/src/loki/reach.cc index b0884eb6d0..e8375269fb 100644 --- a/src/loki/reach.cc +++ b/src/loki/reach.cc @@ -20,7 +20,7 @@ void Reach::enqueue(const baldr::GraphId& node_id, // skip nodes which are done or invalid if (!node_id.Is_Valid() || done_.find(node_id) != done_.cend()) return; - // if the node isnt accessable bail + // if the node isnt accessible bail if (!reader.GetGraphTile(node_id, tile)) return; const auto* node = tile->node(node_id); diff --git a/src/loki/search.cc b/src/loki/search.cc index df649c47b0..cc286a4d24 100644 --- a/src/loki/search.cc +++ b/src/loki/search.cc @@ -9,7 +9,6 @@ #include #include #include -#include #include using namespace valhalla::midgard; @@ -23,23 +22,22 @@ template inline T square(T v) { return v * v; } -bool is_search_filter_triggered(const DirectedEdge* edge, - const DynamicCost& costing, - const graph_tile_ptr& tile, - const Location::SearchFilter& search_filter) { +bool search_filter(const DirectedEdge* edge, + const DynamicCost& costing, + const graph_tile_ptr& tile, + const Location::SearchFilter& filter) { // check if this edge matches any of the exclusion filters uint32_t road_class = static_cast(edge->classification()); - uint32_t min_road_class = static_cast(search_filter.min_road_class_); - uint32_t max_road_class = static_cast(search_filter.max_road_class_); + uint32_t min_road_class = static_cast(filter.min_road_class_); + uint32_t max_road_class = static_cast(filter.max_road_class_); // Note that min_ and max_road_class are integers where, by default, max_road_class // is 0 and min_road_class is 7. This filter rejects roads where the functional // road class is outside of the min to max range. return (road_class > min_road_class || road_class < max_road_class) || - (search_filter.exclude_tunnel_ && edge->tunnel()) || - (search_filter.exclude_bridge_ && edge->bridge()) || - (search_filter.exclude_ramp_ && (edge->use() == Use::kRamp)) || - (search_filter.exclude_closures_ && (costing.flow_mask() & kCurrentFlowMask) && + (filter.exclude_tunnel_ && edge->tunnel()) || (filter.exclude_bridge_ && edge->bridge()) || + (filter.exclude_ramp_ && (edge->use() == Use::kRamp)) || + (filter.exclude_closures_ && (costing.flow_mask() & kCurrentFlowMask) && tile->IsClosed(edge)); } @@ -49,15 +47,24 @@ bool side_filter(const PathLocation::PathEdge& edge, const Location& location, G location.preferred_side_ == Location::PreferredSide::EITHER) return false; - // need the driving side for this edge + // need this for further checking of driving side and road class graph_tile_ptr tile; auto* opp = reader.GetOpposingEdge(edge.id, tile); if (!opp) return false; + + // nothing to filter if it is a minor road + // since motorway = 0 and service = 7, higher number means smaller road class + uint32_t road_class = static_cast(opp->classification()); + if (road_class > location.street_side_cutoff_) + return false; + + // need the driving side for this edge auto* node = reader.GetEndNode(opp, tile); if (!node) return false; - // if its on the right side and you drive on the rigth OR if its not on the right and you dont drive + + // if its on the right side and you drive on the right OR if its not on the right and you dont drive // on the right THEN its the same side that you drive on bool same = node->drive_on_right() == (edge.sos == PathLocation::SideOfStreet::RIGHT); // and then if you were asking for the same and it was the same OR if you were asking for opposite @@ -312,8 +319,10 @@ struct bin_handler_t { tangent_angle(index, candidate.point, info.shape(), GetOffsetForHeading(edge->classification(), edge->use()), edge->forward()); auto layer = info.layer(); - // do we want this edge - if (costing->Allowed(edge, tile, kDisallowShortcut)) { + // do we want this edge, note we have to re-evaluate the filter check because we may be + // seeing these edges a second time (filtered out before) + if (costing->Allowed(edge, tile, kDisallowShortcut) && + !search_filter(edge, *costing, tile, location.search_filter_)) { auto reach = get_reach(id, edge); PathLocation::PathEdge path_edge{id, 0, node_ll, distance, PathLocation::NONE, reach.outbound, reach.inbound, @@ -332,7 +341,8 @@ struct bin_handler_t { if (!other_edge) continue; - if (costing->Allowed(other_edge, other_tile, kDisallowShortcut)) { + if (costing->Allowed(other_edge, other_tile, kDisallowShortcut) && + !search_filter(other_edge, *costing, other_tile, location.search_filter_)) { auto opp_angle = std::fmod(angle + 180.f, 360.f); auto reach = get_reach(other_id, other_edge); PathLocation::PathEdge path_edge{other_id, @@ -410,11 +420,13 @@ struct bin_handler_t { PathLocation::PathEdge path_edge{candidate.edge_id, length_ratio, candidate.point, distance, side, reach.outbound, reach.inbound, angle}; - // correlate the edge we found - if (side_filter(path_edge, location, reader) || heading_filter(location, angle) || - layer_filter(location, layer)) { + // correlate the edge we found if its not filtered out + bool hard_filtered = + search_filter(candidate.edge, *costing, candidate.tile, location.search_filter_); + if (!hard_filtered && (side_filter(path_edge, location, reader) || + heading_filter(location, angle) || layer_filter(location, layer))) { filtered.push_back(std::move(path_edge)); - } else if (correlated_edges.insert(candidate.edge_id).second) { + } else if (!hard_filtered && correlated_edges.insert(candidate.edge_id).second) { correlated.edges.push_back(std::move(path_edge)); } // correlate its evil twin @@ -422,7 +434,8 @@ struct bin_handler_t { graph_tile_ptr other_tile; auto opposing_edge_id = reader.GetOpposingEdgeId(candidate.edge_id, other_edge, other_tile); - if (other_edge && costing->Allowed(other_edge, other_tile, kDisallowShortcut)) { + if (other_edge && costing->Allowed(other_edge, other_tile, kDisallowShortcut) && + !search_filter(other_edge, *costing, other_tile, location.search_filter_)) { auto opp_angle = std::fmod(angle + 180.f, 360.f); reach = get_reach(opposing_edge_id, other_edge); PathLocation::PathEdge other_path_edge{opposing_edge_id, 1 - length_ratio, candidate.point, @@ -506,14 +519,22 @@ struct bin_handler_t { continue; } + // lots of places below where we might like to know about the opp edge + const DirectedEdge* opp_edge = nullptr; + graph_tile_ptr opp_tile = tile; + GraphId opp_edgeid; + // if this edge is filtered const auto* edge = tile->directededge(edge_id); if (!costing->Allowed(edge, tile, kDisallowShortcut)) { - // then we try its opposing edge - edge_id = reader.GetOpposingEdgeId(edge_id, edge, tile); // but if we couldnt get it or its filtered too then we move on - if (!edge_id.Is_Valid() || !costing->Allowed(edge, tile, kDisallowShortcut)) + if (!(opp_edgeid = reader.GetOpposingEdgeId(edge_id, opp_edge, opp_tile)) || + !costing->Allowed(opp_edge, opp_tile, kDisallowShortcut)) continue; + // if we will continue with the opposing edge lets swap it in + std::swap(edge, opp_edge); + std::swap(tile, opp_tile); + std::swap(edge_id, opp_edgeid); } // initialize candidates vector: @@ -524,8 +545,12 @@ struct bin_handler_t { bool all_prefiltered = true; for (p_itr = begin; p_itr != end; ++p_itr, ++c_itr) { c_itr->sq_distance = std::numeric_limits::max(); + // for traffic closures we may have only one direction disabled so we must also check opp + // before we can be sure that we can completely filter this edge pair for this location c_itr->prefiltered = - is_search_filter_triggered(edge, *costing, tile, p_itr->location.search_filter_); + search_filter(edge, *costing, tile, p_itr->location.search_filter_) && + (opp_edgeid = reader.GetOpposingEdgeId(edge_id, opp_edge, opp_tile)) && + search_filter(opp_edge, *costing, opp_tile, p_itr->location.search_filter_); // set to false if even one candidate was not filtered all_prefiltered = all_prefiltered && c_itr->prefiltered; } @@ -587,12 +612,10 @@ struct bin_handler_t { // is this edge reachable in the right way bool reachable = reach.outbound >= p_itr->location.min_outbound_reach_ && reach.inbound >= p_itr->location.min_inbound_reach_; - const DirectedEdge* opp_edge = nullptr; - graph_tile_ptr opp_tile = tile; - GraphId opp_edgeid; // it's possible that it isnt reachable but the opposing is, switch to that if so if (!reachable && (opp_edgeid = reader.GetOpposingEdgeId(edge_id, opp_edge, opp_tile)) && - costing->Allowed(opp_edge, opp_tile, kDisallowShortcut)) { + costing->Allowed(opp_edge, opp_tile, kDisallowShortcut) && + !search_filter(opp_edge, *costing, opp_tile, p_itr->location.search_filter_)) { auto opp_reach = check_reachability(begin, end, opp_tile, opp_edge, opp_edgeid); if (opp_reach.outbound >= p_itr->location.min_outbound_reach_ && opp_reach.inbound >= p_itr->location.min_inbound_reach_) { diff --git a/src/loki/status_action.cc b/src/loki/status_action.cc index 511f44fe7d..f58061cd61 100644 --- a/src/loki/status_action.cc +++ b/src/loki/status_action.cc @@ -33,7 +33,7 @@ time_t get_tileset_last_modified(const std::shared_ptr& reader) { namespace valhalla { namespace loki { void loki_worker_t::status(Api& request) const { -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES // if we are in the process of shutting down we signal that here // should react by draining traffic (though they are likely doing this as they are usually the ones // who sent us the request to shutdown) diff --git a/src/loki/transit_available_action.cc b/src/loki/transit_available_action.cc index d5491508d9..d7e71bab4e 100644 --- a/src/loki/transit_available_action.cc +++ b/src/loki/transit_available_action.cc @@ -31,10 +31,9 @@ std::string loki_worker_t::transit_available(Api& request) { // Get a list of tiles required within the radius of the projected point const auto& ll = location.latlng_; DistanceApproximator approximator(ll); - double latdeg = double(location.radius_) / kMetersPerDegreeLat; - double lngdeg = location.radius_ / DistanceApproximator::MetersPerLngDegree(ll.lat()); - AABB2 bbox(Point2d(ll.lng() - lngdeg, ll.lat() - latdeg), - Point2d(ll.lng() + lngdeg, ll.lat() + latdeg)); + auto latdeg = location.radius_ / kMetersPerDegreeLat; + auto lngdeg = location.radius_ / DistanceApproximator::MetersPerLngDegree(ll.lat()); + AABB2 bbox(ll.lng() - lngdeg, ll.lat() - latdeg, ll.lng() + lngdeg, ll.lat() + latdeg); std::vector tilelist = tiles.TileList(bbox); for (auto id : tilelist) { // transit is level hierarchy level 3 diff --git a/src/loki/worker.cc b/src/loki/worker.cc index 135868830a..3f96f4f22e 100644 --- a/src/loki/worker.cc +++ b/src/loki/worker.cc @@ -1,10 +1,8 @@ #include #include #include -#include #include #include -#include #include #include "baldr/json.h" @@ -315,7 +313,7 @@ void loki_worker_t::check_hierarchy_distance(Api& request) { } } -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES prime_server::worker_t::result_t loki_worker_t::work(const std::list& job, void* request_info, @@ -378,8 +376,10 @@ loki_worker_t::work(const std::list& job, case Options::expansion: if (options.expansion_action() == Options::route) { route(request); - } else { + } else if (options.expansion_action() == Options::isochrone) { isochrones(request); + } else { + matrix(request); } result.messages.emplace_back(request.SerializeAsString()); break; diff --git a/src/meili/CMakeLists.txt b/src/meili/CMakeLists.txt index 35bd4c1c91..3ebf3bcb46 100644 --- a/src/meili/CMakeLists.txt +++ b/src/meili/CMakeLists.txt @@ -14,11 +14,9 @@ set(sources_with_warnings transition_cost_model.cc viterbi_search.cc) -# treat date library as system -set(system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include) -if(APPLE) - list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) -endif() +set(system_includes + ${date_include_dir} + $<$:${dirent_include_dir}>) valhalla_module(NAME meili SOURCES ${sources} @@ -28,13 +26,13 @@ valhalla_module(NAME meili PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include ${CMAKE_BINARY_DIR} SYSTEM_INCLUDE_DIRECTORIES PUBLIC ${system_includes} + PRIVATE + ${rapidjson_include_dir} DEPENDS valhalla::sif ${valhalla_protobuf_targets} diff --git a/src/meili/map_matcher.cc b/src/meili/map_matcher.cc index c6365570e4..30395cba9c 100644 --- a/src/meili/map_matcher.cc +++ b/src/meili/map_matcher.cc @@ -397,7 +397,7 @@ MatchResult FindMatchResult(const MapMatcher& mapmatcher, ends_discontinuity}; } - // when the instersection match fails, we do a more labor intensive search at transitions + // when the intersection match fails, we do a more labor intensive search at transitions // for both prev_edge's ending node and next_edge's starting node and see if we could do a // node snap match. We collect the info together and search both prev edge end node and next // edge start node in the below loop diff --git a/src/meili/map_matcher_factory.cc b/src/meili/map_matcher_factory.cc index bf2f6bbd47..f8c3b02ec4 100644 --- a/src/meili/map_matcher_factory.cc +++ b/src/meili/map_matcher_factory.cc @@ -1,5 +1,3 @@ -#include - #include "baldr/graphreader.h" #include "baldr/tilehierarchy.h" #include "sif/autocost.h" diff --git a/src/meili/match_route.cc b/src/meili/match_route.cc index ab003ba56a..fa9d05b89d 100644 --- a/src/meili/match_route.cc +++ b/src/meili/match_route.cc @@ -73,7 +73,7 @@ EdgeSegment::EdgeSegment(baldr::GraphId the_edgeid, /** * Here we return the vector of edge segments between the source and target states. If its a node to - * node route (meaning no realy edge is traversed) then we use the target_result to say what edge the + * node route (meaning no real edge is traversed) then we use the target_result to say what edge the * segment should use * * TODO: we should modify this function to take the range of MatchResults between the source and diff --git a/src/midgard/CMakeLists.txt b/src/midgard/CMakeLists.txt index b92484c50b..ba018a0a86 100644 --- a/src/midgard/CMakeLists.txt +++ b/src/midgard/CMakeLists.txt @@ -20,7 +20,10 @@ valhalla_module(NAME midgard PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> + SYSTEM_INCLUDE_DIRECTORIES + PUBLIC + $<$:${dirent_include_dir}> + PRIVATE + ${robinhoodhashing_include_dir} DEPENDS - Boost::boost - robin_hood::robin_hood) + Boost::boost) diff --git a/src/midgard/aabb2.cc b/src/midgard/aabb2.cc index 2890963ba9..cccf0706a4 100644 --- a/src/midgard/aabb2.cc +++ b/src/midgard/aabb2.cc @@ -5,7 +5,6 @@ #include "midgard/util.h" #include -#include namespace { diff --git a/src/midgard/logging.cc b/src/midgard/logging.cc index 35f9cffe23..caa90292e8 100644 --- a/src/midgard/logging.cc +++ b/src/midgard/logging.cc @@ -1,13 +1,13 @@ #include "midgard/logging.h" #include "filesystem.h" +#include #include #include #include #include #include #include -#include #include #ifdef __ANDROID__ @@ -39,8 +39,11 @@ std::string TimeStamp() { (tp - std::chrono::system_clock::from_time_t(tt)) + std::chrono::seconds(gmt.tm_sec); // format the string std::string buffer("year/mo/dy hr:mn:sc.xxxxxx0"); - snprintf(&buffer.front(), buffer.length(), "%04d/%02d/%02d %02d:%02d:%09.6f", gmt.tm_year + 1900, - gmt.tm_mon + 1, gmt.tm_mday, gmt.tm_hour, gmt.tm_min, fractional_seconds.count()); + [[maybe_unused]] int ret = + snprintf(&buffer.front(), buffer.length(), "%04d/%02d/%02d %02d:%02d:%09.6f", + gmt.tm_year + 1900, gmt.tm_mon + 1, gmt.tm_mday, gmt.tm_hour, gmt.tm_min, + fractional_seconds.count()); + assert(ret == static_cast(buffer.length()) - 1); // Remove trailing null terminator added by snprintf. buffer.pop_back(); diff --git a/src/midgard/point_tile_index.cc b/src/midgard/point_tile_index.cc index 29234c7a99..b76131eb35 100644 --- a/src/midgard/point_tile_index.cc +++ b/src/midgard/point_tile_index.cc @@ -1,5 +1,7 @@ #include "midgard/point_tile_index.h" +#include + namespace valhalla { namespace midgard { diff --git a/src/midgard/polyline2.cc b/src/midgard/polyline2.cc index 2f55da5977..7e28cd4220 100644 --- a/src/midgard/polyline2.cc +++ b/src/midgard/polyline2.cc @@ -254,7 +254,7 @@ void DouglasPeucker(container_t& polyline, size_t j = e - 1, k = 0; coord_t tmp; for (auto i = std::prev(end); i != start; --i, --j) { - // special points we dont want to generalize no matter what take precidence + // special points we dont want to generalize no matter what take precedence if (exclusions.find(j) != exclusions.end()) { itr = i; dmax = epsilon; @@ -314,6 +314,31 @@ void Polyline2::Generalize(container_t& polyline, DouglasPeucker(polyline, epsilon_m, exclusions); } +template +template +typename container_t::value_type::first_type +Polyline2::HausdorffDistance(const container_t& l1, const container_t& l2) { + typename container_t::value_type::first_type hausdorff = 0; + + // which point of l1 is furthest away from l2 + for (const auto& p : l1) { + auto closest = p.ClosestPoint(l2); + auto min_distance = p.Distance(std::get<0>(closest)); + if (min_distance > hausdorff) + hausdorff = min_distance; + } + + // which point of l2 is furthest away from l1 + for (const auto& p : l2) { + auto closest = p.ClosestPoint(l1); + auto min_distance = p.Distance(std::get<0>(closest)); + if (min_distance > hausdorff) + hausdorff = min_distance; + } + + return hausdorff; +} + // Explicit instantiation template class Polyline2>; template class Polyline2>; @@ -362,5 +387,8 @@ template void Polyline2>::Generalize(std::list const std::unordered_set&, bool); +template double Polyline2>::HausdorffDistance(const std::vector>&, + const std::vector>&); + } // namespace midgard } // namespace valhalla diff --git a/src/midgard/tiles.cc b/src/midgard/tiles.cc index b547d868a5..5ad031eae4 100644 --- a/src/midgard/tiles.cc +++ b/src/midgard/tiles.cc @@ -418,7 +418,7 @@ Tiles::Intersect(const container_t& linestring) const { if (ds == 0) { set_pixel(ix0, iy0); } - // if not the next most likley thing is adjacent cells + // if not the next most likely thing is adjacent cells else if (ds == 1) { set_pixel(ix0, iy0); set_pixel(ix1, iy1); diff --git a/src/midgard/util.cc b/src/midgard/util.cc index 8e4f3486d8..182046c3ac 100644 --- a/src/midgard/util.cc +++ b/src/midgard/util.cc @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -47,6 +47,70 @@ resample_at_1hz(const std::vector& segments) { return resampled; } +/** + * determines the quadrant of pt1 relative to pt2 + * + * +-----+-----+ + * | | | + * | 1 | 0 | + * | | | + * +----pt2----+ + * | | | + * | 2 | 3 | + * | | | + * +-----+-----+ + * + */ +template int8_t quadrant_type(const coord_t& pt1, const coord_t& pt2) { + return (pt1.first > pt2.first) ? ((pt1.second > pt2.second) ? 0 : 3) + : ((pt1.second > pt2.second) ? 1 : 2); +} +template int8_t quadrant_type(const valhalla::midgard::PointLL&, + const valhalla::midgard::PointLL&); + +/** + * get the x intercept of an edge {pt1, pt2} with a horizontal line at a given y + */ +template +typename coord_t::first_type +x_intercept(const coord_t& pt1, const coord_t& pt2, const typename coord_t::first_type& y) { + return pt2.first - ((pt2.second - y) * ((pt1.first - pt2.first) / (pt1.second - pt2.second))); +} + +template valhalla::midgard::PointLL::first_type +x_intercept(const valhalla::midgard::PointLL&, + const valhalla::midgard::PointLL&, + const valhalla::midgard::PointLL::first_type&); + +template +void adjust_delta(int8_t& delta, + const coord_t& vertex, + const coord_t& next_vertex, + const coord_t& p) { + switch (delta) { + /* make quadrant deltas wrap around */ + case 3: + delta = -1; + break; + case -3: + delta = 1; + break; + /* when a quadrant was skipped, check if clockwise or counter-clockwise */ + case 2: + case -2: + if (x_intercept(vertex, next_vertex, p.second) > p.first) + delta = -(delta); + break; + } +} + +template void adjust_delta(int8_t&, + const valhalla::midgard::PointLL&, + const valhalla::midgard::PointLL&, + const valhalla::midgard::PointLL& + +); + } // namespace namespace valhalla { @@ -325,7 +389,7 @@ resample_spherical_polyline>(const std::list&, double, /* Resample a polyline at uniform intervals using more accurate spherical interpolation between * points. The length and number of samples is specified. The interval is computed based on - * the number of samples and the algorithm guarantees that the secified number of samples + * the number of samples and the algorithm guarantees that the specified number of samples * is exactly produced. * This method makes use of several computations explained and demonstrated at: * http://williams.best.vwh.net/avform.htm (reference no longer active) @@ -545,6 +609,33 @@ template bool intersect(const PointLL& u, template bool intersect(const Point2& u, const Point2& v, const Point2& a, const Point2& b, Point2& i); +template +bool point_in_poly(const coord_t& pt, const container_t& poly) { + int8_t quad, next_quad, delta, angle; + quad = quadrant_type(poly.front(), pt); + angle = 0; + + auto it = poly.begin(); + for (size_t i = 0; i < poly.size(); ++i) { + const coord_t vertex = *it; + it++; + if (it == poly.end()) { + it = poly.begin(); + } + const coord_t& next_vertex = *it; + next_quad = quadrant_type(next_vertex, pt); + delta = next_quad - quad; + adjust_delta(delta, vertex, next_vertex, pt); + angle = angle + delta; + quad = next_quad; + } + return (angle == 4) || (angle == -4); +}; + +template bool point_in_poly>( + const valhalla::midgard::PointLL&, + const std::list&); + template typename container_t::value_type::first_type polygon_area(const container_t& polygon) { // Shoelace formula diff --git a/src/mjolnir/CMakeLists.txt b/src/mjolnir/CMakeLists.txt index 05c79550b9..a3e00f5f1f 100644 --- a/src/mjolnir/CMakeLists.txt +++ b/src/mjolnir/CMakeLists.txt @@ -1,6 +1,6 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/admin_lua_proc.h - COMMAND ${CMAKE_COMMAND} -P ${VALHALLA_SOURCE_DIR}/cmake/Binary2Header.cmake + COMMAND ${CMAKE_COMMAND} -P ${VALHALLA_SOURCE_DIR}/cmake/ValhallaBin2Header.cmake ${VALHALLA_SOURCE_DIR}/lua/admin.lua ${CMAKE_CURRENT_BINARY_DIR}/admin_lua_proc.h --variable-name lua_admin_lua @@ -10,7 +10,7 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/admin_lua_proc.h VERBATIM) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/graph_lua_proc.h - COMMAND ${CMAKE_COMMAND} -P ${VALHALLA_SOURCE_DIR}/cmake/Binary2Header.cmake + COMMAND ${CMAKE_COMMAND} -P ${VALHALLA_SOURCE_DIR}/cmake/ValhallaBin2Header.cmake ${VALHALLA_SOURCE_DIR}/lua/graph.lua ${CMAKE_CURRENT_BINARY_DIR}/graph_lua_proc.h --variable-name lua_graph_lua @@ -42,6 +42,7 @@ set(sources graphvalidator.cc hierarchybuilder.cc ingest_transit.cc + landmarks.cc linkclassification.cc luatagtransform.cc node_expander.cc @@ -61,8 +62,11 @@ set(sources util.cc validatetransit.cc) -# treat date library as system -set(system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include) +set(system_includes + ${date_include_dir} + ${rapidjson_include_dir} + $<$:${dirent_include_dir}> + ${PROTOBUF_INCLUDE_DIR}) if(APPLE) list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) endif() @@ -74,23 +78,21 @@ valhalla_module(NAME mjolnir PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> - ${PROTOBUF_INCLUDE_DIR} PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/valhalla - ${VALHALLA_SOURCE_DIR}/third_party/just_gtfs/include SYSTEM_INCLUDE_DIRECTORIES PUBLIC ${system_includes} + PRIVATE + ${VALHALLA_SOURCE_DIR}/third_party/just_gtfs/include + ${robinhoodhashing_include_dir} DEPENDS valhalla::proto valhalla::baldr - SpatiaLite::SpatiaLite + PkgConfig::SpatiaLite SQLite3::SQLite3 Boost::boost - Lua::Lua + PkgConfig::LuaJIT Threads::Threads - ZLIB::ZLIB - robin_hood::robin_hood) + PkgConfig::ZLIB) diff --git a/src/mjolnir/admin.cc b/src/mjolnir/admin.cc index 5b80dc6ae4..9aca8ca78e 100644 --- a/src/mjolnir/admin.cc +++ b/src/mjolnir/admin.cc @@ -24,6 +24,7 @@ sqlite3* GetDBHandle(const std::string& database) { sqlite3_close(db_handle); return nullptr; } + sqlite3_extended_result_codes(db_handle, 1); } return db_handle; } @@ -36,7 +37,7 @@ uint32_t GetMultiPolyId(const std::multimap& polys uint32_t index = 0; point_type p(ll.lng(), ll.lat()); for (const auto& poly : polys) { - if (boost::geometry::covered_by(p, poly.second)) { + if (bg::covered_by(p, poly.second, bg::strategy::within::crossings_multiply())) { const auto& admin = graphtile.admins_builder(poly.first); if (!admin.state_offset()) index = poly.first; @@ -53,12 +54,57 @@ uint32_t GetMultiPolyId(const std::multimap& polys uint32_t index = 0; point_type p(ll.lng(), ll.lat()); for (const auto& poly : polys) { - if (boost::geometry::covered_by(p, poly.second)) + // TODO: we recently discovered that boost::geometry doesn't do bbox checks to speed things up + if (bg::covered_by(p, poly.second, bg::strategy::within::crossings_multiply())) { return poly.first; + } } return index; } +// This function returns a vector pairs. The pair is a string and boolean {language, +// is_default_language}. The function takes a LL and checks if it is covered by a linguistic, +// state/providence, and country polygon. If the LL is covered by the polygon, then the language is +// added to the vector and the flag is set for whether this is a default or supported language. The +// default language is the default_language key that is set by users for the administrative relations. +// Supported are values we can add under in the adminconstants.h. This vector of pairs is our +// languages that will be considered for any name* or destination* keys. Basically, we only support +// the languages that are on the signs in that area. Note: The first pair always contains an empty +// language which makes the name key with no language the most important key. +std::vector> GetMultiPolyIndexes(const language_poly_index& polys, + const PointLL& ll) { + std::vector> languages; + + // first entry is blank for the default name + languages.emplace_back("", false); + + point_type p(ll.lng(), ll.lat()); + + for (auto it = polys.qbegin(bg::index::covers(p)); it != polys.qend(); ++it) { + // Make sure the point is in the admin poly (not just its envelope) + if (bg::covered_by(p, std::get<1>(*it), bg::strategy::within::crossings_multiply())) { + auto& langs = std::get<2>(*it); + bool is_default = std::get<3>(*it); + + for (const auto& l : langs) { + if (stringLanguage(l) != Language::kNone) { + auto needle = + std::find_if(languages.begin(), languages.end(), + [&l](const std::pair& p) { return p.first == l; }); + + if (needle == languages.end()) { + languages.emplace_back(l, is_default); + } else if (is_default) { // fr - nl or fr;en in default lang column + needle->second = false; + } + } + } + } + } + + return languages; +} + // Get the timezone polys from the db std::multimap GetTimeZones(sqlite3* db_handle, const AABB2& aabb) { @@ -71,7 +117,7 @@ std::multimap GetTimeZones(sqlite3* db_handle, uint32_t ret; uint32_t result = 0; - std::string sql = "select TZID, st_astext(geom) from tz_world where "; + std::string sql = "select TZID, st_astext(geom) as geom_text from tz_world where "; sql += "ST_Intersects(geom, BuildMBR(" + std::to_string(aabb.minx()) + ","; sql += std::to_string(aabb.miny()) + ", " + std::to_string(aabb.maxx()) + ","; sql += std::to_string(aabb.maxy()) + ")) "; @@ -98,8 +144,8 @@ std::multimap GetTimeZones(sqlite3* db_handle, uint32_t idx = DateTime::get_tz_db().to_index(tz_id); if (idx == 0) { - result = sqlite3_step(stmt); - continue; + sqlite3_finalize(stmt); + throw std::runtime_error("Can't find timezone ID " + std::string(tz_id)); } multi_polygon_type multi_poly; @@ -115,13 +161,27 @@ std::multimap GetTimeZones(sqlite3* db_handle, return polys; } +/*** + * Parses a language tag into a vector of individual tokens. + */ +std::vector ParseLanguageTokens(const std::string& lang_tag) { + auto langs = GetTagTokens(lang_tag, " - "); + if (langs.size() == 1) { + langs = GetTagTokens(langs.at(0)); + } + + return langs; +} + void GetData(sqlite3* db_handle, sqlite3_stmt* stmt, const std::string& sql, GraphTileBuilder& tilebuilder, std::multimap& polys, std::unordered_map& drive_on_right, - std::unordered_map& allow_intersection_names) { + std::unordered_map& allow_intersection_names, + language_poly_index& language_ploys, + bool languages_only = false) { uint32_t result = 0; bool dor = true; bool intersection_name = false; @@ -139,45 +199,99 @@ void GetData(sqlite3* db_handle, while (result == SQLITE_ROW) { - std::string country_name, state_name, country_iso, state_iso; + if (!languages_only) { - if (sqlite3_column_type(stmt, 0) == SQLITE_TEXT) { - country_name = (char*)sqlite3_column_text(stmt, 0); - } + std::string country_name, state_name, country_iso, state_iso, supported_languages, + default_language; - if (sqlite3_column_type(stmt, 1) == SQLITE_TEXT) { - state_name = (char*)sqlite3_column_text(stmt, 1); - } + if (sqlite3_column_type(stmt, 0) == SQLITE_TEXT) { + country_name = (char*)sqlite3_column_text(stmt, 0); + } - if (sqlite3_column_type(stmt, 2) == SQLITE_TEXT) { - country_iso = (char*)sqlite3_column_text(stmt, 2); - } + if (sqlite3_column_type(stmt, 1) == SQLITE_TEXT) { + state_name = (char*)sqlite3_column_text(stmt, 1); + } - if (sqlite3_column_type(stmt, 3) == SQLITE_TEXT) { - state_iso = (char*)sqlite3_column_text(stmt, 3); - } + if (sqlite3_column_type(stmt, 2) == SQLITE_TEXT) { + country_iso = (char*)sqlite3_column_text(stmt, 2); + } - dor = true; - if (sqlite3_column_type(stmt, 4) == SQLITE_INTEGER) { - dor = sqlite3_column_int(stmt, 4); - } + if (sqlite3_column_type(stmt, 3) == SQLITE_TEXT) { + state_iso = (char*)sqlite3_column_text(stmt, 3); + } - intersection_name = false; - if (sqlite3_column_type(stmt, 5) == SQLITE_INTEGER) { - intersection_name = sqlite3_column_int(stmt, 5); - } + dor = true; + if (sqlite3_column_type(stmt, 4) == SQLITE_INTEGER) { + dor = sqlite3_column_int(stmt, 4); + } - std::string geom; - if (sqlite3_column_type(stmt, 6) == SQLITE_TEXT) { - geom = (char*)sqlite3_column_text(stmt, 6); - } + intersection_name = false; + if (sqlite3_column_type(stmt, 5) == SQLITE_INTEGER) { + intersection_name = sqlite3_column_int(stmt, 5); + } - uint32_t index = tilebuilder.AddAdmin(country_name, state_name, country_iso, state_iso); - multi_polygon_type multi_poly; - boost::geometry::read_wkt(geom, multi_poly); - polys.emplace(index, multi_poly); - drive_on_right.emplace(index, dor); - allow_intersection_names.emplace(index, intersection_name); + if (sqlite3_column_type(stmt, 7) == SQLITE_TEXT) { + supported_languages = (char*)sqlite3_column_text(stmt, 7); + } + + if (sqlite3_column_type(stmt, 8) == SQLITE_TEXT) { + default_language = (char*)sqlite3_column_text(stmt, 8); + } + + std::string geom; + if (sqlite3_column_type(stmt, 9) == SQLITE_TEXT) { + geom = (char*)sqlite3_column_text(stmt, 9); + } + + uint32_t index = tilebuilder.AddAdmin(country_name, state_name, country_iso, state_iso); + multi_polygon_type multi_poly; + bg::read_wkt(geom, multi_poly); + polys.emplace(index, multi_poly); + drive_on_right.emplace(index, dor); + allow_intersection_names.emplace(index, intersection_name); + + bg::model::box box{}; + bg::envelope(multi_poly, box); + if (!default_language.empty()) { + auto langs = ParseLanguageTokens(default_language); + language_ploys.insert(std::make_tuple(box, multi_poly, langs, true)); + } + if (!supported_languages.empty()) { + auto langs = ParseLanguageTokens(supported_languages); + language_ploys.insert(std::make_tuple(box, multi_poly, langs, false)); + } + + } else { + + std::string supported_languages; + if (sqlite3_column_type(stmt, 1) == SQLITE_TEXT) { + supported_languages = (char*)sqlite3_column_text(stmt, 1); + } + + std::string default_language; + if (sqlite3_column_type(stmt, 2) == SQLITE_TEXT) { + default_language = (char*)sqlite3_column_text(stmt, 2); + } + + std::string geom; + if (sqlite3_column_type(stmt, 3) == SQLITE_TEXT) { + geom = (char*)sqlite3_column_text(stmt, 3); + } + + multi_polygon_type multi_poly; + bg::read_wkt(geom, multi_poly); + bg::model::box box{}; + bg::envelope(multi_poly, box); + + if (!default_language.empty()) { + auto langs = ParseLanguageTokens(default_language); + language_ploys.insert(std::make_tuple(box, multi_poly, langs, true)); + } + if (!supported_languages.empty()) { + auto langs = ParseLanguageTokens(supported_languages); + language_ploys.insert(std::make_tuple(box, multi_poly, langs, false)); + } + } result = sqlite3_step(stmt); } @@ -193,6 +307,7 @@ std::multimap GetAdminInfo(sqlite3* db_handle, std::unordered_map& drive_on_right, std::unordered_map& allow_intersection_names, + language_poly_index& language_polys, const AABB2& aabb, GraphTileBuilder& tilebuilder) { std::multimap polys; @@ -201,11 +316,27 @@ GetAdminInfo(sqlite3* db_handle, } sqlite3_stmt* stmt = 0; + + // default language query + std::string sql = + "SELECT admin_level, supported_languages, default_language, st_astext(geom) from "; + sql += + " admins where (supported_languages is NOT NULL or default_language is NOT NULL) and ST_Intersects(geom, BuildMBR(" + + std::to_string(aabb.minx()) + ","; + sql += std::to_string(aabb.miny()) + ", " + std::to_string(aabb.maxx()) + ","; + sql += std::to_string(aabb.maxy()) + ")) and admin_level>4 "; + sql += "and rowid IN (SELECT rowid FROM SpatialIndex WHERE f_table_name = "; + sql += "'admins' AND search_frame = BuildMBR(" + std::to_string(aabb.minx()) + ","; + sql += std::to_string(aabb.miny()) + ", " + std::to_string(aabb.maxx()) + ","; + sql += std::to_string(aabb.maxy()) + ")) order by admin_level desc, name;"; + GetData(db_handle, stmt, sql, tilebuilder, polys, drive_on_right, allow_intersection_names, + language_polys, true); + // state query - std::string sql = "SELECT country.name, state.name, country.iso_code, "; + sql = "SELECT country.name, state.name, country.iso_code, "; + sql += "state.iso_code, state.drive_on_right, state.allow_intersection_names, state.admin_level, "; sql += - "state.iso_code, state.drive_on_right, state.allow_intersection_names, st_astext(state.geom) "; - sql += "from admins state, admins country where "; + "state.supported_languages, state.default_language, st_astext(state.geom) from admins state, admins country where "; sql += "ST_Intersects(state.geom, BuildMBR(" + std::to_string(aabb.minx()) + ","; sql += std::to_string(aabb.miny()) + ", " + std::to_string(aabb.maxx()) + ","; sql += std::to_string(aabb.maxy()) + ")) and "; @@ -213,20 +344,23 @@ GetAdminInfo(sqlite3* db_handle, sql += "and state.rowid IN (SELECT rowid FROM SpatialIndex WHERE f_table_name = "; sql += "'admins' AND search_frame = BuildMBR(" + std::to_string(aabb.minx()) + ","; sql += std::to_string(aabb.miny()) + ", " + std::to_string(aabb.maxx()) + ","; - sql += std::to_string(aabb.maxy()) + "));"; - GetData(db_handle, stmt, sql, tilebuilder, polys, drive_on_right, allow_intersection_names); + sql += std::to_string(aabb.maxy()) + ")) order by state.name, country.name;"; + GetData(db_handle, stmt, sql, tilebuilder, polys, drive_on_right, allow_intersection_names, + language_polys); // country query - sql = - "SELECT name, \"\", iso_code, \"\", drive_on_right, allow_intersection_names, st_astext(geom) from "; - sql += " admins where ST_Intersects(geom, BuildMBR(" + std::to_string(aabb.minx()) + ","; + sql = "SELECT name, \"\", iso_code, \"\", drive_on_right, allow_intersection_names, admin_level, "; + sql += + "supported_languages, default_language, st_astext(geom) from admins where ST_Intersects(geom, BuildMBR(" + + std::to_string(aabb.minx()) + ","; sql += std::to_string(aabb.miny()) + ", " + std::to_string(aabb.maxx()) + ","; sql += std::to_string(aabb.maxy()) + ")) and admin_level=2 "; sql += "and rowid IN (SELECT rowid FROM SpatialIndex WHERE f_table_name = "; sql += "'admins' AND search_frame = BuildMBR(" + std::to_string(aabb.minx()) + ","; sql += std::to_string(aabb.miny()) + ", " + std::to_string(aabb.maxx()) + ","; - sql += std::to_string(aabb.maxy()) + "));"; - GetData(db_handle, stmt, sql, tilebuilder, polys, drive_on_right, allow_intersection_names); + sql += std::to_string(aabb.maxy()) + ")) order by name;"; + GetData(db_handle, stmt, sql, tilebuilder, polys, drive_on_right, allow_intersection_names, + language_polys); if (stmt) { // just in case something bad happened. sqlite3_finalize(stmt); diff --git a/src/mjolnir/adminbuilder.cc b/src/mjolnir/adminbuilder.cc index ef1d0295c8..d602785776 100644 --- a/src/mjolnir/adminbuilder.cc +++ b/src/mjolnir/adminbuilder.cc @@ -142,8 +142,14 @@ void buffer_polygon(const polygon_t& polygon, multipolygon_t& multipolygon) { auto* outer_ring = geos_helper_t::from_striped_container(polygon.outer()); std::vector inner_rings; inner_rings.reserve(polygon.inners().size()); - for (const auto& inner : polygon.inners()) + + // annoying circleci apple clang bug, not reproducible on any other machine.. + // https://github.com/valhalla/valhalla/pull/4500/files#r1445039739 + auto unused_size = std::to_string(polygon.inners().size()); + + for (const auto& inner : polygon.inners()) { inner_rings.push_back(geos_helper_t::from_striped_container(inner)); + } auto* geos_poly = GEOSGeom_createPolygon(outer_ring, &inner_rings.front(), inner_rings.size()); auto* buffered = GEOSBuffer(geos_poly, 0, 8); GEOSNormalize(buffered); @@ -163,7 +169,8 @@ void buffer_polygon(const polygon_t& polygon, multipolygon_t& multipolygon) { break; } default: - throw std::runtime_error("Unusable geometry type after buffering"); + throw std::runtime_error("Unusable geometry type after buffering with inners size " + + unused_size); } GEOSGeom_destroy(geos_poly); GEOSGeom_destroy(buffered); @@ -413,8 +420,10 @@ bool BuildAdminFromPBF(const boost::property_tree::ptree& pt, sql += "parent_admin INTEGER,"; sql += "name TEXT NOT NULL,"; sql += "name_en TEXT,"; - sql += "drive_on_right INTEGER NOT NULL,"; - sql += "allow_intersection_names INTEGER NOT NULL)"; + sql += "drive_on_right INTEGER NULL,"; + sql += "allow_intersection_names INTEGER NULL,"; + sql += "default_language TEXT,"; + sql += "supported_languages TEXT)"; ret = sqlite3_exec(db_handle, sql.c_str(), NULL, NULL, &err_msg); if (ret != SQLITE_OK) { @@ -491,7 +500,8 @@ bool BuildAdminFromPBF(const boost::property_tree::ptree& pt, * this time too we'll use a Prepared Statement */ sql = "INSERT INTO admins (admin_level, iso_code, parent_admin, name, name_en, "; - sql += "drive_on_right, allow_intersection_names, geom) VALUES (?, ?, ?, ?, ?, ? ,?, "; + sql += + "drive_on_right, allow_intersection_names, default_language, supported_languages, geom) VALUES (?,?,?,?,?,?,?,?,?,"; sql += "CastToMulti(GeomFromText(?, 4326)))"; ret = sqlite3_prepare_v2(db_handle, sql.c_str(), strlen(sql.c_str()), &stmt, NULL); @@ -567,7 +577,7 @@ bool BuildAdminFromPBF(const boost::property_tree::ptree& pt, sqlite3_bind_text(stmt, 4, admin_info.first.c_str(), admin_info.first.length(), SQLITE_STATIC); - std::string name_en; + std::string name_en, default_language; if (admin.name_en_index) { name_en = admin_data.name_offset_map.name(admin.name_en_index); sqlite3_bind_text(stmt, 5, name_en.c_str(), name_en.length(), SQLITE_STATIC); @@ -575,10 +585,26 @@ bool BuildAdminFromPBF(const boost::property_tree::ptree& pt, sqlite3_bind_null(stmt, 5); } - sqlite3_bind_int(stmt, 6, admin.drive_on_right); - sqlite3_bind_int(stmt, 7, admin.allow_intersection_names); - sqlite3_bind_text(stmt, 8, wkt.c_str(), wkt.length(), SQLITE_STATIC); + uint32_t level = admin.admin_level; + if (level == 2 || level == 4) + sqlite3_bind_int(stmt, 6, admin.drive_on_right); + else + sqlite3_bind_null(stmt, 6); + + if (level == 2 || level == 4) + sqlite3_bind_int(stmt, 7, admin.allow_intersection_names); + else + sqlite3_bind_null(stmt, 7); + if (admin.default_language_index) { + default_language = admin_data.name_offset_map.name(admin.default_language_index); + sqlite3_bind_text(stmt, 8, default_language.c_str(), default_language.length(), SQLITE_STATIC); + } else { + sqlite3_bind_null(stmt, 8); + } + + sqlite3_bind_null(stmt, 9); + sqlite3_bind_text(stmt, 10, wkt.c_str(), wkt.length(), SQLITE_STATIC); /* performing INSERT INTO */ ret = sqlite3_step(stmt); if (ret == SQLITE_DONE || ret == SQLITE_ROW) { @@ -591,6 +617,8 @@ bool BuildAdminFromPBF(const boost::property_tree::ptree& pt, LOG_ERROR("sqlite3_step() Drive on Right: " + std::to_string(admin.drive_on_right)); LOG_ERROR("sqlite3_step() Allow Intersection Names: " + std::to_string(admin.allow_intersection_names)); + LOG_ERROR("sqlite3_step() Default Language: " + + admin_data.name_offset_map.name(admin.default_language_index)); } sqlite3_finalize(stmt); @@ -684,7 +712,54 @@ bool BuildAdminFromPBF(const boost::property_tree::ptree& pt, sqlite3_close(db_handle); return false; } - LOG_INFO("Done updating Parent admin"); + LOG_INFO("Done updating parent admin"); + + sql = "update admins set supported_languages = ? "; + sql += "where (name = ? or name_en = ?) and admin_level = ? "; + + ret = sqlite3_prepare_v2(db_handle, sql.c_str(), strlen(sql.c_str()), &stmt, NULL); + if (ret != SQLITE_OK) { + LOG_ERROR("SQL error: " + sql); + LOG_ERROR(std::string(sqlite3_errmsg(db_handle))); + } + ret = sqlite3_exec(db_handle, "BEGIN", NULL, NULL, &err_msg); + if (ret != SQLITE_OK) { + LOG_ERROR("Error: " + std::string(err_msg)); + sqlite3_free(err_msg); + sqlite3_close(db_handle); + return false; + } + + for (const auto& languages : kSupportedLanguages) { + + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + sqlite3_bind_text(stmt, 1, languages.second.second.c_str(), languages.second.second.length(), + SQLITE_STATIC); + sqlite3_bind_text(stmt, 2, languages.first.c_str(), languages.first.length(), SQLITE_STATIC); + sqlite3_bind_text(stmt, 3, languages.first.c_str(), languages.first.length(), SQLITE_STATIC); + sqlite3_bind_int(stmt, 4, (int)languages.second.first); + + /* performing update */ + ret = sqlite3_step(stmt); + if (ret == SQLITE_DONE || ret == SQLITE_ROW) { + continue; + } + LOG_ERROR("Supported Languages: sqlite3_step() error: " + std::string(sqlite3_errmsg(db_handle)) + + ". Ignore if not using a planet extract or check if there was a name change for " + + languages.first.c_str()); + } + + sqlite3_finalize(stmt); + ret = sqlite3_exec(db_handle, "COMMIT", NULL, NULL, &err_msg); + if (ret != SQLITE_OK) { + LOG_ERROR("Error: " + std::string(err_msg)); + sqlite3_free(err_msg); + sqlite3_close(db_handle); + return false; + } + + LOG_INFO("Done updating supported languages"); sql = "INSERT into admin_access (admin_id, iso_code, trunk, trunk_link, track, footway, "; sql += "pedestrian, bridleway, cycleway, path, motorroad) VALUES ("; diff --git a/src/mjolnir/bssbuilder.cc b/src/mjolnir/bssbuilder.cc index 352971702e..444b7deacf 100644 --- a/src/mjolnir/bssbuilder.cc +++ b/src/mjolnir/bssbuilder.cc @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -52,7 +51,7 @@ struct BSSConnection { uint64_t wayid = std::numeric_limits::max(); std::vector names = {}; std::vector tagged_values = {}; - std::vector pronunciations = {}; + std::vector linguistics = {}; std::vector shape = {}; // Is the outbound edge from the waynode is forward? @@ -82,8 +81,8 @@ struct BSSConnection { wayid = edgeinfo.wayid(); names = edgeinfo.GetNames(); tagged_values = edgeinfo.GetTaggedValues(); - pronunciations = edgeinfo.GetTaggedValues(true); + linguistics = edgeinfo.GetLinguisticTaggedValues(); is_forward_from_waynode = is_forward; speed = best.directededge->speed(); surface = best.directededge->surface(); @@ -135,16 +134,16 @@ void compute_and_fill_shape(const BestProjection& best, BSSConnection& start, BSSConnection& end) { const auto& closest_point = std::get<0>(best.closest); - auto cloest_index = std::get<2>(best.closest); + auto closest_index = std::get<2>(best.closest); - std::copy(best.shape.begin(), best.shape.begin() + cloest_index + 1, + std::copy(best.shape.begin(), best.shape.begin() + closest_index + 1, std::back_inserter(start.shape)); start.shape.push_back(closest_point); start.shape.push_back(bss_ll); end.shape.push_back(bss_ll); end.shape.push_back(closest_point); - std::copy(best.shape.begin() + cloest_index + 1, best.shape.end(), std::back_inserter(end.shape)); + std::copy(best.shape.begin() + closest_index + 1, best.shape.end(), std::back_inserter(end.shape)); } const static auto VALID_EDGE_USES = std::unordered_set{ @@ -157,7 +156,8 @@ std::vector project(const GraphTile& local_tile, const std::vecto auto t1 = std::chrono::high_resolution_clock::now(); auto scoped_finally = make_finally([&t1, size = osm_bss.size()]() { auto t2 = std::chrono::high_resolution_clock::now(); - uint32_t secs = std::chrono::duration_cast(t2 - t1).count(); + [[maybe_unused]] uint32_t secs = + std::chrono::duration_cast(t2 - t1).count(); LOG_INFO("Projection Finished - Projection of " + std::to_string(size) + " bike station took " + std::to_string(secs) + " secs"); }); @@ -330,7 +330,7 @@ void add_bss_nodes_and_edges(GraphTileBuilder& tilebuilder_local, new_bss_node_graphid, bss_to_waynode.way_node_id, bss_to_waynode.wayid, 0, 0, 0, bss_to_waynode.shape, bss_to_waynode.names, bss_to_waynode.tagged_values, - bss_to_waynode.pronunciations, 0, added); + bss_to_waynode.linguistics, 0, added); directededge.set_edgeinfo_offset(edge_info_offset); tilebuilder_local.directededges().emplace_back(std::move(directededge)); @@ -469,7 +469,7 @@ void create_edges(GraphTileBuilder& tilebuilder_local, uint32_t edge_info_offset = tilebuilder_local.AddEdgeInfo(tilebuilder_local.directededges().size(), lower->way_node_id, lower->bss_node_id, lower->wayid, 0, 0, 0, lower->shape, - lower->names, lower->tagged_values, lower->pronunciations, 0, + lower->names, lower->tagged_values, lower->linguistics, 0, added); directededge.set_edgeinfo_offset(edge_info_offset); @@ -516,7 +516,7 @@ namespace valhalla { namespace mjolnir { // Add bss to the graph -/* The import of bike share staion(BSS) into the tiles is done in two steps with some hypothesis in +/* The import of bike share station(BSS) into the tiles is done in two steps with some hypothesis in * order to simply the problem. * * We assume that the BSS node and the startnode of projected edge(either the forward edge or its evil @@ -570,7 +570,7 @@ namespace mjolnir { * 2. Now the Bss nodes and their outbound edges are added into the local tiles, it's time to add * their inbound edges(in other words, outbound edges of startnodes and endnodes). These edges are * just considered as the same outbound edges from a way node (outbound edges of either startnode or - * endnode are technically the same). We group those edges whose orign are in the same tiles and work + * endnode are technically the same). We group those edges whose origin are in the same tiles and work * on it in batch. * * @@ -589,7 +589,8 @@ void BssBuilder::Build(const boost::property_tree::ptree& pt, auto scoped_finally = make_finally([&t1]() { auto t2 = std::chrono::high_resolution_clock::now(); - uint32_t secs = std::chrono::duration_cast(t2 - t1).count(); + [[maybe_unused]] uint32_t secs = + std::chrono::duration_cast(t2 - t1).count(); LOG_INFO("Finished - BssBuilder took " + std::to_string(secs) + " secs"); }); diff --git a/src/mjolnir/complexrestrictionbuilder.cc b/src/mjolnir/complexrestrictionbuilder.cc index e6f1439547..fe2ce5c403 100644 --- a/src/mjolnir/complexrestrictionbuilder.cc +++ b/src/mjolnir/complexrestrictionbuilder.cc @@ -1,5 +1,4 @@ #include -#include #include #include "baldr/complexrestriction.h" diff --git a/src/mjolnir/convert_transit.cc b/src/mjolnir/convert_transit.cc index 511c63760a..6cbfb4a6cb 100644 --- a/src/mjolnir/convert_transit.cc +++ b/src/mjolnir/convert_transit.cc @@ -1,19 +1,13 @@ #include #include -#include #include -#include #include -#include -#include -#include #include #include #include #include "baldr/rapidjson_utils.h" #include -#include #include #include @@ -690,13 +684,13 @@ void AddToGraph(GraphTileBuilder& tilebuilder_transit, // Add edge info to the tile and set the offset in the directed edge bool added = false; - std::vector names, tagged_values, pronunciations; + std::vector names, tagged_values, linguistics; std::list shape = {egress_ll, station_ll}; uint32_t edge_info_offset = tilebuilder_transit.AddEdgeInfo(0, egress_graphid, station_graphid, 0, 0, 0, 0, shape, - names, tagged_values, pronunciations, 0, added); + names, tagged_values, linguistics, 0, added); directededge.set_edgeinfo_offset(edge_info_offset); directededge.set_forward(true); @@ -738,13 +732,13 @@ void AddToGraph(GraphTileBuilder& tilebuilder_transit, directededge.set_named(false); // Add edge info to the tile and set the offset in the directed edge bool added = false; - std::vector names, tagged_values, pronunciations; + std::vector names, tagged_values, linguistics; std::list shape = {station_ll, egress_ll}; // TODO - these need to be valhalla graph Ids uint32_t edge_info_offset = tilebuilder_transit.AddEdgeInfo(0, station_graphid, egress_graphid, 0, 0, 0, 0, shape, - names, tagged_values, pronunciations, 0, added); + names, tagged_values, linguistics, 0, added); directededge.set_edgeinfo_offset(edge_info_offset); directededge.set_forward(false); @@ -786,13 +780,13 @@ void AddToGraph(GraphTileBuilder& tilebuilder_transit, // Add edge info to the tile and set the offset in the directed edge bool added = false; - std::vector names, tagged_values, pronunciations; + std::vector names, tagged_values, linguistics; std::list shape = {station_ll, platform_ll}; // TODO - these need to be valhalla graph Ids uint32_t edge_info_offset = tilebuilder_transit.AddEdgeInfo(0, station_graphid, platform_graphid, 0, 0, 0, 0, shape, - names, tagged_values, pronunciations, 0, added); + names, tagged_values, linguistics, 0, added); directededge.set_edgeinfo_offset(edge_info_offset); directededge.set_forward(true); @@ -865,13 +859,13 @@ void AddToGraph(GraphTileBuilder& tilebuilder_transit, directededge.set_named(false); // Add edge info to the tile and set the offset in the directed edge bool added = false; - std::vector names, tagged_values, pronunciations; + std::vector names, tagged_values, linguistics; std::list shape = {platform_ll, station_ll}; // TODO - these need to be valhalla graph Ids uint32_t edge_info_offset = tilebuilder_transit.AddEdgeInfo(0, platform_graphid, station_graphid, 0, 0, 0, 0, shape, - names, tagged_values, pronunciations, 0, added); + names, tagged_values, linguistics, 0, added); directededge.set_edgeinfo_offset(edge_info_offset); directededge.set_forward(false); @@ -934,7 +928,7 @@ void AddToGraph(GraphTileBuilder& tilebuilder_transit, // Leave the name empty. Use the trip Id to look up the route Id and // route within TripLegBuilder. bool added = false; - std::vector names, tagged_values, pronunciations; + std::vector names, tagged_values, linguistics; std::vector points; std::vector distance; @@ -958,7 +952,7 @@ void AddToGraph(GraphTileBuilder& tilebuilder_transit, uint32_t edge_info_offset = tilebuilder_transit.AddEdgeInfo(transitedge.routeid, platform_graphid, end_platform_graphid, - 0, 0, 0, 0, shape, names, tagged_values, pronunciations, 0, + 0, 0, 0, 0, shape, names, tagged_values, linguistics, 0, added); directededge.set_edgeinfo_offset(edge_info_offset); @@ -985,7 +979,8 @@ void AddToGraph(GraphTileBuilder& tilebuilder_transit, // Log the number of added nodes and edges auto t2 = std::chrono::high_resolution_clock::now(); - uint32_t msecs = std::chrono::duration_cast(t2 - t1).count(); + [[maybe_unused]] uint32_t msecs = + std::chrono::duration_cast(t2 - t1).count(); LOG_INFO("Tile " + std::to_string(tileid.tileid()) + ": added " + std::to_string(transitedges) + " transit edges, and " + std::to_string(tilebuilder_transit.nodes().size()) + " nodes. time = " + std::to_string(msecs) + " ms"); @@ -1180,7 +1175,7 @@ void build_tiles(const boost::property_tree::ptree& pt, std::multimap tz_polys; if (tz_db_handle) { tz_polys = GetTimeZones(tz_db_handle, tile_bounds); - if (tz_polys.size() == 1) { + if (tz_polys.size() < 2) { tile_within_one_tz = true; } } @@ -1324,11 +1319,11 @@ std::unordered_set convert_transit(const ptree& pt) { } auto t2 = std::chrono::high_resolution_clock::now(); - uint32_t secs = std::chrono::duration_cast(t2 - t1).count(); + [[maybe_unused]] uint32_t secs = std::chrono::duration_cast(t2 - t1).count(); LOG_INFO("Finished building transit network - took " + std::to_string(secs) + " secs"); return all_tiles; } } // namespace mjolnir -} // namespace valhalla \ No newline at end of file +} // namespace valhalla diff --git a/src/mjolnir/directededgebuilder.cc b/src/mjolnir/directededgebuilder.cc index 4f6eb66b3a..7e554479ad 100644 --- a/src/mjolnir/directededgebuilder.cc +++ b/src/mjolnir/directededgebuilder.cc @@ -26,7 +26,7 @@ DirectedEdgeBuilder::DirectedEdgeBuilder(const OSMWay& way, const bool minor, const uint32_t restrictions, const uint32_t bike_network, - const bool reclass_ferry) + const bool remove_destonly) : DirectedEdge() { set_endnode(endnode); set_use(use); @@ -52,13 +52,13 @@ DirectedEdgeBuilder::DirectedEdgeBuilder(const OSMWay& way, set_truck_route(way.truck_route()); - // Set destination only to true if the reclass_ferry is set to false and either destination only or - // no thru traffic is set. Adding the reclass_ferry check allows us to know if we should override - // the destination only attribution - set_dest_only(!reclass_ferry && (way.destination_only() || way.no_thru_traffic())); - if (reclass_ferry && (way.destination_only() || way.no_thru_traffic())) { + // Set destination only to true if the remove_destonly is set to false and either destination only + // or no thru traffic is set. remove_destonly is set for reclassified paths due to ferries. + set_dest_only(!remove_destonly && (way.destination_only() || way.no_thru_traffic())); + if (remove_destonly && (way.destination_only() || way.no_thru_traffic())) { LOG_DEBUG("Overriding dest_only attribution to false for ferry."); } + set_dest_only_hgv(way.destination_only_hgv()); set_dismount(way.dismount()); set_use_sidepath(way.use_sidepath()); set_sac_scale(way.sac_scale()); diff --git a/src/mjolnir/edgeinfobuilder.cc b/src/mjolnir/edgeinfobuilder.cc index 5c55f86002..e37a4af4c1 100644 --- a/src/mjolnir/edgeinfobuilder.cc +++ b/src/mjolnir/edgeinfobuilder.cc @@ -1,5 +1,4 @@ #include -#include #include #include "baldr/edgeinfo.h" @@ -50,6 +49,11 @@ void EdgeInfoBuilder::set_speed_limit(const uint32_t speed_limit) { } } +// Sets the elevation flag. +void EdgeInfoBuilder::set_has_elevation(const bool elevation) { + ei_.has_elevation_ = elevation; +} + // Set the list of name info (offsets, etc.) used by this edge. void EdgeInfoBuilder::set_name_info_list(const std::vector& name_info_list) { if (name_info_list.size() > kMaxNamesPerEdge) { @@ -80,12 +84,20 @@ void EdgeInfoBuilder::set_encoded_shape(const std::string& encoded_shape) { std::copy(encoded_shape.begin(), encoded_shape.end(), back_inserter(encoded_shape_)); } +// Set the encoded elevation vector. +void EdgeInfoBuilder::set_encoded_elevation(const std::vector& encoded_elevation) { + if (!encoded_elevation.empty()) { + encoded_elevation_ = std::move(encoded_elevation); + } +} + // Get the size of the edge info (including name offsets and shape string) std::size_t EdgeInfoBuilder::BaseSizeOf() const { std::size_t size = sizeof(EdgeInfo::EdgeInfoInner); size += (name_info_list_.size() * sizeof(NameInfo)); size += (encoded_shape_.size() * sizeof(std::string::value_type)); size += ei_.extended_wayid_size_; + size += (encoded_elevation_.size() * sizeof(int8_t)); return size; } @@ -120,6 +132,9 @@ std::ostream& operator<<(std::ostream& os, const EdgeInfoBuilder& eib) { ei.encoded_shape_size_ = static_cast(eib.encoded_shape_.size()); } + // Set the has_elevation flag if encoded_elevation vector is not empty + ei.has_elevation_ = !eib.encoded_elevation_.empty(); + // Write out the bytes os.write(reinterpret_cast(&ei), sizeof(ei)); os.write(reinterpret_cast(eib.name_info_list_.data()), @@ -131,6 +146,10 @@ std::ostream& operator<<(std::ostream& os, const EdgeInfoBuilder& eib) { if (ei.extended_wayid_size_ > 1) { os.write(reinterpret_cast(&eib.extended_wayid3_), sizeof(eib.extended_wayid3_)); } + if (!eib.encoded_elevation_.empty()) { + os.write(reinterpret_cast(eib.encoded_elevation_.data()), + eib.encoded_elevation_.size()); + } // Pad to a 4 byte boundary std::size_t padding = (eib.BaseSizeOf() % 4); diff --git a/src/mjolnir/elevationbuilder.cc b/src/mjolnir/elevationbuilder.cc index 692bf382f2..176026d01b 100644 --- a/src/mjolnir/elevationbuilder.cc +++ b/src/mjolnir/elevationbuilder.cc @@ -1,14 +1,16 @@ #include "mjolnir/elevationbuilder.h" #include +#include #include #include -#include - #include "baldr/graphconstants.h" +#include "baldr/graphid.h" #include "baldr/graphreader.h" #include "filesystem.h" +#include "midgard/elevation_encoding.h" +#include "midgard/encoded.h" #include "midgard/logging.h" #include "midgard/pointll.h" #include "midgard/polyline2.h" @@ -33,6 +35,78 @@ constexpr double kMinimumInterval = 10.0f; using cache_t = std::unordered_map>; +/** + * Encode elevation along an edge to store in tiles. + */ +std::vector encode_edge_elevation(const std::unique_ptr& sample, + const std::vector& shape, + const uint32_t length, + uint32_t wayid) { + // Uniformly resample the polyline to create the desired number of vertices + uint32_t n = encoded_elevation_count(length) + 2; + std::vector resampled = + valhalla::midgard::uniform_resample_spherical_polyline(shape, length, n); + + // Get elevation (height) at each sampled point along the edge. + std::vector heights(resampled.size()); + heights = sample->get_all(resampled); + + // Encode the elevation. + bool error = false; + std::vector encoded = encode_elevation(heights, error); + if (error) { + double diff = 0; + for (size_t i = 1; i < heights.size(); i++) { + auto d = std::abs(heights[i] - heights[i - 1]); + diff = d < diff ? diff : d; + LOG_DEBUG(" " + std::to_string(heights[i])); + } + LOG_WARN("edge elevation wayid = " + std::to_string(wayid) + " exceeds difference with " + + std::to_string(diff) + " meters."); + } + return encoded; +} + +/** + * Encode elevation for a bridge, tunnel, ferry. + */ +std::vector encode_btf_elevation(const std::unique_ptr& sample, + const std::vector& shape, + const uint32_t length, + uint32_t wayid) { + // Compute a uniform sampling interval along the edge based on its length. + double interval = sampling_interval(length); + + // Sample at the first and last shape point + double h1 = sample->get(shape.front()); + double h2 = sample->get(shape.back()); + + // Use linear interpolation from h1 to h2 along the length of the edge + uint32_t n = static_cast(length / interval) + 1; + std::vector heights(n); + heights.front() = h1; + float delta = (h2 - h1) / n; + for (uint32_t i = 1; i < n - 1; ++i) { + heights[i] = heights[i - 1] + delta; + } + heights.back() = h2; + + // Encode the elevation. + bool error = false; + auto e = encode_elevation(heights, error); + if (error) { + double diff = 0; + for (size_t i = 1; i < heights.size(); i++) { + auto d = std::abs(heights[i] - heights[i - 1]); + diff = d < diff ? diff : d; + LOG_DEBUG(" " + std::to_string(heights[i])); + } + LOG_WARN("BTF edge elevation wayid = " + std::to_string(wayid) + " exceeds difference with " + + std::to_string(diff) + " meters."); + } + return e; +} + void add_elevations_to_single_tile(GraphReader& graphreader, std::mutex& graphreader_lck, cache_t& cache, @@ -45,16 +119,36 @@ void add_elevations_to_single_tile(GraphReader& graphreader, // retrieved/used? tilebuilder.header_builder().set_has_elevation(true); + // Iterate through the nodes edges, get the node lat,lng, sample and store elevation. + for (uint32_t i = 0; i < tilebuilder.header()->nodecount(); ++i) { + // Get a writeable reference to the node edge + NodeInfo& nodeinfo = tilebuilder.node_builder(i); + PointLL ll = nodeinfo.latlng(tilebuilder.header()->base_ll()); + nodeinfo.set_elevation(sample->get(ll)); + } + // Reserve twice the number of directed edges in the tile. We do not directly know // how many EdgeInfo records exist but it cannot be more than 2x the directed edge count. uint32_t count = tilebuilder.header()->directededgecount(); cache.clear(); cache.reserve(2 * count); - // Iterate through the directed edges + // Order the directed edges by edge_info_offset + std::multimap edge_info_offsets; for (uint32_t i = 0; i < count; ++i) { - // Get a writeable reference to the directed edge DirectedEdge& directededge = tilebuilder.directededge_builder(i); + uint32_t edge_info_offset = directededge.edgeinfo_offset(); + edge_info_offsets.insert(std::pair(edge_info_offset, i)); + } + + // Map existing edge info offsets to new (after adding encoded elevation) + std::unordered_map new_offsets; + + // Iterate through the directed edges + uint32_t ei_offset = 0; + for (auto& elem : edge_info_offsets) { + // Get a writeable reference to the directed edge + DirectedEdge& directededge = tilebuilder.directededge_builder(elem.second); // Get the edge info offset uint32_t edge_info_offset = directededge.edgeinfo_offset(); @@ -69,37 +163,47 @@ void add_elevations_to_single_tile(GraphReader& graphreader, // Grade estimation and max slopes std::tuple forward_grades(0.0, 0.0, 0.0, 0.0); std::tuple reverse_grades(0.0, 0.0, 0.0, 0.0); - if (!directededge.tunnel() && directededge.use() != Use::kFerry) { - // Evenly sample the shape. If it is really short or a bridge just do both ends - auto interval = POSTING_INTERVAL; - std::vector resampled; - if (length < POSTING_INTERVAL * 3 || directededge.bridge()) { - resampled = {shape.front(), shape.back()}; - interval = length; - } else { - resampled = valhalla::midgard::resample_spherical_polyline(shape, interval); - } - // Get the heights at each sampled point. Compute "weighted" - // grades as well as max grades in both directions. Valid range - // for weighted grades is between -10 and +15 which is then - // mapped to a value between 0 to 15 for use in costing. - auto heights = sample->get_all(resampled); - auto grades = valhalla::skadi::weighted_grade(heights, interval); - if (length < kMinimumInterval) { - // Keep the default grades - but set the mean elevation - forward_grades = std::make_tuple(0.0, 0.0, 0.0, std::get<3>(grades)); - reverse_grades = std::make_tuple(0.0, 0.0, 0.0, std::get<3>(grades)); - } else { - // Set the forward grades. Reverse the path and compute the - // weighted grade in reverse direction. - forward_grades = grades; - std::reverse(heights.begin(), heights.end()); - reverse_grades = valhalla::skadi::weighted_grade(heights, interval); + // Evenly sample the shape and add the last shape point. TODO - if close to the end do not! + std::vector resampled = + valhalla::midgard::resample_spherical_polyline(shape, POSTING_INTERVAL); + resampled.push_back(shape.back()); + + // Get the heights at each sampled point. + std::vector heights(resampled.size()); + if (directededge.bridge() || directededge.tunnel() || directededge.use() == Use::kFerry) { + // Get height at beginning and end of bridge/tunnel + std::vector tmp; + tmp.emplace_back(resampled.front()); + tmp.emplace_back(resampled.back()); + auto h = sample->get_all(tmp); + heights[0] = h[0]; + float dh = (h[1] - heights[0]) / heights.size(); + for (size_t i = 1; i < heights.size(); ++i) { + heights[i] = heights[i - 1] + dh; } + } else { + heights = sample->get_all(resampled); + } + + // Compute "weighted" grades as well as max grades in both directions. Valid range + // for weighted grades is between -10 and +15 which is then mapped to a value + // between 0 to 15 for use in costing. + auto grades = valhalla::skadi::weighted_grade(heights, POSTING_INTERVAL); + if (length < kMinimumInterval) { + // Keep the default grades - but set the mean elevation + forward_grades = std::make_tuple(0.0, 0.0, 0.0, std::get<3>(grades)); + reverse_grades = std::make_tuple(0.0, 0.0, 0.0, std::get<3>(grades)); + } else { + // Set the forward grades. Reverse the path and compute the + // weighted grade in reverse direction. + forward_grades = grades; + std::reverse(heights.begin(), heights.end()); + reverse_grades = valhalla::skadi::weighted_grade(heights, POSTING_INTERVAL); } - // Add elevation info to the geo attribute cache. TODO - add mean elevation. + // Add elevation info to the geo attribute cache. + float mean_elevation = std::get<3>(forward_grades); uint32_t forward_grade = static_cast(std::get<0>(forward_grades) * .6 + 6.5); uint32_t reverse_grade = static_cast(std::get<0>(reverse_grades) * .6 + 6.5); auto inserted = @@ -109,12 +213,19 @@ void add_elevations_to_single_tile(GraphReader& graphreader, std::get<2>(reverse_grades))}); found = inserted.first; - // Set the mean elevation on EdgeInfo - float mean_elevation = std::get<3>(forward_grades); - tilebuilder.set_mean_elevation(edge_info_offset, - mean_elevation == valhalla::skadi::get_no_data_value() - ? kNoElevationData - : mean_elevation); + // Store the new edge info offset + new_offsets[edge_info_offset] = ei_offset; + + // Encode elevation along the edge and add to EdgeInfo along with the mean elevation. + // Bridges, tunnels, ferries are special cases. Increment the new edge info offset. + std::vector encoded; + auto wayid = tilebuilder.edgeinfo(&directededge).wayid(); + if (directededge.bridge() || directededge.tunnel() || directededge.use() == Use::kFerry) { + encoded = encode_btf_elevation(sample, shape, length, wayid); + } else { + encoded = encode_edge_elevation(sample, shape, length, wayid); + } + ei_offset += tilebuilder.set_elevation(edge_info_offset, mean_elevation, encoded); } // Edge elevation information. If the edge is forward (with respect to the shape) @@ -128,6 +239,18 @@ void add_elevations_to_single_tile(GraphReader& graphreader, directededge.set_max_down_slope(max_down_slope); } + // Iterate through all directed edges and update their edge info offsets + for (uint32_t i = 0; i < tilebuilder.header()->directededgecount(); ++i) { + DirectedEdge& directededge = tilebuilder.directededge_builder(i); + uint32_t edge_info_offset = directededge.edgeinfo_offset(); + auto ei_offset = new_offsets.find(edge_info_offset); + if (ei_offset == new_offsets.end()) { + LOG_ERROR("Could not find edge info offset in the map"); + } else { + directededge.set_edgeinfo_offset(ei_offset->second); + } + } + // Update the tile tilebuilder.StoreTileData(); @@ -226,4 +349,4 @@ void ElevationBuilder::Build(const boost::property_tree::ptree& pt, } } // namespace mjolnir -} // namespace valhalla \ No newline at end of file +} // namespace valhalla diff --git a/src/mjolnir/ferry_connections.cc b/src/mjolnir/ferry_connections.cc index 79bc828dff..7ab02d749f 100644 --- a/src/mjolnir/ferry_connections.cc +++ b/src/mjolnir/ferry_connections.cc @@ -8,14 +8,17 @@ namespace valhalla { namespace mjolnir { -// Get the best classification for any driveable non-ferry and non-link +// Invalid index +const uint32_t kInvalidIndex = std::numeric_limits::max(); + +// Get the best classification for any drivable non-ferry and non-link // edges from a node. Skip any reclassified ferry edges uint32_t GetBestNonFerryClass(const std::map& edges) { uint32_t bestrc = kAbsurdRoadClass; for (const auto& edge : edges) { uint16_t fwd_access = edge.first.fwd_access & baldr::kVehicularAccess; uint16_t rev_access = edge.first.rev_access & baldr::kVehicularAccess; - if (!edge.first.attributes.driveable_ferry && !edge.first.attributes.link && + if (!edge.first.attributes.drivable_ferry && !edge.first.attributes.link && !edge.first.attributes.reclass_ferry && (fwd_access || rev_access)) { if (edge.first.attributes.importance < bestrc) { bestrc = edge.first.attributes.importance; @@ -35,14 +38,14 @@ class CompareCost { // Form the shortest path from the start node until a node that // touches the specified road classification. -uint32_t ShortestPath(const uint32_t start_node_idx, - const uint32_t node_idx, - sequence& ways, - sequence& way_nodes, - sequence& edges, - sequence& nodes, - const bool inbound, - const uint32_t rc) { +std::pair ShortestPath(const uint32_t start_node_idx, + const uint32_t node_idx, + sequence& ways, + sequence& way_nodes, + sequence& edges, + sequence& nodes, + const bool inbound, + const bool first_edge_destonly) { // Method to get the shape for an edge - since LL is stored as a pair of // floats we need to change into PointLL to get length of an edge const auto EdgeShape = [&way_nodes](size_t idx, const size_t count) { @@ -61,7 +64,9 @@ uint32_t ShortestPath(const uint32_t start_node_idx, uint16_t overall_access_before = baldr::kVehicularAccess; uint16_t overall_access_after = 0; // find accessible paths for as many modes as we can but stop if we can't find any more - while (overall_access_before != overall_access_after) { + // or found all modes in the first round + while ((overall_access_after != baldr::kVehicularAccess) && + (overall_access_before != overall_access_after)) { overall_access_before = overall_access_after; // which modes do we still have to find a path for? @@ -80,7 +85,7 @@ uint32_t ShortestPath(const uint32_t start_node_idx, // Add node to list of node labels, set the node status and add // to the adjacency set uint32_t nodelabel_index = 0; - node_labels.emplace_back(0.0f, node_idx, node_idx); + node_labels.emplace_back(0.0f, node_idx, node_idx, 0, first_edge_destonly); node_status[node_idx] = {kTemporary, nodelabel_index}; adjset.push({0.0f, nodelabel_index}); nodelabel_index++; @@ -89,6 +94,7 @@ uint32_t ShortestPath(const uint32_t start_node_idx, // is reached uint32_t n = 0; uint32_t label_idx = 0; + uint32_t last_label_idx = kInvalidIndex; while (!adjset.empty()) { // Get the next node from the adjacency list/priority queue. Gets its // current cost and index @@ -96,6 +102,7 @@ uint32_t ShortestPath(const uint32_t start_node_idx, float current_cost = expand_node.first; label_idx = expand_node.second; uint32_t expand_node_idx = node_labels[label_idx].node_index; + const bool pred_destonly = node_labels[label_idx].dest_only; adjset.pop(); // Skip if already labeled - this can happen if an edge is already in @@ -107,25 +114,35 @@ uint32_t ShortestPath(const uint32_t start_node_idx, // Expand all edges from this node auto expand_node_itr = nodes[expand_node_idx]; auto expanded_bundle = collect_node_edges(expand_node_itr, nodes, edges); + if (!(expanded_bundle.node.access() & access_filter)) { + continue; + } // We are finished if node has RC <= rc and beyond first several edges. // Have seen cases where the immediate connections are high class roads // but then there are service roads (lanes) immediately after (like // Twawwassen Terminal near Vancouver,BC) - if (n > 400 && GetBestNonFerryClass(expanded_bundle.node_edges) <= rc) { - break; + if (GetBestNonFerryClass(expanded_bundle.node_edges) <= kFerryUpClass) { + // Set the last label index - shortest path is recovered backwards from this + // label to the ferry start + last_label_idx = label_idx; + + // TODO - better termination criteria?! + if (n > 400) { + break; + } } n++; // Label the node as done/permanent node_status[expand_node_idx] = {kPermanent, label_idx}; - // Expand edges. Skip ferry edges and non-driveable edges (based on + // Expand edges. Skip ferry edges and non-drivable edges (based on // the inbound flag). for (const auto& expandededge : expanded_bundle.node_edges) { // Skip any ferry edge and any edge that includes the start node index const auto& edge = expandededge.first; - if (edge.attributes.driveable_ferry || edge.sourcenode_ == start_node_idx || + if (edge.attributes.drivable_ferry || edge.sourcenode_ == start_node_idx || edge.targetnode_ == start_node_idx) { continue; } @@ -140,7 +157,7 @@ uint32_t ShortestPath(const uint32_t start_node_idx, uint16_t edge_fwd_access = edge.fwd_access & baldr::kVehicularAccess; uint16_t edge_rev_access = edge.rev_access & baldr::kVehicularAccess; - // Skip non-driveable edges and ones which are not accessible for the current access_filter + // Skip non-drivable edges and ones which are not accessible for the current access_filter // (based on inbound flag) bool forward = (edge.sourcenode_ == expand_node_idx); if (forward) { @@ -162,9 +179,13 @@ uint32_t ShortestPath(const uint32_t start_node_idx, continue; } - // Get cost - need the length and speed of the edge; Use a penalty if an edge is - // destination_only and calculate it in the cost - float penalty = w.destination_only() ? 300 : 0; + // if the first edge was destonly and the previous edge was as well, add no penalty + // TODO(nils): this would have to take into account HGV destonly to be accurate + float penalty = + (first_edge_destonly ? (!pred_destonly && w.destination_only()) : w.destination_only()) + ? 300 + : 0; + // Get cost - need the length and speed of the edge; auto shape = EdgeShape(edge.llindex_, edge.attributes.llcount); float cost = current_cost + ((valhalla::midgard::length(shape) * 3.6f) / w.speed()) + penalty; @@ -177,30 +198,34 @@ uint32_t ShortestPath(const uint32_t start_node_idx, } // Add to the node labels and adjacency set. Skip if this is a loop. - node_labels.emplace_back(cost, endnode, expand_node_idx); + node_labels.emplace_back(cost, endnode, expand_node_idx, edge.wayindex_, + w.destination_only()); node_status[endnode] = {kTemporary, nodelabel_index}; adjset.push({cost, nodelabel_index}); nodelabel_index++; } } - // If only one label we have immediately found an edge with proper - // classification - or we cannot expand due to driveability - if (node_labels.size() == 1) { - LOG_DEBUG("Only 1 edge reclassified"); - return 0; + // Path not found to edge with proper classification + if (last_label_idx == kInvalidIndex) { + return std::make_pair(0, false); } // Trace shortest path backwards and upgrade edge classifications // did we find all modes in the path? uint16_t path_access = baldr::kVehicularAccess; while (true) { - // Get the edge between this node and the predecessor - uint32_t idx = node_labels[label_idx].node_index; - uint32_t pred_node = node_labels[label_idx].pred_node_index; + // Get the edge with matching wayindex between this node and the predecessor + const NodeLabel& node_lab = node_labels[last_label_idx]; + uint32_t idx = node_lab.node_index; + uint32_t pred_node = node_lab.pred_node_index; + uint32_t way_index = node_lab.way_index; auto expand_node_itr = nodes[idx]; auto bundle2 = collect_node_edges(expand_node_itr, nodes, edges); for (auto& edge : bundle2.node_edges) { + if (edge.first.wayindex_ != way_index) { + continue; + } bool forward = edge.first.sourcenode_ == pred_node; if (forward || edge.first.targetnode_ == pred_node) { sequence::iterator element = edges[edge.second]; @@ -210,8 +235,8 @@ uint32_t ShortestPath(const uint32_t start_node_idx, } else if ((forward && !inbound) || (!forward && inbound)) { path_access &= update_edge.fwd_access; } - if (update_edge.attributes.importance > rc) { - update_edge.attributes.importance = rc; + if (update_edge.attributes.importance > kFerryUpClass) { + update_edge.attributes.importance = kFerryUpClass; update_edge.attributes.reclass_ferry = true; element = update_edge; edge_count++; @@ -223,13 +248,14 @@ uint32_t ShortestPath(const uint32_t start_node_idx, if (pred_node == node_idx) { break; } - label_idx = node_status[pred_node].index; + + last_label_idx = node_status[pred_node].index; } // update the overall mode access with the previous path overall_access_after |= path_access; } - return edge_count; + return std::make_pair(edge_count, true); } // Check if the ferry included in this node bundle is short. Must be @@ -252,7 +278,7 @@ bool ShortFerry(const uint32_t node_index, bool short_edge = false; for (const auto& edge : bundle.node_edges) { // Check ferry edge. - if (edge.first.attributes.driveable_ferry) { + if (edge.first.attributes.drivable_ferry) { uint32_t endnode = (edge.first.sourcenode_ == node_index) ? edge.first.targetnode_ : edge.first.sourcenode_; auto end_node_itr = nodes[endnode]; @@ -261,12 +287,12 @@ bool ShortFerry(const uint32_t node_index, // If we notice that either the end node or source node is a connection to another ferry edge, // be cautious and assume this isn't a short edge. for (const auto& edge2 : bundle.node_edges) { - if (edge2.first.llindex_ != edge.first.llindex_ && edge2.first.attributes.driveable_ferry) { + if (edge2.first.llindex_ != edge.first.llindex_ && edge2.first.attributes.drivable_ferry) { return false; } } for (const auto& edge2 : bundle2.node_edges) { - if (edge2.first.llindex_ != edge.first.llindex_ && edge2.first.attributes.driveable_ferry) { + if (edge2.first.llindex_ != edge.first.llindex_ && edge2.first.attributes.drivable_ferry) { return false; } } @@ -290,8 +316,7 @@ bool ShortFerry(const uint32_t node_index, void ReclassifyFerryConnections(const std::string& ways_file, const std::string& way_nodes_file, const std::string& nodes_file, - const std::string& edges_file, - const uint32_t rc) { + const std::string& edges_file) { LOG_INFO("Reclassifying ferry connection graph edges..."); sequence ways(ways_file, false); @@ -307,20 +332,25 @@ void ReclassifyFerryConnections(const std::string& ways_file, // regular (non-ferry) edge. Skip short ferry edges (river crossing?) uint32_t ferry_endpoint_count = 0; uint32_t total_count = 0; + uint32_t missed_both = 0; sequence::iterator node_itr = nodes.begin(); while (node_itr != nodes.end()) { auto bundle = collect_node_edges(node_itr, nodes, edges); if (bundle.node.ferry_edge_ && bundle.node.non_ferry_edge_ && - GetBestNonFerryClass(bundle.node_edges) > rc && + GetBestNonFerryClass(bundle.node_edges) > kFerryUpClass && !ShortFerry(node_itr.position(), bundle, edges, nodes, way_nodes)) { + bool inbound_path_found = false; + bool outbound_path_found = false; + PointLL ll = (*nodes[node_itr.position()]).node.latlng(); + // Form shortest path from node along each edge connected to the ferry, // track until the specified RC is reached for (const auto& edge : bundle.node_edges) { uint16_t edge_fwd_access = edge.first.fwd_access & baldr::kVehicularAccess; uint16_t edge_rev_access = edge.first.rev_access & baldr::kVehicularAccess; - // Skip ferry edges and non-driveable edges - if (edge.first.attributes.driveable_ferry || (!edge_fwd_access && !edge_rev_access)) { + // Skip ferry edges and non-drivable edges + if (edge.first.attributes.drivable_ferry || (!edge_fwd_access && !edge_rev_access)) { continue; } @@ -329,42 +359,95 @@ void ReclassifyFerryConnections(const std::string& ways_file, ? edge.first.targetnode_ : edge.first.sourcenode_; + // if the non-ferry edge connecting on land is dest_only, we will unset dest_only + // for all ways encountered during the expansion, to counteract a popular mapping + // error, see https://github.com/valhalla/valhalla/issues/3942 + const bool remove_destonly = (*ways[edge.first.wayindex_]).destination_only(); + // Check if edge is oneway towards the ferry or outbound from the // ferry. If edge is drivable both ways we need to expand it twice- - // once with a driveable path towards the ferry and once with a - // driveable path away from the ferry + // once with a drivable path towards the ferry and once with a + // drivable path away from the ferry if (edge_fwd_access == edge_rev_access) { - // Driveable in both directions - get an inbound path and an + // drivable in both directions - get an inbound path and an // outbound path. - total_count += ShortestPath(node_itr.position(), end_node_idx, ways, way_nodes, edges, - nodes, true, rc); - total_count += ShortestPath(node_itr.position(), end_node_idx, ways, way_nodes, edges, - nodes, false, rc); + auto ret1 = ShortestPath(node_itr.position(), end_node_idx, ways, way_nodes, edges, nodes, + true, remove_destonly); + total_count += ret1.first; + if (ret1.second) { + inbound_path_found = true; + } + auto ret2 = ShortestPath(node_itr.position(), end_node_idx, ways, way_nodes, edges, nodes, + false, remove_destonly); + total_count += ret2.first; + if (ret2.second) { + outbound_path_found = true; + } + + // Reclassify the first/start edge if a connection to higher class roads + // is found. Do this AFTER finding shortest path so we do not immediately + // determine we hit the specified classification + sequence::iterator element = edges[edge.second]; + auto update_edge = *element; + if (ret1.second && ret2.second && update_edge.attributes.importance > kFerryUpClass) { + update_edge.attributes.importance = kFerryUpClass; + update_edge.attributes.reclass_ferry = remove_destonly; + element = update_edge; + total_count++; + } } else { // Check if oneway inbound to the ferry bool inbound = (edge.first.sourcenode_ == node_itr.position()) ? edge_rev_access : edge_fwd_access; - total_count += ShortestPath(node_itr.position(), end_node_idx, ways, way_nodes, edges, - nodes, inbound, rc); + auto ret = ShortestPath(node_itr.position(), end_node_idx, ways, way_nodes, edges, nodes, + inbound, remove_destonly); + total_count += ret.first; + if (ret.second) { + // Reclassify the first/start edge if a connection to higher class roads + // is found. Do this AFTER finding shortest path so we do not immediately + // determine we hit the specified classification + sequence::iterator element = edges[edge.second]; + auto update_edge = *element; + if (update_edge.attributes.importance > kFerryUpClass) { + update_edge.attributes.importance = kFerryUpClass; + update_edge.attributes.reclass_ferry = remove_destonly; + element = update_edge; + total_count++; + } + + if (inbound) { + inbound_path_found = true; + } else { + outbound_path_found = true; + } + } } ferry_endpoint_count++; + } - // Reclassify the first/start edge. Do this AFTER finding shortest path so - // we do not immediately determine we hit the specified classification - sequence::iterator element = edges[edge.second]; - auto update_edge = *element; - update_edge.attributes.importance = rc; - element = update_edge; - total_count++; + // Log cases where reclassification fails + if (!inbound_path_found && !outbound_path_found) { + missed_both++; + } else { + if (!inbound_path_found) { + LOG_WARN("Reclassification fails inbound to ferry at LL =" + std::to_string(ll.lat()) + + "," + std::to_string(ll.lng())); + } + if (!outbound_path_found) { + LOG_WARN("Reclassification fails outbound from ferry at LL =" + std::to_string(ll.lat()) + + "," + std::to_string(ll.lng())); + } } } // Go to the next node node_itr += bundle.node_count; } + LOG_INFO("Finished ReclassifyFerryEdges: ferry_endpoint_count = " + std::to_string(ferry_endpoint_count) + ", " + std::to_string(total_count) + - " edges reclassified."); + " edges reclassified. Failed both directions for " + std::to_string(missed_both) + + " connections."); } } // namespace mjolnir diff --git a/src/mjolnir/graphbuilder.cc b/src/mjolnir/graphbuilder.cc index 557ffa3c0b..8e13b9ae3c 100644 --- a/src/mjolnir/graphbuilder.cc +++ b/src/mjolnir/graphbuilder.cc @@ -1,5 +1,5 @@ #include -#include +#include #include #include @@ -18,6 +18,7 @@ #include "midgard/logging.h" #include "midgard/pointll.h" #include "midgard/polyline2.h" +#include "midgard/sequence.h" #include "midgard/tiles.h" #include "midgard/util.h" #include "mjolnir/admin.h" @@ -43,11 +44,16 @@ namespace { std::map SortGraph(const std::string& nodes_file, const std::string& edges_file) { LOG_INFO("Sorting graph..."); - // Sort nodes by graphid then by osmid, so its basically a set of tiles + // Sort nodes by graphid then by grid within the tile. This sorts nodes geo-spatially which + // helps performance by improving memory coherence. sequence nodes(nodes_file, false); nodes.sort([](const Node& a, const Node& b) { if (a.graph_id == b.graph_id) { - return a.node.osmid_ < b.node.osmid_; + if (a.grid_id == b.grid_id) { + return a.node.osmid_ < b.node.osmid_; + } else { + return a.grid_id < b.grid_id; + } } return a.graph_id < b.graph_id; }); @@ -149,6 +155,7 @@ void ConstructEdges(const std::string& ways_file, const std::string& nodes_file, const std::string& edges_file, const std::function& graph_id_predicate, + const std::function& grid_id_predicate, const bool infer_turn_channels) { LOG_INFO("Creating graph edges from ways..."); @@ -200,7 +207,7 @@ void ConstructEdges(const std::string& ways_file, way_node.node.link_edge_ = way.link(); way_node.node.non_link_edge_ = !way.link() && (way.auto_forward() || way.auto_backward()); nodes.push_back({way_node.node, static_cast(edges.size()), static_cast(-1), - graph_id_predicate(way_node.node)}); + graph_id_predicate(way_node.node), grid_id_predicate(way_node.node)}); // Iterate through the nodes of the way until we find an intersection while (current_way_node_index < way_nodes.size()) { @@ -220,8 +227,8 @@ void ConstructEdges(const std::string& ways_file, // remember what edge this node will end, its complicated by the fact that we delay adding the // edge until the next iteration of the loop, ie once the edge becomes prev_edge uint32_t end_of = static_cast(edges.size() + prev_edge.is_valid()); - nodes.push_back( - {way_node.node, static_cast(-1), end_of, graph_id_predicate(way_node.node)}); + nodes.push_back({way_node.node, static_cast(-1), end_of, + graph_id_predicate(way_node.node), grid_id_predicate(way_node.node)}); // Mark the edge as ending a way if this is the last node in the way edge.attributes.way_end = current_way_node_index == last_way_node_index; @@ -366,6 +373,7 @@ uint32_t CreateSimpleTurnRestriction(const uint64_t wayid, uint32_t AddAccessRestrictions(const uint32_t edgeid, const uint64_t wayid, const OSMData& osmdata, + const bool forward, GraphTileBuilder& graphtile) { auto res = osmdata.access_restrictions.equal_range(wayid); if (res.first == osmdata.access_restrictions.end()) { @@ -374,10 +382,16 @@ uint32_t AddAccessRestrictions(const uint32_t edgeid, uint32_t modes = 0; for (auto r = res.first; r != res.second; ++r) { - AccessRestriction access_restriction(edgeid, r->second.type(), r->second.modes(), - r->second.value()); - graphtile.AddAccessRestriction(access_restriction); - modes |= r->second.modes(); + auto direction = r->second.direction(); + + if ((direction == AccessRestrictionDirection::kBoth) || + (forward && direction == AccessRestrictionDirection::kForward) || + (!forward && direction == AccessRestrictionDirection::kBackward)) { + AccessRestriction access_restriction(edgeid, r->second.type(), r->second.modes(), + r->second.value()); + graphtile.AddAccessRestriction(access_restriction); + modes |= r->second.modes(); + } } return modes; } @@ -388,11 +402,11 @@ void BuildTileSet(const std::string& ways_file, const std::string& edges_file, const std::string& complex_restriction_from_file, const std::string& complex_restriction_to_file, - const std::string& pronunciation_file, + const std::string& linguistic_node_file, const std::string& tile_dir, const OSMData& osmdata, - std::map::const_iterator tile_start, - std::map::const_iterator tile_end, + std::queue>& tiles, + std::mutex& tiles_lock, const uint32_t tile_creation_date, const boost::property_tree::ptree& pt, std::promise& result) { @@ -403,11 +417,7 @@ void BuildTileSet(const std::string& ways_file, sequence nodes(nodes_file, false); sequence complex_restrictions_from(complex_restriction_from_file, false); sequence complex_restrictions_to(complex_restriction_to_file, false); - - auto less_than = [](const OSMPronunciation& a, const OSMPronunciation& b) { - return a.way_id() < b.way_id(); - }; - sequence pronunciation(pronunciation_file, false); + sequence linguistic_node(linguistic_node_file, false); auto database = pt.get_optional("admin"); bool infer_internal_intersections = @@ -455,12 +465,25 @@ void BuildTileSet(const std::string& ways_file, // shape/attributes twice we avoid doing this by caching it here std::unordered_map> geo_attribute_cache; + std::map, uint32_t> pronunciationMap; + std::map, uint32_t> langMap; //////////////////////////////////////////////////////////////////////////// // Iterate over tiles - for (; tile_start != tile_end; ++tile_start) { + while (true) { + tiles_lock.lock(); + if (tiles.empty()) { + // all work done + tiles_lock.unlock(); + break; + } + + auto tile = tiles.front(); + tiles.pop(); + tiles_lock.unlock(); + try { // What actually writes the tile - GraphId tile_id = tile_start->first.Tile_Base(); + GraphId tile_id = tile.first.Tile_Base(); GraphTileBuilder graphtile(tile_dir, tile_id, false); // Information about tile creation @@ -478,10 +501,11 @@ void BuildTileSet(const std::string& ways_file, std::multimap admin_polys; std::unordered_map drive_on_right; std::unordered_map allow_intersection_names; + language_poly_index language_polys; if (admin_db_handle) { admin_polys = GetAdminInfo(admin_db_handle, drive_on_right, allow_intersection_names, - tiling.TileBounds(id), graphtile); + language_polys, tiling.TileBounds(id), graphtile); if (admin_polys.size() == 1) { // TODO - check if tile bounding box is entirely inside the polygon... tile_within_one_admin = true; @@ -489,12 +513,9 @@ void BuildTileSet(const std::string& ways_file, } bool tile_within_one_tz = false; - std::multimap tz_polys; - if (tz_db_handle) { - tz_polys = GetTimeZones(tz_db_handle, tiling.TileBounds(id)); - if (tz_polys.size() == 1) { - tile_within_one_tz = true; - } + auto tz_polys = GetTimeZones(tz_db_handle, tiling.TileBounds(id)); + if (tz_polys.size() == 1) { + tile_within_one_tz = true; } // Iterate through the nodes @@ -502,12 +523,12 @@ void BuildTileSet(const std::string& ways_file, //////////////////////////////////////////////////////////////////////// // Iterate over nodes in the tile - auto node_itr = nodes[tile_start->second]; + auto node_itr = nodes[tile.second]; // to avoid realloc we guess how many edges there might be in a given tile geo_attribute_cache.clear(); - geo_attribute_cache.reserve(5 * (std::next(tile_start) == tile_end - ? nodes.end() - node_itr - : std::next(tile_start)->second - tile_start->second)); + // geo_attribute_cache.reserve(5 * (std::next(tile_start) == tile_end + // ? nodes.end() - node_itr + // : std::next(tile_start)->second - tile_start->second)); while (node_itr != nodes.end() && (*node_itr).graph_id.Tile_Base() == tile_id) { // amalgamate all the node duplicates into one and the edges that connect to it @@ -515,7 +536,7 @@ void BuildTileSet(const std::string& ways_file, auto bundle = collect_node_edges(node_itr, nodes, edges); // Make sure node has edges - if (bundle.node_edges.size() == 0) { + if (bundle.node_edges.empty()) { LOG_ERROR("Node has no edges - skip"); continue; } @@ -526,11 +547,14 @@ void BuildTileSet(const std::string& ways_file, // Get the admin index uint32_t admin_index = 0; bool dor = false; + std::vector> default_languages; if (use_admin_db) { admin_index = (tile_within_one_admin) ? admin_polys.begin()->first : GetMultiPolyId(admin_polys, node_ll, graphtile); dor = drive_on_right[admin_index]; + default_languages = GetMultiPolyIndexes(language_polys, node_ll); + } else { admin_index = graphtile.AddAdmin("", "", osmdata.node_names.name(node.country_iso_index()), osmdata.node_names.name(node.state_iso_index())); @@ -590,7 +614,15 @@ void BuildTileSet(const std::string& ways_file, speed_limit = kMaxAssumedSpeed; } - uint32_t truck_speed = w.truck_speed(); + const uint8_t directed_truck_speed = + forward ? w.truck_speed_forward() : w.truck_speed_backward(); + + // if there's a general truck speed AND a directed one, apply the stricter one + // otherwise just pick whichever is set + uint32_t truck_speed = w.truck_speed() && directed_truck_speed + ? std::min(w.truck_speed(), directed_truck_speed) + : std::max(w.truck_speed(), directed_truck_speed); + if (truck_speed > kMaxAssumedSpeed) { LOG_WARN("Truck Speed = " + std::to_string(truck_speed) + " wayId= " + std::to_string(w.way_id())); @@ -704,31 +736,129 @@ void BuildTileSet(const std::string& ways_file, } } - OSMPronunciation p{0}; - if (w.has_pronunciation_tags()) { - OSMPronunciation target{w.way_id()}; - sequence::iterator pronunciation_it = - pronunciation.find(target, less_than); - if (pronunciation_it != pronunciation.end()) { - p = pronunciation_it; + pronunciationMap.clear(); + auto pron = osmdata.pronunciations.equal_range(w.way_id()); + if (pron.first != osmdata.pronunciations.end()) { + for (auto p = pron.first; p != pron.second; ++p) { + pronunciationMap[std::make_pair(p->second.key_.type_, p->second.key_.alpha_)] = + p->second.name_offset(); + } + } + + langMap.clear(); + auto lang = osmdata.langs.equal_range(w.way_id()); + if (lang.first != osmdata.langs.end()) { + for (auto l = lang.first; l != lang.second; ++l) { + langMap[std::make_pair(l->second.key_.type_, l->second.key_.alpha_)] = + l->second.name_offset(); } } // Get the shape for the edge and compute its length uint32_t edge_info_offset; auto found = geo_attribute_cache.cend(); - if (dual_refs || !graphtile.HasEdgeInfo(edge_pair.second, (*nodes[source]).graph_id, - (*nodes[target]).graph_id, edge_info_offset)) { + if (((w.ref_left_index() && w.ref_right_index()) || + (w.name_left_index() && w.name_right_index()) || + (w.official_name_left_index() && w.official_name_right_index()) || + (w.alt_name_left_index() && w.alt_name_right_index()) || + (w.tunnel_name_left_index() && w.tunnel_name_right_index())) || + dual_refs || (w.name_forward_index() && w.name_backward_index()) || + !graphtile.HasEdgeInfo(edge_pair.second, (*nodes[source]).graph_id, + (*nodes[target]).graph_id, edge_info_offset)) { // add the info auto shape = EdgeShape(edge.llindex_, edge.attributes.llcount); + bool diff_names = false; + OSMLinguistic::DiffType type = OSMLinguistic::DiffType::kRight; + uint32_t name_index = w.name_index(), name_lang_index = w.name_lang_index(); + if (w.name_right_index() && forward) { + name_index = w.name_right_index(); + name_lang_index = w.name_right_lang_index(); + diff_names = true; + type = OSMLinguistic::DiffType::kRight; + } else if (w.name_left_index() && !forward) { + name_index = w.name_left_index(); + name_lang_index = w.name_left_lang_index(); + diff_names = true; + type = OSMLinguistic::DiffType::kLeft; + } else if (w.name_forward_index() && forward) { + name_index = w.name_forward_index(); + name_lang_index = w.name_forward_lang_index(); + diff_names = true; + type = OSMLinguistic::DiffType::kForward; + } else if (w.name_backward_index() && !forward) { + name_index = w.name_backward_index(); + name_lang_index = w.name_backward_lang_index(); + diff_names = true; + type = OSMLinguistic::DiffType::kBackward; + } + + uint32_t official_name_index = w.official_name_index(), + official_name_lang_index = w.official_name_lang_index(); + if (w.official_name_right_index() && forward) { + official_name_index = w.official_name_right_index(); + official_name_lang_index = w.official_name_right_lang_index(); + diff_names = true; + type = OSMLinguistic::DiffType::kRight; + } else if (w.official_name_left_index() && !forward) { + official_name_index = w.official_name_left_index(); + official_name_lang_index = w.official_name_left_lang_index(); + diff_names = true; + type = OSMLinguistic::DiffType::kLeft; + } + + uint32_t alt_name_index = w.alt_name_index(), + alt_name_lang_index = w.alt_name_lang_index(); + if (w.alt_name_right_index() && forward) { + alt_name_index = w.alt_name_right_index(); + alt_name_lang_index = w.alt_name_right_lang_index(); + diff_names = true; + type = OSMLinguistic::DiffType::kRight; + } else if (w.alt_name_left_index() && !forward) { + alt_name_index = w.alt_name_left_index(); + alt_name_lang_index = w.alt_name_left_lang_index(); + diff_names = true; + type = OSMLinguistic::DiffType::kLeft; + } + + uint32_t ref_index = w.ref_index(), ref_lang_index = w.ref_lang_index(); + if (w.ref_right_index() && forward) { + ref_index = w.ref_right_index(); + ref_lang_index = w.ref_right_lang_index(); + diff_names = true; + type = OSMLinguistic::DiffType::kRight; + } else if (w.ref_left_index() && !forward) { + ref_index = w.ref_left_index(); + ref_lang_index = w.ref_left_lang_index(); + diff_names = true; + type = OSMLinguistic::DiffType::kLeft; + } + + uint32_t tunnel_index = w.tunnel_name_index(), + tunnel_lang_index = w.tunnel_name_lang_index(); + if (w.tunnel_name_right_index() && forward) { + tunnel_index = w.tunnel_name_right_index(); + tunnel_lang_index = w.tunnel_name_right_lang_index(); + diff_names = true; + type = OSMLinguistic::DiffType::kRight; + } else if (w.tunnel_name_left_index() && !forward) { + tunnel_index = w.tunnel_name_left_index(); + tunnel_lang_index = w.tunnel_name_left_lang_index(); + diff_names = true; + type = OSMLinguistic::DiffType::kLeft; + } + uint16_t types = 0; - std::vector names, tagged_values, pronunciations; - w.GetNames(ref, osmdata.name_offset_map, p, types, names, pronunciations); - w.GetTaggedValues(osmdata.name_offset_map, p, names.size(), tagged_values, - pronunciations); + std::vector names, tagged_values, linguistics; + w.GetNames(ref, osmdata.name_offset_map, pronunciationMap, langMap, default_languages, + ref_index, ref_lang_index, name_index, name_lang_index, official_name_index, + official_name_lang_index, alt_name_index, alt_name_lang_index, types, names, + linguistics, type, diff_names); + w.GetTaggedValues(osmdata.name_offset_map, pronunciationMap, langMap, default_languages, + tunnel_index, tunnel_lang_index, names.size(), tagged_values, + linguistics, type, diff_names); // Update bike_network type if (bike_network) { @@ -741,7 +871,8 @@ void BuildTileSet(const std::string& ways_file, graphtile.AddEdgeInfo(edge_pair.second, (*nodes[source]).graph_id, (*nodes[target]).graph_id, w.way_id(), kNoElevationData, bike_network, speed_limit, shape, names, tagged_values, - pronunciations, types, added, dual_refs); + linguistics, types, added, (diff_names || dual_refs)); + if (added) { stats.edgeinfocount++; } @@ -810,10 +941,12 @@ void BuildTileSet(const std::string& ways_file, // TODO - update logic so we limit the CreateSignInfoList calls // Any exits for this directed edge? is auto and oneway? std::vector signs; - std::vector pronunciations; + std::vector linguistics; + bool has_guide = - GraphBuilder::CreateSignInfoList(node, w, p, osmdata, signs, pronunciations, fork, - forward, (directededge.use() == Use::kRamp), + GraphBuilder::CreateSignInfoList(node, w, pronunciationMap, langMap, osmdata, + default_languages, linguistic_node, signs, linguistics, + fork, forward, (directededge.use() == Use::kRamp), (directededge.use() == Use::kTurnChannel)); // add signs if signs exist // and directed edge if forward access and auto use @@ -824,8 +957,7 @@ void BuildTileSet(const std::string& ways_file, ((directededge.link() && (!((bundle.link_count == 2) && (bundle.driveforward_count == 1)))) || fork || has_guide)) { - - graphtile.AddSigns(idx, signs, pronunciations); + graphtile.AddSigns(idx, signs, linguistics); directededge.set_sign(true); } @@ -890,7 +1022,8 @@ void BuildTileSet(const std::string& ways_file, // Add restrictions..For now only storing access restrictions for trucks // TODO - support more than one mode if (directededge.forwardaccess()) { - uint32_t ar_modes = AddAccessRestrictions(idx, w.way_id(), osmdata, graphtile); + uint32_t ar_modes = + AddAccessRestrictions(idx, w.way_id(), osmdata, directededge.forward(), graphtile); if (ar_modes) { directededge.set_access_restriction(ar_modes); } @@ -1035,44 +1168,93 @@ void BuildTileSet(const std::string& ways_file, graphtile.nodes().back().set_transition_index(stop_yield_info); } - if (admin_index != 0 && node.named_intersection() && allow_intersection_names[admin_index]) { + if (node.named_intersection()) { + if ((admin_index != 0 && allow_intersection_names[admin_index]) || + (node.type() == NodeType::kTollBooth) || (node.type() == NodeType::kTollGantry)) { - std::vector node_names; - node_names = GetTagTokens(osmdata.node_names.name(node.name_index())); + std::vector node_names; + std::vector node_langs; + OSMNodeLinguistic ling; + if (node.linguistic_info_index() != 0) { + ling = linguistic_node.at(node.linguistic_info_index()); + } + OSMWay::ProcessNamesPronunciations(osmdata.node_names, default_languages, + node.name_index(), ling.name_lang_index(), node_names, + node_langs, false); + + std::vector signs; + signs.reserve(node_names.size()); + + std::vector ipa_tokens, nt_sampa_tokens, katakana_tokens, jeita_tokens; + std::vector ipa_langs, nt_sampa_langs, katakana_langs, + jeita_langs; + + GraphBuilder::GetPronunciationTokens(osmdata.node_names, default_languages, + ling.name_pronunciation_ipa_index(), + ling.name_pronunciation_ipa_lang_index(), + ling.name_pronunciation_nt_sampa_index(), + ling.name_pronunciation_nt_sampa_lang_index(), + ling.name_pronunciation_katakana_index(), + ling.name_pronunciation_katakana_lang_index(), + ling.name_pronunciation_jeita_index(), + ling.name_pronunciation_jeita_lang_index(), + ipa_tokens, ipa_langs, nt_sampa_tokens, + nt_sampa_langs, katakana_tokens, katakana_langs, + jeita_tokens, jeita_langs); + + bool add_ipa = (ipa_tokens.size() != 0); + bool add_nt_sampa = (nt_sampa_tokens.size() != 0); + bool add_katakana = (katakana_tokens.size() != 0); + bool add_jeita = (jeita_tokens.size() != 0); + + std::vector linguistics; + std::map lang_map; + + uint32_t phoneme_count = 0; + size_t phoneme_start_index = 0; + + size_t key = signs.size(); + bool add_phoneme = (add_ipa || add_nt_sampa || add_katakana || add_jeita); + if (add_phoneme) { + + GraphBuilder::BuildPronunciations(ipa_tokens, ipa_langs, nt_sampa_tokens, + nt_sampa_langs, katakana_tokens, katakana_langs, + jeita_tokens, jeita_langs, node_langs, signs.size(), + key, linguistics, lang_map, add_ipa, add_nt_sampa, + add_katakana, add_jeita); + phoneme_count = linguistics.size(); + if (phoneme_count == 0) + add_phoneme = false; + } - std::vector signs; - signs.reserve(node_names.size()); - - std::vector ipa_tokens, nt_sampa_tokens, katakana_tokens, jeita_tokens; - GraphBuilder::GetPronunciationTokens(osmdata, node.name_pronunciation_ipa_index(), - node.name_pronunciation_nt_sampa_index(), - node.name_pronunciation_katakana_index(), - node.name_pronunciation_jeita_index(), ipa_tokens, - nt_sampa_tokens, katakana_tokens, jeita_tokens, true); - - bool add_ipa = (ipa_tokens.size() && node_names.size() == ipa_tokens.size()); - bool add_nt_sampa = (nt_sampa_tokens.size() && node_names.size() == nt_sampa_tokens.size()); - bool add_katakana = (katakana_tokens.size() && node_names.size() == katakana_tokens.size()); - bool add_jeita = (jeita_tokens.size() && node_names.size() == jeita_tokens.size()); - - std::vector pronunciations; - - for (size_t i = 0; i < node_names.size(); ++i) { - if (add_ipa || add_nt_sampa || add_katakana || add_jeita) { - uint32_t count = 0; - const size_t size = pronunciations.size(); - GraphBuilder::BuildPronunciations(ipa_tokens, nt_sampa_tokens, katakana_tokens, - jeita_tokens, i, pronunciations, add_ipa, - add_nt_sampa, add_katakana, add_jeita, count); - signs.emplace_back(Sign::Type::kJunctionName, false, false, (count != 0), size, count, - node_names[i]); - } else - signs.emplace_back(Sign::Type::kJunctionName, false, false, false, 0, 0, node_names[i]); - } + for (size_t i = 0; i < node_names.size(); ++i) { + + if (lang_map.size() != 0) { + // only add a language if it has not already been added during pronunciation + // processing + auto itr = lang_map.find(i); + if (itr != lang_map.end()) { + GraphBuilder::AddLanguage(i, itr->second, linguistics); + } + } else if (i < node_langs.size() && !add_phoneme) { + phoneme_start_index = linguistics.size(); + GraphBuilder::AddLanguage(key, node_langs.at(i), linguistics); + phoneme_count = linguistics.size() - phoneme_start_index; + key++; + } - if (signs.size()) { - graphtile.nodes().back().set_named_intersection(true); - graphtile.AddSigns(graphtile.nodes().size() - 1, signs, pronunciations); + Sign::Type sign_type = Sign::Type::kJunctionName; + if ((node.type() == NodeType::kTollBooth) || (node.type() == NodeType::kTollGantry)) + sign_type = Sign::Type::kTollName; + + signs.emplace_back(sign_type, false, false, (phoneme_count != 0), phoneme_start_index, + phoneme_count, node_names[i]); + } + + if (signs.size()) { + graphtile.nodes().back().set_named_intersection(true); + graphtile.AddSigns(graphtile.nodes().size() - 1, signs, linguistics); + } } } // Set drive on right flag @@ -1101,14 +1283,14 @@ void BuildTileSet(const std::string& ways_file, graphtile.StoreTileData(); // Made a tile - LOG_DEBUG((boost::format("Wrote tile %1%: %2% bytes") % tile_start->first % + LOG_DEBUG((boost::format("Wrote tile %1%: %2% bytes") % tile.first % graphtile.header_builder().end_offset()) .str()); } // Whatever happens in Vegas.. catch (std::exception& e) { // ..gets sent back to the main thread result.set_exception(std::current_exception()); - LOG_ERROR((boost::format("Failed tile %1%: %2%") % tile_start->first % e.what()).str()); + LOG_ERROR((boost::format("Failed tile %1%: %2%") % tile.first % e.what()).str()); return; } } @@ -1134,7 +1316,7 @@ void BuildLocalTiles(const unsigned int thread_count, const std::string& edges_file, const std::string& complex_from_restriction_file, const std::string& complex_to_restriction_file, - const std::string& pronunciation_file, + const std::string& linguistic_node_file, const std::map& tiles, const std::string& tile_dir, DataQuality& stats, @@ -1153,26 +1335,24 @@ void BuildLocalTiles(const unsigned int thread_count, std::vector> results(threads.size()); // Divvy up the work - size_t floor = tiles.size() / threads.size(); - size_t at_ceiling = tiles.size() - (threads.size() * floor); - std::map::const_iterator tile_start, tile_end = tiles.begin(); + std::queue> tile_queue; + std::mutex tile_lock; + for (auto id : tiles) { + tile_queue.emplace(id); + } // Atomically pass around stats info for (size_t i = 0; i < threads.size(); ++i) { - // Figure out how many this thread will work on (either ceiling or floor) - size_t tile_count = (i < at_ceiling ? floor + 1 : floor); - // Where the range begins - tile_start = tile_end; - // Where the range ends - std::advance(tile_end, tile_count); // Make the thread - threads[i].reset(new std::thread(BuildTileSet, std::cref(ways_file), std::cref(way_nodes_file), - std::cref(nodes_file), std::cref(edges_file), - std::cref(complex_from_restriction_file), - std::cref(complex_to_restriction_file), - std::cref(pronunciation_file), std::cref(tile_dir), - std::cref(osmdata), tile_start, tile_end, tile_creation_date, - std::cref(pt.get_child("mjolnir")), std::ref(results[i]))); + threads[i] = + std::make_shared(BuildTileSet, std::cref(ways_file), std::cref(way_nodes_file), + std::cref(nodes_file), std::cref(edges_file), + std::cref(complex_from_restriction_file), + std::cref(complex_to_restriction_file), + std::cref(linguistic_node_file), std::cref(tile_dir), + std::cref(osmdata), std::ref(tile_queue), std::ref(tile_lock), + tile_creation_date, std::cref(pt.get_child("mjolnir")), + std::ref(results[i])); } // Join all the threads to wait for them to finish up their work @@ -1199,8 +1379,34 @@ void BuildLocalTiles(const unsigned int thread_count, } // namespace -namespace valhalla { -namespace mjolnir { +namespace valhalla::mjolnir { + +// Returns the grid Id within the tile. A tile is subdivided into a nxn grid. +// The grid Id within the tile is used to sort nodes spatially. +uint32_t GetGridId(const midgard::PointLL& pointll, + const midgard::Tiles& tiling, + const uint32_t grid_divisions) { + // By default grid_divisions is set to 0 to indicate no spatial sorting within a tile + if (grid_divisions == 0) { + return 0; + } + + auto tile_id = tiling.TileId(pointll); + if (tile_id >= 0) { + auto base_ll = tiling.Base(tile_id); + float grid_size = tiling.TileSize() / static_cast(grid_divisions); + uint32_t row = static_cast((pointll.lat() - base_ll.lat()) / grid_size); + uint32_t col = static_cast((pointll.lng() - base_ll.lng()) / grid_size); + if (row > grid_divisions || col > grid_divisions) { + LOG_ERROR("grid row = " + std::to_string(row) + " col = " + std::to_string(col)); + return 0; + } + return (row * grid_divisions + col); + } else { + LOG_ERROR("GetGridId: Invalid tile id"); + return 0; + } +} std::map GraphBuilder::BuildEdges(const boost::property_tree::ptree& pt, const std::string& ways_file, @@ -1208,10 +1414,23 @@ std::map GraphBuilder::BuildEdges(const boost::property_tree::p const std::string& nodes_file, const std::string& edges_file) { uint8_t level = TileHierarchy::levels().back().level; + auto tiling = TileHierarchy::get_tiling(level); + uint32_t grid_divisions = + pt.get("mjolnir.data_processing.grid_divisions_within_tile", 0); + if (grid_divisions > 0) { + LOG_INFO("Sort nodes spatially within each tile using nxn grids where n = " + + std::to_string(grid_divisions)); + } else { + LOG_INFO("Spatial sorting of nodes within each tile is disabled"); + } + // Make the edges and nodes in the graph ConstructEdges( ways_file, way_nodes_file, nodes_file, edges_file, [&level](const OSMNode& node) { return TileHierarchy::GetGraphId(node.latlng(), level); }, + [&tiling, &grid_divisions](const OSMNode& node) { + return GetGridId(node.latlng(), tiling, grid_divisions); + }, pt.get("mjolnir.data_processing.infer_turn_channels", true)); return SortGraph(nodes_file, edges_file); @@ -1226,35 +1445,42 @@ void GraphBuilder::Build(const boost::property_tree::ptree& pt, const std::string& edges_file, const std::string& complex_from_restriction_file, const std::string& complex_to_restriction_file, - const std::string& pronunciation_file, + const std::string& linguistic_node_file, const std::map& tiles) { // Reclassify links (ramps). Cannot do this when building tiles since the - // edge list needs to be modified - DataQuality stats; - if (pt.get("mjolnir.reclassify_links", true)) { - ReclassifyLinks(ways_file, nodes_file, edges_file, way_nodes_file, osmdata, - pt.get("mjolnir.data_processing.infer_turn_channels", true)); + // edge list needs to be modified. ReclassifyLinks also infers turn channels + // so we always want to do this unless reclassify_links and infer_turn_channels + // are both false. + bool reclassify_links = pt.get("mjolnir.reclassify_links", true); + bool infer_turn_channels = pt.get("mjolnir.data_processing.infer_turn_channels", true); + if (reclassify_links || infer_turn_channels) { + ReclassifyLinks(ways_file, nodes_file, edges_file, way_nodes_file, osmdata, reclassify_links, + infer_turn_channels); } else { - LOG_WARN("Not reclassifying link graph edges"); + LOG_WARN("Not reclassifying link graph edges or inferring turn channels"); } - // Reclassify ferry connection edges - use the highway classification cutoff - baldr::RoadClass rc = baldr::RoadClass::kPrimary; - for (auto& level : TileHierarchy::levels()) { - if (level.name == "highway") { - rc = level.importance; - } + // Do not reclassify ferry connection edges if no hierarchies are built. If reclassifying, + // we use RoadClass::kPrimary (highway classification) as cutoff. + if (pt.get("mjolnir.hierarchy", true)) { + ReclassifyFerryConnections(ways_file, way_nodes_file, nodes_file, edges_file); + } else { + LOG_WARN("Not reclassifying ferry connections since no hierarches are being created"); } - ReclassifyFerryConnections(ways_file, way_nodes_file, nodes_file, edges_file, - static_cast(rc)); + + // Build tiles at the local level. Form connected graph from nodes and edges. + DataQuality stats; unsigned int threads = std::max(static_cast(1), pt.get("mjolnir.concurrency", std::thread::hardware_concurrency())); - // Build tiles at the local level. Form connected graph from nodes and edges. - std::string tile_dir = pt.get("mjolnir.tile_dir"); + auto tile_dir = pt.get("mjolnir.tile_dir"); + // Disable sqlite3 internal memory tracking (results in a high-contention mutex, and we don't care + // about marginal sqlite memory usage). + sqlite3_config(SQLITE_CONFIG_MEMSTATUS, false); + BuildLocalTiles(threads, osmdata, ways_file, way_nodes_file, nodes_file, edges_file, - complex_from_restriction_file, complex_to_restriction_file, pronunciation_file, + complex_from_restriction_file, complex_to_restriction_file, linguistic_node_file, tiles, tile_dir, stats, pt); stats.LogStatistics(); } @@ -1308,137 +1534,307 @@ std::string GraphBuilder::GetRef(const std::string& way_ref, const std::string& return refs; } -void GraphBuilder::GetPronunciationTokens(const OSMData& osmdata, - const uint32_t ipa_index, - const uint32_t nt_sampa_index, - const uint32_t katakana_index, - const uint32_t jeita_index, - std::vector& ipa_tokens, - std::vector& nt_sampa_tokens, - std::vector& katakana_tokens, - std::vector& jeita_tokens, - bool is_node_pronunciation) { +void GraphBuilder::GetPronunciationTokens( + const UniqueNames& uniquenames, + const std::vector>& default_languages, + const uint32_t ipa_index, + const uint32_t ipa_lang_index, + const uint32_t nt_sampa_index, + const uint32_t nt_sampa_lang_index, + const uint32_t katakana_index, + const uint32_t katakana_lang_index, + const uint32_t jeita_index, + const uint32_t jeita_lang_index, + std::vector& ipa_tokens, + std::vector& ipa_langs, + std::vector& nt_sampa_tokens, + std::vector& nt_sampa_langs, + std::vector& katakana_tokens, + std::vector& katakana_langs, + std::vector& jeita_tokens, + std::vector& jeita_langs) { ipa_tokens.clear(); nt_sampa_tokens.clear(); katakana_tokens.clear(); jeita_tokens.clear(); - if (is_node_pronunciation) { - if (ipa_index != 0) - ipa_tokens = GetTagTokens(osmdata.node_names.name(ipa_index)); + ipa_langs.clear(); + nt_sampa_langs.clear(); + katakana_langs.clear(); + jeita_langs.clear(); + + if (ipa_index != 0) { + OSMWay::ProcessNamesPronunciations(uniquenames, default_languages, ipa_index, ipa_lang_index, + ipa_tokens, ipa_langs, false, true); + } + if (nt_sampa_index != 0) + OSMWay::ProcessNamesPronunciations(uniquenames, default_languages, nt_sampa_index, + nt_sampa_lang_index, nt_sampa_tokens, nt_sampa_langs, false, + true); + if (katakana_index != 0) + OSMWay::ProcessNamesPronunciations(uniquenames, default_languages, katakana_index, + katakana_lang_index, katakana_tokens, katakana_langs, false, + true); + if (jeita_index != 0) + OSMWay::ProcessNamesPronunciations(uniquenames, default_languages, jeita_index, jeita_lang_index, + jeita_tokens, jeita_langs, false, true); +} + +void GraphBuilder::AddLanguage(const size_t index, + const baldr::Language& lang, + std::vector& linguistics) { + + if (lang != baldr::Language::kNone) { + linguistic_text_header_t header{static_cast(lang), + 0, + static_cast(baldr::PronunciationAlphabet::kNone), + static_cast(index), + 0, + 0}; + linguistics.emplace_back(reinterpret_cast(&header), kLinguisticHeaderSize); + } +} + +void GraphBuilder::AddPronunciationsWithLang(std::vector& pronunciations, + std::map& lang_map, + const baldr::PronunciationAlphabet verbal_type, + const std::vector& pronunciation_tokens, + const std::vector& pronunciation_langs, + const std::vector& token_langs, + const size_t token_size, + const size_t key) { + + auto get_pronunciations = [](const std::vector& pronunciation_tokens, + const std::vector& pronunciation_langs, + const std::map indexMap, const size_t key, + const baldr::PronunciationAlphabet verbal_type) { + linguistic_text_header_t header{static_cast(baldr::Language::kNone), + 0, + static_cast(verbal_type), + static_cast(key), + 0, + 0}; + std::vector pronunciations; + for (size_t i = 0; i < pronunciation_tokens.size(); i++) { + auto& t = pronunciation_tokens[i]; + + if (!indexMap.empty()) { + auto index = indexMap.find(i); + if (index != indexMap.end()) + header.name_index_ = index->second; + else + continue; + } - if (nt_sampa_index != 0) - nt_sampa_tokens = GetTagTokens(osmdata.node_names.name(nt_sampa_index)); + if (t.empty()) { // pronunciation is blank. just add the lang - if (katakana_index != 0) - katakana_tokens = GetTagTokens(osmdata.node_names.name(katakana_index)); + if (pronunciation_langs.empty()) + continue; - if (jeita_index != 0) - jeita_tokens = GetTagTokens(osmdata.node_names.name(jeita_index)); + header.language_ = static_cast(pronunciation_langs.at(i)); + header.length_ = 0; + header.phonetic_alphabet_ = static_cast(baldr::PronunciationAlphabet::kNone); - } else { - if (ipa_index != 0) - ipa_tokens = GetTagTokens(osmdata.name_offset_map.name(ipa_index)); + pronunciations.emplace_back(reinterpret_cast(&header), kLinguisticHeaderSize); + } else { + + header.phonetic_alphabet_ = static_cast(verbal_type); - if (nt_sampa_index != 0) - nt_sampa_tokens = GetTagTokens(osmdata.name_offset_map.name(nt_sampa_index)); + header.length_ = t.size(); + header.language_ = + (!pronunciation_langs.empty() ? static_cast(pronunciation_langs.at(i)) + : static_cast(baldr::Language::kNone)); - if (katakana_index != 0) - katakana_tokens = GetTagTokens(osmdata.name_offset_map.name(katakana_index)); + pronunciations.emplace_back( + (std::string(reinterpret_cast(&header), kLinguisticHeaderSize) + t)); + } + if (indexMap.empty()) + ++header.name_index_; + } + return pronunciations; + }; - if (jeita_index != 0) - jeita_tokens = GetTagTokens(osmdata.name_offset_map.name(jeita_index)); + bool process = false, found = false; + std::map indexMap; + size_t k = key; + + if ((pronunciation_langs.empty() && token_langs.empty()) || + ((!pronunciation_langs.empty() && token_langs.empty()) && + (pronunciation_langs.size() <= token_size))) + process = true; + else { + std::pair::iterator, bool> ret; + for (auto& token_lang : token_langs) { + for (size_t j = 0; j < pronunciation_langs.size(); j++) { + if (token_lang == pronunciation_langs[j]) { + ret = indexMap.insert(std::make_pair(j, k)); + if (!ret.second) // already used + continue; + else { + k++; + process = true; + found = true; + break; + } + } + } + if (!found) { + lang_map.emplace(k, token_lang); + k++; + } + found = false; + } } -} -void GraphBuilder::AddPronunciation(const baldr::PronunciationAlphabet alphabet, - const std::string& phoneme, - std::vector& pronunciations, - uint32_t& count) { - - // TODO set the language - if (phoneme.size()) { - linguistic_text_header_t header{static_cast(baldr::Language::kNone), 0, - static_cast(alphabet), static_cast(0)}; - header.length_ = phoneme.length(); - pronunciations.emplace_back((std::string(reinterpret_cast(&header), 3) + phoneme)); - count++; + if (process) { + std::vector p = + get_pronunciations(pronunciation_tokens, pronunciation_langs, indexMap, key, verbal_type); + + pronunciations.insert(pronunciations.end(), p.begin(), p.end()); } } void GraphBuilder::BuildPronunciations(const std::vector& ipa_tokens, + const std::vector& ipa_langs, const std::vector& nt_sampa_tokens, + const std::vector& nt_sampa_langs, const std::vector& katakana_tokens, + const std::vector& katakana_langs, const std::vector& jeita_tokens, - const size_t index, + const std::vector& jeita_langs, + const std::vector& token_langs, + const size_t token_size, + const size_t key, std::vector& pronunciations, + std::map& lang_map, bool add_ipa, bool add_nt_sampa, bool add_katakana, - bool add_jeita, - uint32_t& count) { - if (add_ipa) - AddPronunciation(PronunciationAlphabet::kIpa, ipa_tokens[index], pronunciations, count); + bool add_jeita) { + if (add_ipa) { + AddPronunciationsWithLang(pronunciations, lang_map, baldr::PronunciationAlphabet::kIpa, + ipa_tokens, ipa_langs, token_langs, token_size, key); + } - if (add_nt_sampa) - AddPronunciation(PronunciationAlphabet::kNtSampa, nt_sampa_tokens[index], pronunciations, count); + if (add_nt_sampa) { - if (add_katakana) - AddPronunciation(PronunciationAlphabet::kXKatakana, katakana_tokens[index], pronunciations, - count); + AddPronunciationsWithLang(pronunciations, lang_map, baldr::PronunciationAlphabet::kNtSampa, + nt_sampa_tokens, nt_sampa_langs, token_langs, token_size, key); + } - if (add_jeita) - AddPronunciation(PronunciationAlphabet::kXJeita, jeita_tokens[index], pronunciations, count); + if (add_katakana) { + AddPronunciationsWithLang(pronunciations, lang_map, baldr::PronunciationAlphabet::kKatakana, + katakana_tokens, katakana_langs, token_langs, token_size, key); + } + + if (add_jeita) { + AddPronunciationsWithLang(pronunciations, lang_map, baldr::PronunciationAlphabet::kJeita, + jeita_tokens, jeita_langs, token_langs, token_size, key); + } } -bool GraphBuilder::CreateSignInfoList(const OSMNode& node, - const OSMWay& way, - const OSMPronunciation& pronunciation, - const OSMData& osmdata, - std::vector& exit_list, - std::vector& pronunciations, - bool fork, - bool forward, - bool ramp, - bool tc) { +bool GraphBuilder::CreateSignInfoList( + const OSMNode& node, + const OSMWay& way, + const std::map, uint32_t>& pronunciationMap, + const std::map, uint32_t>& langMap, + const OSMData& osmdata, + const std::vector>& default_languages, + sequence& linguistic_node, + std::vector& exit_list, + std::vector& linguistics, + bool fork, + bool forward, + bool ramp, + bool tc) { bool has_guide = false; - std::vector ipa_tokens, nt_sampa_tokens, katakana_tokens, jeita_tokens; + std::vector ipa_tokens, nt_sampa_tokens, katakana_tokens, jeita_tokens, + display_ref_tokens; + std::vector ipa_langs, nt_sampa_langs, katakana_langs, jeita_langs; + bool add_ipa, add_nt_sampa, add_katakana, add_jeita; bool is_branch_or_toward = tc || (!ramp && !fork); - Sign::Type sign_type; + Sign::Type sign_type = Sign::Type::kExitNumber; + + auto get_pronunciation_index = [&pronunciationMap](const uint8_t type, const uint8_t alpha) { + auto itr = pronunciationMap.find(std::make_pair(type, alpha)); + if (itr != pronunciationMap.end()) { + return itr->second; + } + return static_cast(0); + }; + + auto get_lang_index = [&langMap](const uint8_t type, const uint8_t alpha) { + auto itr = langMap.find(std::make_pair(type, alpha)); + if (itr != langMap.end()) { + return itr->second; + } + return static_cast(0); + }; auto get_pronunciations = - [](const OSMData& osmdata, const size_t signs_size, const uint32_t ipa_index, - const uint32_t nt_sampa_index, const uint32_t katakana_index, const uint32_t jeita_index, - std::vector& ipa_tokens, std::vector& nt_sampa_tokens, - std::vector& katakana_tokens, std::vector& jeita_tokens, - bool& add_ipa, bool& add_nt_sampa, bool& add_katakana, bool& add_jeita, - bool is_node_pronunciation = false) { - GetPronunciationTokens(osmdata, ipa_index, nt_sampa_index, katakana_index, jeita_index, - ipa_tokens, nt_sampa_tokens, katakana_tokens, jeita_tokens, - is_node_pronunciation); - - add_ipa = (ipa_tokens.size() && signs_size == ipa_tokens.size()); - add_nt_sampa = (nt_sampa_tokens.size() && signs_size == nt_sampa_tokens.size()); - add_katakana = (katakana_tokens.size() && signs_size == katakana_tokens.size()); - add_jeita = (jeita_tokens.size() && signs_size == jeita_tokens.size()); + [](const UniqueNames& uniquenames, + const std::vector>& default_languages, const uint32_t ipa_index, + const uint32_t ipa_lang_index, const uint32_t nt_sampa_index, + const uint32_t nt_sampa_lang_index, const uint32_t katakana_index, + const uint32_t katakana_lang_index, const uint32_t jeita_index, + const uint32_t jeita_lang_index, std::vector& ipa_tokens, + std::vector& ipa_langs, std::vector& nt_sampa_tokens, + std::vector& nt_sampa_langs, std::vector& katakana_tokens, + std::vector& katakana_langs, std::vector& jeita_tokens, + std::vector& jeita_langs, bool& add_ipa, bool& add_nt_sampa, + bool& add_katakana, bool& add_jeita) { + GetPronunciationTokens(uniquenames, default_languages, ipa_index, ipa_lang_index, + nt_sampa_index, nt_sampa_lang_index, katakana_index, + katakana_lang_index, jeita_index, jeita_lang_index, ipa_tokens, + ipa_langs, nt_sampa_tokens, nt_sampa_langs, katakana_tokens, + katakana_langs, jeita_tokens, jeita_langs); + + add_ipa = !ipa_tokens.empty(); + add_nt_sampa = !nt_sampa_tokens.empty(); + add_katakana = !katakana_tokens.empty(); + add_jeita = !jeita_tokens.empty(); return (add_ipa || add_nt_sampa || add_katakana || add_jeita); }; // helper to build SignInfo and update exit_list - auto add_sign_info = [&](const std::vector& refs, const Sign::Type& type, - const bool is_route_number, const bool add_phoneme) { + auto add_sign_info = [&](const std::vector& refs, + const std::vector& sign_langs, const Sign::Type& type, + const bool is_route_number, const bool phoneme) { + uint32_t phoneme_count = 0; + bool add_phoneme = phoneme; + size_t phoneme_start_index = 0; + size_t key = exit_list.size(); + std::map lang_map; + + if (add_phoneme) { + phoneme_start_index = linguistics.size(); + BuildPronunciations(ipa_tokens, ipa_langs, nt_sampa_tokens, nt_sampa_langs, katakana_tokens, + katakana_langs, jeita_tokens, jeita_langs, sign_langs, refs.size(), key, + linguistics, lang_map, add_ipa, add_nt_sampa, add_katakana, add_jeita); + phoneme_count = linguistics.size() - phoneme_start_index; + if (phoneme_count == 0) + add_phoneme = false; + } + for (size_t i = 0; i < refs.size(); ++i) { - uint32_t phoneme_count = 0; - size_t phoneme_start_index = pronunciations.size(); - if (add_phoneme) { - BuildPronunciations(ipa_tokens, nt_sampa_tokens, katakana_tokens, jeita_tokens, i, - pronunciations, add_ipa, add_nt_sampa, add_katakana, add_jeita, - phoneme_count); + if (!lang_map.empty()) { + // only add a language if it has not already been added during pronunciation processing + auto itr = lang_map.find(key); + if (itr != lang_map.end()) { + AddLanguage(key, itr->second, linguistics); + phoneme_count = linguistics.size() - phoneme_start_index; + } + } else if (i < sign_langs.size() && !add_phoneme) { + phoneme_start_index = linguistics.size(); + AddLanguage(key, sign_langs.at(i), linguistics); + phoneme_count = linguistics.size() - phoneme_start_index; } + key++; + if (phoneme_count == 0) { // reset phoneme index if no phonemes were found phoneme_start_index = 0; @@ -1449,32 +1845,58 @@ bool GraphBuilder::CreateSignInfoList(const OSMNode& node, }; std::vector sign_names; + std::vector sign_langs; //////////////////////////////////////////////////////////////////////////// // NUMBER // Exit sign number bool has_phoneme = false; + const auto ipa = static_cast(PronunciationAlphabet::kIpa); + const auto nt_sampa = static_cast(PronunciationAlphabet::kNtSampa); + const auto katakana = static_cast(PronunciationAlphabet::kKatakana); + const auto jeita = static_cast(PronunciationAlphabet::kJeita); + if (way.junction_ref_index() != 0) { - sign_names = GetTagTokens(osmdata.name_offset_map.name(way.junction_ref_index())); - has_phoneme = get_pronunciations(osmdata, sign_names.size(), - pronunciation.junction_ref_pronunciation_ipa_index(), - pronunciation.junction_ref_pronunciation_nt_sampa_index(), - pronunciation.junction_ref_pronunciation_katakana_index(), - pronunciation.junction_ref_pronunciation_jeita_index(), - ipa_tokens, nt_sampa_tokens, katakana_tokens, jeita_tokens, - add_ipa, add_nt_sampa, add_katakana, add_jeita); + way.ProcessNamesPronunciations(osmdata.name_offset_map, default_languages, + way.junction_ref_index(), way.junction_ref_lang_index(), + sign_names, sign_langs, false); + + const auto t = static_cast(OSMLinguistic::Type::kJunctionRef); + has_phoneme = + get_pronunciations(osmdata.name_offset_map, default_languages, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), ipa_tokens, + ipa_langs, nt_sampa_tokens, nt_sampa_langs, katakana_tokens, + katakana_langs, jeita_tokens, jeita_langs, add_ipa, add_nt_sampa, + add_katakana, add_jeita); + } else if (node.has_ref() && !fork && ramp) { - sign_names = GetTagTokens(osmdata.node_names.name(node.ref_index())); - has_phoneme = get_pronunciations(osmdata, sign_names.size(), node.ref_pronunciation_ipa_index(), - node.ref_pronunciation_nt_sampa_index(), - node.ref_pronunciation_katakana_index(), - node.ref_pronunciation_jeita_index(), ipa_tokens, - nt_sampa_tokens, katakana_tokens, jeita_tokens, add_ipa, - add_nt_sampa, add_katakana, add_jeita, true); + OSMNodeLinguistic ling; + if (node.linguistic_info_index() != 0) { + ling = linguistic_node.at(node.linguistic_info_index()); + } + + way.ProcessNamesPronunciations(osmdata.node_names, default_languages, node.ref_index(), + ling.ref_lang_index(), sign_names, sign_langs, false); + has_phoneme = + get_pronunciations(osmdata.node_names, default_languages, ling.ref_pronunciation_ipa_index(), + ling.ref_pronunciation_ipa_lang_index(), + ling.ref_pronunciation_nt_sampa_index(), + ling.ref_pronunciation_nt_sampa_lang_index(), + ling.ref_pronunciation_katakana_index(), + ling.ref_pronunciation_katakana_lang_index(), + ling.ref_pronunciation_jeita_index(), + ling.ref_pronunciation_jeita_lang_index(), ipa_tokens, ipa_langs, + nt_sampa_tokens, nt_sampa_langs, katakana_tokens, katakana_langs, + jeita_tokens, jeita_langs, add_ipa, add_nt_sampa, add_katakana, add_jeita); } - add_sign_info(sign_names, Sign::Type::kExitNumber, false /* is_route_number */, has_phoneme); + add_sign_info(sign_names, sign_langs, Sign::Type::kExitNumber, false /* is_route_number */, + has_phoneme); sign_names.clear(); + sign_langs.clear(); //////////////////////////////////////////////////////////////////////////// // BRANCH @@ -1483,128 +1905,200 @@ bool GraphBuilder::CreateSignInfoList(const OSMNode& node, // Guide or Exit sign branch refs if (way.destination_ref_index() != 0) { - sign_names = GetTagTokens(osmdata.name_offset_map.name(way.destination_ref_index())); + way.ProcessNamesPronunciations(osmdata.name_offset_map, default_languages, + way.destination_ref_index(), way.destination_ref_lang_index(), + sign_names, sign_langs, false); sign_type = is_branch_or_toward ? Sign::Type::kGuideBranch : Sign::Type::kExitBranch; has_branch = true; has_guide = is_branch_or_toward; - } - - has_phoneme = get_pronunciations(osmdata, sign_names.size(), - pronunciation.destination_ref_pronunciation_ipa_index(), - pronunciation.destination_ref_pronunciation_nt_sampa_index(), - pronunciation.destination_ref_pronunciation_katakana_index(), - pronunciation.destination_ref_pronunciation_jeita_index(), - ipa_tokens, nt_sampa_tokens, katakana_tokens, jeita_tokens, - add_ipa, add_nt_sampa, add_katakana, add_jeita); - add_sign_info(sign_names, sign_type, true /* is_route_number */, has_phoneme); + const uint8_t t = static_cast(OSMLinguistic::Type::kDestinationRef); + has_phoneme = + get_pronunciations(osmdata.name_offset_map, default_languages, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), ipa_tokens, + ipa_langs, nt_sampa_tokens, nt_sampa_langs, katakana_tokens, + katakana_langs, jeita_tokens, jeita_langs, add_ipa, add_nt_sampa, + add_katakana, add_jeita); + } + add_sign_info(sign_names, sign_langs, sign_type, true /* is_route_number */, has_phoneme); sign_names.clear(); + sign_langs.clear(); // Guide or Exit sign branch road names if (way.destination_street_index() != 0) { - sign_names = GetTagTokens(osmdata.name_offset_map.name(way.destination_street_index())); + + way.ProcessNamesPronunciations(osmdata.name_offset_map, default_languages, + way.destination_street_index(), + way.destination_street_lang_index(), sign_names, sign_langs, + false); + sign_type = is_branch_or_toward ? Sign::Type::kGuideBranch : Sign::Type::kExitBranch; has_branch = true; has_guide = is_branch_or_toward; - } - has_phoneme = get_pronunciations(osmdata, sign_names.size(), - pronunciation.destination_street_pronunciation_ipa_index(), - pronunciation.destination_street_pronunciation_nt_sampa_index(), - pronunciation.destination_street_pronunciation_katakana_index(), - pronunciation.destination_street_pronunciation_jeita_index(), - ipa_tokens, nt_sampa_tokens, katakana_tokens, jeita_tokens, - add_ipa, add_nt_sampa, add_katakana, add_jeita); - - add_sign_info(sign_names, sign_type, false /* is_route_number */, has_phoneme); + const uint8_t t = static_cast(OSMLinguistic::Type::kDestinationStreet); + has_phoneme = + get_pronunciations(osmdata.name_offset_map, default_languages, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), ipa_tokens, + ipa_langs, nt_sampa_tokens, nt_sampa_langs, katakana_tokens, + katakana_langs, jeita_tokens, jeita_langs, add_ipa, add_nt_sampa, + add_katakana, add_jeita); + } + add_sign_info(sign_names, sign_langs, sign_type, false /* is_route_number */, has_phoneme); sign_names.clear(); + sign_langs.clear(); //////////////////////////////////////////////////////////////////////////// // TOWARD bool has_toward = false; + //----------------------------- // Guide or Exit sign toward refs if (way.destination_ref_to_index() != 0) { - sign_names = GetTagTokens(osmdata.name_offset_map.name(way.destination_ref_to_index())); + + way.ProcessNamesPronunciations(osmdata.name_offset_map, default_languages, + way.destination_ref_to_index(), + way.destination_ref_to_lang_index(), sign_names, sign_langs, + false); + sign_type = is_branch_or_toward ? Sign::Type::kGuideToward : Sign::Type::kExitToward; has_toward = true; has_guide = is_branch_or_toward; - } - has_phoneme = get_pronunciations(osmdata, sign_names.size(), - pronunciation.destination_ref_to_pronunciation_ipa_index(), - pronunciation.destination_ref_to_pronunciation_nt_sampa_index(), - pronunciation.destination_ref_to_pronunciation_katakana_index(), - pronunciation.destination_ref_to_pronunciation_jeita_index(), - ipa_tokens, nt_sampa_tokens, katakana_tokens, jeita_tokens, - add_ipa, add_nt_sampa, add_katakana, add_jeita); + const uint8_t t = static_cast(OSMLinguistic::Type::kDestinationRefTo); + has_phoneme = + get_pronunciations(osmdata.name_offset_map, default_languages, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), ipa_tokens, + ipa_langs, nt_sampa_tokens, nt_sampa_langs, katakana_tokens, + katakana_langs, jeita_tokens, jeita_langs, add_ipa, add_nt_sampa, + add_katakana, add_jeita); + } + add_sign_info(sign_names, sign_langs, sign_type, true /* is_route_number */, has_phoneme); - add_sign_info(sign_names, sign_type, true /* is_route_number */, has_phoneme); sign_names.clear(); + sign_langs.clear(); + //----------------------------- // Guide or Exit sign toward streets if (way.destination_street_to_index() != 0) { - sign_names = GetTagTokens(osmdata.name_offset_map.name(way.destination_street_to_index())); + + way.ProcessNamesPronunciations(osmdata.name_offset_map, default_languages, + way.destination_street_to_index(), + way.destination_street_to_lang_index(), sign_names, sign_langs, + false); + sign_type = is_branch_or_toward ? Sign::Type::kGuideToward : Sign::Type::kExitToward; has_toward = true; has_guide = is_branch_or_toward; - } - has_phoneme = get_pronunciations(osmdata, sign_names.size(), - pronunciation.destination_street_to_pronunciation_ipa_index(), - pronunciation.destination_street_to_pronunciation_nt_sampa_index(), - pronunciation.destination_street_to_pronunciation_katakana_index(), - pronunciation.destination_street_to_pronunciation_jeita_index(), - ipa_tokens, nt_sampa_tokens, katakana_tokens, jeita_tokens, - add_ipa, add_nt_sampa, add_katakana, add_jeita); - - add_sign_info(sign_names, sign_type, false /* is_route_number */, has_phoneme); + const uint8_t t = static_cast(OSMLinguistic::Type::kDestinationStreetTo); + has_phoneme = + get_pronunciations(osmdata.name_offset_map, default_languages, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), ipa_tokens, + ipa_langs, nt_sampa_tokens, nt_sampa_langs, katakana_tokens, + katakana_langs, jeita_tokens, jeita_langs, add_ipa, add_nt_sampa, + add_katakana, add_jeita); + } + add_sign_info(sign_names, sign_langs, sign_type, false /* is_route_number */, has_phoneme); sign_names.clear(); + sign_langs.clear(); + //----------------------------- // Exit sign toward locations if (way.destination_index() != 0 || (forward && way.destination_forward_index() != 0) || (!forward && way.destination_backward_index() != 0)) { + uint32_t index = way.destination_index() ? way.destination_index() : (forward ? way.destination_forward_index() : way.destination_backward_index()); - sign_names = GetTagTokens(osmdata.name_offset_map.name(index)); + + // make sure we grab the correct lang based on the index we used above. + uint32_t lang_index = way.destination_lang_index() + ? way.destination_lang_index() + : (forward ? way.destination_forward_lang_index() + : way.destination_backward_lang_index()); + + way.ProcessNamesPronunciations(osmdata.name_offset_map, default_languages, index, lang_index, + sign_names, sign_langs, false); + sign_type = is_branch_or_toward ? Sign::Type::kGuideToward : Sign::Type::kExitToward; has_toward = true; has_guide = is_branch_or_toward; - uint32_t ipa_index = - pronunciation.destination_pronunciation_ipa_index() - ? pronunciation.destination_pronunciation_ipa_index() - : (forward ? pronunciation.destination_forward_pronunciation_ipa_index() - : pronunciation.destination_backward_pronunciation_ipa_index()); + const uint8_t t_destination = static_cast(OSMLinguistic::Type::kDestination); + const uint8_t t_destination_forward = + static_cast(OSMLinguistic::Type::kDestinationForward); + const uint8_t t_destination_backward = + static_cast(OSMLinguistic::Type::kDestinationBackward); + index = get_pronunciation_index(t_destination, ipa); + uint32_t ipa_index = index ? index + : (forward ? get_pronunciation_index(t_destination_forward, ipa) + : get_pronunciation_index(t_destination_backward, ipa)); + + index = get_pronunciation_index(t_destination, nt_sampa); uint32_t nt_sampa_index = - pronunciation.destination_pronunciation_nt_sampa_index() - ? pronunciation.destination_pronunciation_nt_sampa_index() - : (forward ? pronunciation.destination_forward_pronunciation_nt_sampa_index() - : pronunciation.destination_backward_pronunciation_nt_sampa_index()); + index ? index + : (forward ? get_pronunciation_index(t_destination_forward, nt_sampa) + : get_pronunciation_index(t_destination_backward, nt_sampa)); + index = get_pronunciation_index(t_destination, katakana); uint32_t katakana_index = - pronunciation.destination_pronunciation_katakana_index() - ? pronunciation.destination_pronunciation_katakana_index() - : (forward ? pronunciation.destination_forward_pronunciation_katakana_index() - : pronunciation.destination_backward_pronunciation_katakana_index()); - - uint32_t jeita_index = - pronunciation.destination_pronunciation_jeita_index() - ? pronunciation.destination_pronunciation_jeita_index() - : (forward ? pronunciation.destination_forward_pronunciation_jeita_index() - : pronunciation.destination_backward_pronunciation_jeita_index()); + index ? index + : (forward ? get_pronunciation_index(t_destination_forward, katakana) + : get_pronunciation_index(t_destination_backward, katakana)); + + index = get_pronunciation_index(t_destination, jeita); + uint32_t jeita_index = index ? index + : (forward ? get_pronunciation_index(t_destination_forward, jeita) + : get_pronunciation_index(t_destination_backward, jeita)); + + index = get_lang_index(t_destination, ipa); + uint32_t ipa_lang_index = index ? index + : (forward ? get_lang_index(t_destination_forward, ipa) + : get_lang_index(t_destination_backward, ipa)); + + index = get_lang_index(t_destination, nt_sampa); + uint32_t nt_sampa_lang_index = index + ? index + : (forward ? get_lang_index(t_destination_forward, nt_sampa) + : get_lang_index(t_destination_backward, nt_sampa)); + + index = get_lang_index(t_destination, katakana); + uint32_t katakana_lang_index = index + ? index + : (forward ? get_lang_index(t_destination_forward, katakana) + : get_lang_index(t_destination_backward, katakana)); + + index = get_lang_index(t_destination, jeita); + uint32_t jeita_lang_index = index ? index + : (forward ? get_lang_index(t_destination_forward, jeita) + : get_lang_index(t_destination_backward, jeita)); has_phoneme = - get_pronunciations(osmdata, sign_names.size(), ipa_index, nt_sampa_index, katakana_index, - jeita_index, ipa_tokens, nt_sampa_tokens, katakana_tokens, jeita_tokens, + get_pronunciations(osmdata.name_offset_map, default_languages, ipa_index, ipa_lang_index, + nt_sampa_index, nt_sampa_lang_index, katakana_index, katakana_lang_index, + jeita_index, jeita_lang_index, ipa_tokens, ipa_langs, nt_sampa_tokens, + nt_sampa_langs, katakana_tokens, katakana_langs, jeita_tokens, jeita_langs, add_ipa, add_nt_sampa, add_katakana, add_jeita); } - add_sign_info(sign_names, sign_type, false /* is_route_number */, has_phoneme); + add_sign_info(sign_names, sign_langs, sign_type, false /* is_route_number */, has_phoneme); sign_names.clear(); + sign_langs.clear(); //////////////////////////////////////////////////////////////////////////// // Process exit_to only if other branch or toward info does not exist No pronunciations. @@ -1674,39 +2168,79 @@ bool GraphBuilder::CreateSignInfoList(const OSMNode& node, if (node.has_name() && !node.named_intersection() && !fork && ramp) { sign_type = Sign::Type::kExitName; // Get the name from OSMData using the name index - sign_names = GetTagTokens(osmdata.node_names.name(node.name_index())); + + OSMNodeLinguistic ling; + if (node.linguistic_info_index() != 0) { + ling = linguistic_node.at(node.linguistic_info_index()); + } + + way.ProcessNamesPronunciations(osmdata.node_names, default_languages, node.name_index(), + ling.name_lang_index(), sign_names, sign_langs, false); + has_phoneme = + get_pronunciations(osmdata.node_names, default_languages, ling.name_pronunciation_ipa_index(), + ling.name_pronunciation_ipa_lang_index(), + ling.name_pronunciation_nt_sampa_index(), + ling.name_pronunciation_nt_sampa_lang_index(), + ling.name_pronunciation_katakana_index(), + ling.name_pronunciation_katakana_lang_index(), + ling.name_pronunciation_jeita_index(), + ling.name_pronunciation_jeita_lang_index(), ipa_tokens, ipa_langs, + nt_sampa_tokens, nt_sampa_langs, katakana_tokens, katakana_langs, + jeita_tokens, jeita_langs, add_ipa, add_nt_sampa, add_katakana, add_jeita); } - has_phoneme = get_pronunciations(osmdata, sign_names.size(), node.name_pronunciation_ipa_index(), - node.name_pronunciation_nt_sampa_index(), - node.name_pronunciation_katakana_index(), - node.name_pronunciation_jeita_index(), ipa_tokens, nt_sampa_tokens, - katakana_tokens, jeita_tokens, add_ipa, add_nt_sampa, add_katakana, - add_jeita, true); + add_sign_info(sign_names, sign_langs, sign_type, false /* is_route_number */, has_phoneme); + sign_names.clear(); + sign_langs.clear(); + + //----------------------------- + // junction:name:* + //----------------------------- + if (way.junction_name_index() != 0) { + + way.ProcessNamesPronunciations(osmdata.name_offset_map, default_languages, + way.junction_name_index(), way.junction_name_lang_index(), + sign_names, sign_langs, false); + + const uint8_t t = static_cast(OSMLinguistic::Type::kJunctionName); + sign_type = Sign::Type::kExitName; + has_phoneme = + get_pronunciations(osmdata.name_offset_map, default_languages, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), ipa_tokens, + ipa_langs, nt_sampa_tokens, nt_sampa_langs, katakana_tokens, + katakana_langs, jeita_tokens, jeita_langs, add_ipa, add_nt_sampa, + add_katakana, add_jeita); + } + + add_sign_info(sign_names, sign_langs, sign_type, false /* is_route_number */, has_phoneme); - add_sign_info(sign_names, sign_type, false /* is_route_number */, has_phoneme); sign_names.clear(); + sign_langs.clear(); //////////////////////////////////////////////////////////////////////////// // GUIDANCE VIEWS - bool has_guidance_view = false; + // junction + bool has_guidance_view_jct = false; if (forward && way.fwd_jct_base_index() > 0) { std::vector names = GetTagTokens(osmdata.name_offset_map.name(way.fwd_jct_base_index()), '|'); // route number set to true for kGuidanceViewJct type means base type for (auto& name : names) { exit_list.emplace_back(Sign::Type::kGuidanceViewJunction, true, false, false, 0, 0, name); - has_guidance_view = true; } + has_guidance_view_jct = true; } else if (!forward && way.bwd_jct_base_index() > 0) { std::vector names = GetTagTokens(osmdata.name_offset_map.name(way.bwd_jct_base_index()), '|'); // route number set to true for kGuidanceViewJct type means base type for (auto& name : names) { exit_list.emplace_back(Sign::Type::kGuidanceViewJunction, true, false, false, 0, 0, name); - has_guidance_view = true; } + has_guidance_view_jct = true; } if (forward && way.fwd_jct_overlay_index() > 0) { @@ -1715,17 +2249,19 @@ bool GraphBuilder::CreateSignInfoList(const OSMNode& node, // route number set to false for kGuidanceViewJct type means overlay type for (auto& name : names) { exit_list.emplace_back(Sign::Type::kGuidanceViewJunction, false, false, false, 0, 0, name); - has_guidance_view = true; } + has_guidance_view_jct = true; } else if (!forward && way.bwd_jct_overlay_index() > 0) { std::vector names = GetTagTokens(osmdata.name_offset_map.name(way.bwd_jct_overlay_index()), '|'); // route number set to false for kGuidanceViewJct type means overlay type for (auto& name : names) { exit_list.emplace_back(Sign::Type::kGuidanceViewJunction, false, false, false, 0, 0, name); - has_guidance_view = true; } + has_guidance_view_jct = true; } + + // signboard bool has_guidance_view_signboard = false; if (forward && way.fwd_signboard_base_index() > 0) { std::vector names = @@ -1733,23 +2269,22 @@ bool GraphBuilder::CreateSignInfoList(const OSMNode& node, // route number set to true for kGuidanceViewSignboard type means base type for (auto& name : names) { exit_list.emplace_back(Sign::Type::kGuidanceViewSignboard, true, false, false, 0, 0, name); - has_guidance_view_signboard = true; } + has_guidance_view_signboard = true; } else if (!forward && way.bwd_signboard_base_index() > 0) { std::vector names = GetTagTokens(osmdata.name_offset_map.name(way.bwd_signboard_base_index()), '|'); // route number set to true for kGuidanceViewSignboard type means base type for (auto& name : names) { exit_list.emplace_back(Sign::Type::kGuidanceViewSignboard, true, false, false, 0, 0, name); - has_guidance_view_signboard = true; } + has_guidance_view_signboard = true; } // we have to sort because we need the key/indexes for phonemes std::stable_sort(exit_list.begin(), exit_list.end()); - return (has_guide || has_guidance_view || has_guidance_view_signboard); + return (has_guide || has_guidance_view_jct || has_guidance_view_signboard); } -} // namespace mjolnir -} // namespace valhalla +} // namespace valhalla::mjolnir diff --git a/src/mjolnir/graphenhancer.cc b/src/mjolnir/graphenhancer.cc index c95bd6c9a0..95d1d5dd9e 100644 --- a/src/mjolnir/graphenhancer.cc +++ b/src/mjolnir/graphenhancer.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -115,11 +116,11 @@ void GetTurnTypes(const DirectedEdge& directededge, const NodeInfo* node = tile->node(directededge.endnode()); // Iterate through outbound edges and get turn degrees from the candidate - // edge onto outbound driveable edges. + // edge onto outbound drivable edges. const auto* diredge = tile->directededge(node->edge_index()); for (uint32_t i = 0; i < node->edge_count(); i++, diredge++) { // Skip opposing directed edge and any edge that is not a road. Skip any - // edges that are not driveable outbound. + // edges that are not drivable outbound. if (i == directededge.opp_local_idx() || !(diredge->forwardaccess() & kAutoAccess) || (directededge.restrictions() & (1 << diredge->localedgeidx())) != 0) { continue; @@ -134,7 +135,7 @@ void GetTurnTypes(const DirectedEdge& directededge, uint32_t to_heading = std::round(PointLL::HeadingAlongPolyline(shape, GetOffsetForHeading(diredge->classification(), diredge->use()))); - // Store outgoing turn type for any driveable edges + // Store outgoing turn type for any drivable edges uint32_t turndegree = GetTurnDegree(heading, to_heading); outgoing_turn_type.insert(Turn::GetType(turndegree)); } @@ -401,7 +402,7 @@ void UpdateTurnLanes(const OSMData& osmdata, constexpr uint32_t kUnreachableIterations = 20; /** - * Tests if the directed edge is unreachable by driving. If a driveable + * Tests if the directed edge is unreachable by driving. If a drivable * edge cannot reach higher class roads and a search cannot expand after * a set number of iterations the edge is considered unreachable. * @param reader Graph reader @@ -410,7 +411,7 @@ constexpr uint32_t kUnreachableIterations = 20; * @return Returns true if the edge is found to be unreachable. */ bool IsUnreachable(GraphReader& reader, std::mutex& lock, DirectedEdge& directededge) { - // Only check driveable edges. If already on a higher class road consider + // Only check drivable edges. If already on a higher class road consider // the edge reachable if (!(directededge.forwardaccess() & kAutoAccess) || directededge.classification() < RoadClass::kTertiary) { @@ -432,11 +433,11 @@ bool IsUnreachable(GraphReader& reader, std::mutex& lock, DirectedEdge& directed while (n < kUnreachableIterations) { if (expandset.empty()) { // Have expanded all nodes without reaching a higher classification - // driveable road - consider this unreachable + // drivable road - consider this unreachable return true; } - // Get all driveable edges from the node on the expandlist + // Get all drivable edges from the node on the expandlist const GraphId expandnode = *expandset.cbegin(); expandset.erase(expandset.begin()); visitedset.insert(expandnode); @@ -604,7 +605,7 @@ void SetStopYieldSignInfo(const graph_tile_ptr& start_tile, const DirectedEdge* diredge = tile->directededge(nodeinfo->edge_index()); for (uint32_t i = 0; i < nodeinfo->edge_count(); i++, diredge++) { // Skip the candidate directed edge and any non-road edges. Skip any edges - // that are not driveable inbound. + // that are not drivable inbound. if (!diredge->is_road() || !(diredge->reverseaccess() & kAutoAccess)) { continue; } @@ -679,7 +680,7 @@ bool IsIntersectionInternal(const graph_tile_ptr& start_tile, } } - // Iterate through inbound edges and get turn degrees from driveable inbound + // Iterate through inbound edges and get turn degrees from drivable inbound // edges onto the candidate edge. bool oneway_inbound = false; uint32_t heading = startnodeinfo.heading(idx); @@ -687,7 +688,7 @@ bool IsIntersectionInternal(const graph_tile_ptr& start_tile, const DirectedEdge* diredge = tile->directededge(startnodeinfo.edge_index()); for (uint32_t i = 0; i < startnodeinfo.edge_count(); i++, diredge++) { // Skip the candidate directed edge and any non-road edges. Skip any edges - // that are not driveable inbound. + // that are not drivable inbound. if (i == idx || !diredge->is_road() || !(diredge->reverseaccess() & kAutoAccess)) { continue; } @@ -697,7 +698,7 @@ bool IsIntersectionInternal(const graph_tile_ptr& start_tile, return false; } - // Store the turn type of incoming driveable edges. + // Store the turn type of incoming drivable edges. uint32_t from_heading = ((startnodeinfo.heading(i) + 180) % 360); uint32_t turndegree = GetTurnDegree(from_heading, heading); incoming_turn_type.insert(Turn::GetType(turndegree)); @@ -743,13 +744,13 @@ bool IsIntersectionInternal(const graph_tile_ptr& start_tile, } // Iterate through outbound edges and get turn degrees from the candidate - // edge onto outbound driveable edges. + // edge onto outbound drivable edges. bool oneway_outbound = false; std::set outgoing_turn_type; diredge = tile->directededge(node->edge_index()); for (uint32_t i = 0; i < node->edge_count(); i++, diredge++) { // Skip opposing directed edge and any edge that is not a road. Skip any - // edges that are not driveable outbound. + // edges that are not drivable outbound. if (i == directededge.opp_local_idx() || !diredge->is_road() || !(diredge->forwardaccess() & kAutoAccess)) { continue; @@ -770,7 +771,7 @@ bool IsIntersectionInternal(const graph_tile_ptr& start_tile, std::round(PointLL::HeadingAlongPolyline(shape, GetOffsetForHeading(diredge->classification(), diredge->use()))); - // Store outgoing turn type for any driveable edges + // Store outgoing turn type for any drivable edges uint32_t turndegree = GetTurnDegree(heading, to_heading); outgoing_turn_type.insert(Turn::GetType(turndegree)); @@ -824,16 +825,16 @@ uint32_t GetDensity(GraphReader& reader, const Tiles& tiles, uint8_t local_level) { // Radius is in km - turn into meters - float rm = kDensityRadius * kMetersPerKm; - float mr2 = rm * rm; + auto rm = kDensityRadius * kMetersPerKm; + auto mr2 = rm * rm; // Use distance approximator for all distance checks DistanceApproximator approximator(ll); // Get a list of tiles required for a node search within this radius - float lngdeg = (rm / DistanceApproximator::MetersPerLngDegree(ll.lat())); - AABB2 bbox(Point2(ll.lng() - lngdeg, ll.lat() - kDensityLatDeg), - Point2(ll.lng() + lngdeg, ll.lat() + kDensityLatDeg)); + auto lngdeg = (rm / DistanceApproximator::MetersPerLngDegree(ll.lat())); + AABB2 bbox(ll.lng() - lngdeg, ll.lat() - kDensityLatDeg, ll.lng() + lngdeg, + ll.lat() + kDensityLatDeg); std::vector tilelist = tiles.TileList(bbox); // For all tiles needed to find nodes within the radius...find nodes within @@ -1075,7 +1076,7 @@ uint32_t GetStopImpact(uint32_t from, // together for the stop_impact logic. RoadClass bestrc = RoadClass::kUnclassified; for (uint32_t i = 0; i < count; i++, edge++) { - // Check the road if it is driveable TO the intersection and is neither + // Check the road if it is drivable TO the intersection and is neither // the "to" nor "from" edge. Treat roundabout edges as two levels lower // classification (higher value) to reduce the stop impact. if (i != to && i != from && (edge->reverseaccess() & kAutoAccess)) { @@ -1252,45 +1253,6 @@ void ProcessEdgeTransitions(const uint32_t idx, } } -/** - * Get the index of the opposing edge at the end node. This is on the local hierarchy, - * before adding transition and shortcut edges. Make sure that even if the end nodes - * and lengths match that the correct edge is selected (match shape) since some loops - * can have the same length and end node. - * @param endnodetile Graph tile at the end node. - * @param startnode Start node of the directed edge. - * @param tile Graph tile of the edge - * @param directededge Directed edge to match. - */ -uint32_t GetOpposingEdgeIndex(const graph_tile_ptr& endnodetile, - const GraphId& startnode, - const graph_tile_ptr& tile, - const DirectedEdge& edge) { - // Get the nodeinfo at the end of the edge - const NodeInfo* nodeinfo = endnodetile->node(edge.endnode().id()); - - // Iterate through the directed edges and return when the end node matches the specified - // node, the length matches, and the shape matches (or edgeinfo offset matches) - const DirectedEdge* directededge = endnodetile->directededge(nodeinfo->edge_index()); - for (uint32_t i = 0; i < nodeinfo->edge_count(); i++, directededge++) { - if (directededge->endnode() == startnode && directededge->length() == edge.length()) { - // If in the same tile and the edgeinfo offset matches then the shape and names will match - if (endnodetile == tile && directededge->edgeinfo_offset() == edge.edgeinfo_offset()) { - return i; - } else { - // Need to compare shape if not in the same tile or different EdgeInfo (could be different - // names in opposing directions) - if (shapes_match(tile->edgeinfo(&edge).shape(), - endnodetile->edgeinfo(directededge).shape())) { - return i; - } - } - } - } - LOG_ERROR("Could not find opposing edge index"); - return kMaxEdgesPerNode; -} - bool ConsistentNames(const std::string& country_code, const std::vector>& names1, const std::vector>& names2) { @@ -1482,7 +1444,7 @@ void enhance(const boost::property_tree::ptree& pt, } // Go through directed edges and "enhance" directed edge attributes - uint32_t driveable_count = 0; + uint32_t drivable_count = 0; const DirectedEdge* edges = tilebuilder->directededges(nodeinfo.edge_index()); for (uint32_t j = 0; j < nodeinfo.edge_count(); j++) { DirectedEdge& directededge = tilebuilder->directededge_builder(nodeinfo.edge_index() + j); @@ -1603,10 +1565,10 @@ void enhance(const boost::property_tree::ptree& pt, } } - // Update driveable count (do this after country access logic) + // Update drivable count (do this after country access logic) if ((directededge.forwardaccess() & kAutoAccess) || (directededge.reverseaccess() & kAutoAccess)) { - driveable_count++; + drivable_count++; } // Use::kPedestrian is really a kFootway @@ -1620,7 +1582,7 @@ void enhance(const boost::property_tree::ptree& pt, } // Update the named flag - auto names = tilebuilder->edgeinfo(&directededge).GetNames(false); + auto names = e_offset.GetNames(false); directededge.set_named(names.size() > 0); // Speed assignment @@ -1695,7 +1657,7 @@ void enhance(const boost::property_tree::ptree& pt, // gates or toll-booths or toll gantry or sump buster). if (nodeinfo.type() != NodeType::kGate && nodeinfo.type() != NodeType::kTollBooth && nodeinfo.type() != NodeType::kTollGantry && nodeinfo.type() != NodeType::kSumpBuster) { - if (driveable_count == 1) { + if (drivable_count == 1) { nodeinfo.set_intersection(IntersectionType::kDeadEnd); } else if (nodeinfo.edge_count() == 2) { nodeinfo.set_intersection(IntersectionType::kFalse); diff --git a/src/mjolnir/graphfilter.cc b/src/mjolnir/graphfilter.cc index 83a8c74da5..63c927d6bb 100644 --- a/src/mjolnir/graphfilter.cc +++ b/src/mjolnir/graphfilter.cc @@ -1,5 +1,6 @@ #include "mjolnir/graphfilter.h" #include "mjolnir/graphtilebuilder.h" +#include "mjolnir/util.h" #include #include @@ -18,6 +19,7 @@ #include "midgard/sequence.h" using namespace valhalla::baldr; +using namespace valhalla::midgard; using namespace valhalla::mjolnir; namespace { @@ -27,10 +29,195 @@ uint32_t n_original_nodes = 0; uint32_t n_filtered_edges = 0; uint32_t n_filtered_nodes = 0; uint32_t can_aggregate = 0; +uint32_t aggregated = 0; // Group wheelchair and pedestrian access together constexpr uint32_t kAllPedestrianAccess = (kPedestrianAccess | kWheelchairAccess); +bool CanAggregate(const DirectedEdge* de) { + if (de->start_restriction() || de->part_of_complex_restriction() || de->end_restriction() || + de->restrictions() || de->traffic_signal() || de->access_restriction()) { + return false; + } + return true; +} + +// ExpandFromNode and ExpandFromNodeInner is reused code from restriction builder with some slight +// modifications. We are using recursion for graph traversal. We have to make sure we don't loop back +// to ourselves, walk in the correct direction, have not already visited a node, etc. Once we meet our +// criteria or not we stop. +bool ExpandFromNode(GraphReader& reader, + std::list& shape, + GraphId& en, + const GraphId& from_node, + std::unordered_set& isos, + bool forward, + std::unordered_set& visited_nodes, + uint64_t& way_id, + const graph_tile_ptr& prev_tile, + GraphId prev_node, + GraphId current_node, + const RoadClass& rc, + bool validate); + +/* + * Expand from the current node + * @param reader Graph reader. + * @param shape shape that we need to update + * @param en current end node that we started at + * @param from_node node that we started from + * @param isos country ISOs. Used to see if we cross into a new country + * @param forward traverse in the forward or backward direction + * @param visited_nodes nodes that we already visited. don't visit again + * @param way_id only interested in edges with this way_id + * @param prev_tile previous tile + * @param prev_node previous node + * @param current_node current node + * @param node_info current node's info + * @param validate Are we validating data? + * + */ +bool ExpandFromNodeInner(GraphReader& reader, + std::list& shape, + GraphId& en, + const GraphId& from_node, + std::unordered_set& isos, + bool forward, + std::unordered_set& visited_nodes, + uint64_t& way_id, + const graph_tile_ptr& prev_tile, + GraphId prev_node, + GraphId current_node, + const NodeInfo* node_info, + const RoadClass& rc, + bool validate) { + + for (size_t j = 0; j < node_info->edge_count(); ++j) { + GraphId edge_id(prev_tile->id().tileid(), prev_tile->id().level(), node_info->edge_index() + j); + const DirectedEdge* de = prev_tile->directededge(edge_id); + const auto& edge_info = prev_tile->edgeinfo(de); + + auto tile = prev_tile; + if (tile->id() != de->endnode().Tile_Base()) { + tile = reader.GetGraphTile(de->endnode()); + } + + const NodeInfo* en_info = tile->node(de->endnode().id()); + // check the direction, if we looped back, or are we done + if ((de->endnode() != prev_node) && (de->forward() == forward) && + (de->endnode() != from_node || (de->endnode() == from_node && visited_nodes.size() > 1))) { + if (edge_info.wayid() == way_id && + (en_info->mode_change() || (node_info->mode_change() && !en_info->mode_change()))) { + + // If this edge has special attributes, then we can't aggregate + if (!CanAggregate(de) || de->classification() != rc) { + way_id = 0; + return false; + } + + if (validate) { + if (isos.size() >= 1) { + isos.insert(tile->admin(en_info->admin_index())->country_iso()); + } + } else { + std::list edgeshape = + valhalla::midgard::decode7>(edge_info.encoded_shape()); + if (!de->forward()) { + std::reverse(edgeshape.begin(), edgeshape.end()); + } + + // Append shape. Skip first point since it + // should equal the last of the prior edge. + edgeshape.pop_front(); + shape.splice(shape.end(), edgeshape); + } + + // found a node that does not have aggregation marked (using mode_change flag) + // we are done. + if (node_info->mode_change() && !en_info->mode_change()) { + en = de->endnode(); + aggregated++; + return true; + } + aggregated++; + + bool found; + if (visited_nodes.find(de->endnode()) == visited_nodes.end()) { + visited_nodes.insert(de->endnode()); + + // expand with the same way_id + found = ExpandFromNode(reader, shape, en, from_node, isos, forward, visited_nodes, way_id, + tile, current_node, de->endnode(), rc, validate); + if (found) { + return true; + } + + visited_nodes.erase(de->endnode()); + } + } + } + } + return false; +} + +/* + * Expand from the next node which is now our new current node + * @param reader Graph reader. + * @param shape shape that we need to update + * @param en current end node that we started at + * @param from_node node that we started from + * @param isos country ISOs. Used to see if we cross into a new country + * @param forward traverse in the forward or backward direction + * @param visited_nodes nodes that we already visited. don't visit again + * @param way_id only interested in edges with this way_id + * @param prev_tile previous tile + * @param prev_node previous node + * @param current_node current node + * @param validate Are we validating data? + * + */ +bool ExpandFromNode(GraphReader& reader, + std::list& shape, + GraphId& en, + const GraphId& from_node, + std::unordered_set& isos, + bool forward, + std::unordered_set& visited_nodes, + uint64_t& way_id, + const graph_tile_ptr& prev_tile, + GraphId prev_node, + GraphId current_node, + const RoadClass& rc, + bool validate) { + + auto tile = prev_tile; + if (tile->id() != current_node.Tile_Base()) { + tile = reader.GetGraphTile(current_node); + } + + auto* node_info = tile->node(current_node); + // expand from the current node + return ExpandFromNodeInner(reader, shape, en, from_node, isos, forward, visited_nodes, way_id, tile, + prev_node, current_node, node_info, rc, validate); +} + +bool Aggregate(GraphId& start_node, + GraphReader& reader, + std::list& shape, + GraphId& en, + const GraphId& from_node, + uint64_t& way_id, + std::unordered_set& isos, + const RoadClass& rc, + bool forward, + bool validate) { + + graph_tile_ptr tile = reader.GetGraphTile(start_node); + std::unordered_set visited_nodes{start_node}; + return ExpandFromNode(reader, shape, en, from_node, isos, forward, visited_nodes, way_id, tile, + GraphId(), start_node, rc, validate); +} + /** * Filter edges to optionally remove edges by access. * @param reader Graph reader. @@ -74,6 +261,9 @@ void FilterTiles(GraphReader& reader, std::hash hasher; GraphId nodeid(tile_id.tileid(), tile_id.level(), 0); for (uint32_t i = 0; i < tile->header()->nodecount(); ++i, ++nodeid) { + bool diff_names = false; + bool diff_tile = false; + bool edge_filtered = false; // Count of edges added for this node uint32_t edge_count = 0; @@ -82,14 +272,18 @@ void FilterTiles(GraphReader& reader, // Iterate through directed edges outbound from this node std::vector wayid; + std::vector classification; std::vector endnode; const NodeInfo* nodeinfo = tile->node(nodeid); + std::string begin_node_iso = tile->admin(nodeinfo->admin_index())->country_iso(); + GraphId edgeid(nodeid.tileid(), nodeid.level(), nodeinfo->edge_index()); for (uint32_t j = 0; j < nodeinfo->edge_count(); ++j, ++edgeid) { // Check if the directed edge should be included const DirectedEdge* directededge = tile->directededge(edgeid); if (!include_edge(directededge)) { ++n_filtered_edges; + edge_filtered = true; continue; } @@ -137,24 +331,32 @@ void FilterTiles(GraphReader& reader, tilebuilder.AddLaneConnectivity(laneconnectivity); } + // Names can be different in the forward and backward direction + diff_names = tilebuilder.OpposingEdgeInfoDiffers(tile, directededge); + // Get edge info, shape, and names from the old tile and add to the // new. Cannot use edge info offset since edges in arterial and // highway hierarchy can cross base tiles! Use a hash based on the // encoded shape plus way Id. bool added; - auto edgeinfo = tile->edgeinfo(directededge); + const auto& edgeinfo = tile->edgeinfo(directededge); std::string encoded_shape = edgeinfo.encoded_shape(); uint32_t w = hasher(encoded_shape + std::to_string(edgeinfo.wayid())); uint32_t edge_info_offset = tilebuilder.AddEdgeInfo(w, nodeid, directededge->endnode(), edgeinfo.wayid(), edgeinfo.mean_elevation(), edgeinfo.bike_network(), edgeinfo.speed_limit(), encoded_shape, edgeinfo.GetNames(), - edgeinfo.GetTaggedValues(), edgeinfo.GetTaggedValues(true), - edgeinfo.GetTypes(), added); + edgeinfo.GetTaggedValues(), edgeinfo.GetLinguisticTaggedValues(), + edgeinfo.GetTypes(), added, diff_names); newedge.set_edgeinfo_offset(edge_info_offset); wayid.push_back(edgeinfo.wayid()); + classification.push_back(directededge->classification()); endnode.push_back(directededge->endnode()); + if (directededge->endnode().tile_value() != tile->header()->graphid().tile_value()) { + diff_tile = true; + } + // Add directed edge tilebuilder.directededges().emplace_back(std::move(newedge)); ++edge_count; @@ -186,9 +388,31 @@ void FilterTiles(GraphReader& reader, old_to_new[nodeid] = new_node; // Check if edges at this node can be aggregated. Only 2 edges, same way Id (so that - // edge attributes should match), don't end at same node (no loops). - if (edge_count == 2 && wayid[0] == wayid[1] && endnode[0] != endnode[1]) { - ++can_aggregate; + // edge attributes should match), don't end at same node (no loops), no traffic signal, + // no signs exist at the node(named_intersection), does not have different + // names, and end node of edges are not in a different tile. + // + // Note: The classification check is here due to the reclassification of ferries. Found + // that some edges that were split at pedestrian edges had different classifications due to + // the reclassification of ferry edges (e.g., https://www.openstreetmap.org/way/204337649) + if (edge_filtered && edge_count == 2 && wayid[0] == wayid[1] && + classification[0] == classification[1] && endnode[0] != endnode[1] && + !nodeinfo->traffic_signal() && !nodeinfo->named_intersection() && !diff_names && + !diff_tile) { + + // one more check on intersection and node type. similar to shortcuts + bool aggregate = + (nodeinfo->intersection() != IntersectionType::kFork && + nodeinfo->type() != NodeType::kGate && nodeinfo->type() != NodeType::kTollBooth && + nodeinfo->type() != NodeType::kTollGantry && nodeinfo->type() != NodeType::kBollard && + nodeinfo->type() != NodeType::kSumpBuster && + nodeinfo->type() != NodeType::kBorderControl); + + if (aggregate) { + // temporarily used to check aggregating edges from this node + node.set_mode_change(true); + ++can_aggregate; + } } } else { ++n_filtered_nodes; @@ -214,7 +438,348 @@ void FilterTiles(GraphReader& reader, std::to_string(n_original_nodes)); LOG_INFO("Filtered " + std::to_string(n_filtered_edges) + " directededges out of " + std::to_string(n_original_edges)); - LOG_INFO("Can aggregate: " + std::to_string(can_aggregate)); + LOG_INFO("Nodes to aggregate: " + std::to_string(can_aggregate)); +} + +void GetAggregatedData(GraphReader& reader, + std::list& shape, + GraphId& en, + const GraphId& from_node, + const graph_tile_ptr& tile, + const DirectedEdge* directededge) { + std::unordered_set isos; + bool isForward = directededge->forward(); + auto id = directededge->endnode(); + if (!isForward) { + std::reverse(shape.begin(), shape.end()); + } + + // walk in the correct direction. + uint64_t wayid = tile->edgeinfo(directededge).wayid(); + if (Aggregate(id, reader, shape, en, from_node, wayid, isos, directededge->classification(), + isForward, false)) { + aggregated++; // count the current edge + // flip the shape back for storing in edgeinfo + if (!isForward) { + std::reverse(shape.begin(), shape.end()); + } + } +} + +// If we cross into another country we can't aggregate the edges +// as the access can be different in each country. Also, bollards +// and or gates could exist at the node blocking access. +// +// Also, we need to handle islands that we created by tossing the +// pedestrian edges. For example:// +// https://www.openstreetmap.org/way/993706522 +// https://www.openstreetmap.org/way/975845893 +// As of 01/15/2024 there are only ~180 of these. + +void ValidateData(GraphReader& reader, + std::list& shape, + GraphId& en, + std::unordered_set& processed_nodes, + std::unordered_set& no_agg_ways, + const GraphId& from_node, + const graph_tile_ptr& tile, + const DirectedEdge* directededge) { + + // Get the tile at the end node. Skip if node is in another tile. + // mode_change is not set for end nodes that are in diff tiles + if (directededge->endnode().tile_value() == tile->header()->graphid().tile_value()) { + + // original edge data. + const auto& edgeinfo = tile->edgeinfo(directededge); + const NodeInfo* en_info = tile->node(directededge->endnode().id()); + const NodeInfo* sn_info = tile->node(from_node); + + if (en_info->mode_change()) { + + // If this edge has special attributes, then we can't aggregate + if (!CanAggregate(directededge)) { + processed_nodes.insert(directededge->endnode()); + no_agg_ways.insert(edgeinfo.wayid()); + return; + } + + std::unordered_set isos; + bool isForward = directededge->forward(); + auto id = directededge->endnode(); + + isos.insert(tile->admin(sn_info->admin_index())->country_iso()); // start node + isos.insert(tile->admin(en_info->admin_index())->country_iso()); // end node + + if (isos.size() > 1) { // already in diff country + processed_nodes.insert(directededge->endnode()); + return; + } + + // walk in the correct direction. + uint64_t wayid = edgeinfo.wayid(); + if (!Aggregate(id, reader, shape, en, from_node, wayid, isos, directededge->classification(), + isForward, true)) { + // LOG_WARN("ValidateData - failed to validate node. Will not aggregate."); + // for debugging only + // std::cout << "End node: " << directededge->endnode().value << " WayId: " << + // edgeinfo.wayid() + // << std::endl; + + if (wayid == 0) { // This edge has special attributes, we can't aggregate + no_agg_ways.insert(edgeinfo.wayid()); + } + + processed_nodes.insert(directededge->endnode()); // turn off so that we don't fail + } else if (isos.size() > 1) { // in diff country + processed_nodes.insert(directededge->endnode()); + } + } + } +} + +void AggregateTiles(GraphReader& reader, std::unordered_map& old_to_new) { + + LOG_INFO("Validating edges for aggregation"); + // Iterate through all tiles in the local level + auto local_tiles = reader.GetTileSet(TileHierarchy::levels().back().level); + for (const auto& tile_id : local_tiles) { + // Get the graph tile. Read from this tile to create the new tile. + graph_tile_ptr tile = reader.GetGraphTile(tile_id); + assert(tile); + + std::unordered_set processed_nodes; + std::unordered_set no_agg_ways; + processed_nodes.reserve(tile->header()->nodecount()); + no_agg_ways.reserve(tile->header()->directededgecount()); + + GraphId nodeid = GraphId(tile_id.tileid(), tile_id.level(), 0); + for (uint32_t i = 0; i < tile->header()->nodecount(); ++i, ++nodeid) { + const NodeInfo* nodeinfo = tile->node(i); + uint32_t idx = nodeinfo->edge_index(); + for (uint32_t j = 0; j < nodeinfo->edge_count(); j++, idx++) { + const DirectedEdge* directededge = tile->directededge(idx); + if (processed_nodes.find(nodeid) == processed_nodes.end()) { + GraphId en = directededge->endnode(); + std::list shape; + // check if we can aggregate the edges at this node. + ValidateData(reader, shape, en, processed_nodes, no_agg_ways, nodeid, tile, directededge); + } + } + } + + // Now loop again double checking the ways. + nodeid = GraphId(tile_id.tileid(), tile_id.level(), 0); + for (uint32_t i = 0; i < tile->header()->nodecount(); ++i, ++nodeid) { + const NodeInfo* nodeinfo = tile->node(i); + uint32_t idx = nodeinfo->edge_index(); + for (uint32_t j = 0; j < nodeinfo->edge_count(); j++, idx++) { + const DirectedEdge* directededge = tile->directededge(idx); + if (no_agg_ways.find(tile->edgeinfo(directededge).wayid()) != no_agg_ways.end()) { + processed_nodes.insert(directededge->endnode()); + } + } + } + + // Create a new tile builder + GraphTileBuilder tilebuilder(reader.tile_dir(), tile_id, false); + std::vector nodes; + + // Copy edges (they do not change) + std::vector directededges; + size_t n = tile->header()->directededgecount(); + directededges.reserve(n); + const DirectedEdge* orig_edges = tile->directededge(0); + std::copy(orig_edges, orig_edges + n, std::back_inserter(directededges)); + + nodeid = GraphId(tile_id.tileid(), tile_id.level(), 0); + for (uint32_t i = 0; i < tile->header()->nodecount(); ++i, ++nodeid) { + NodeInfo nodeinfo = tilebuilder.node(i); + bool found = (processed_nodes.find(nodeid) != processed_nodes.end()); + + // We can not aggregate at this node. Turn off the mode change(aggregation) bit + if (found) { + nodeinfo.set_mode_change(false); + } + // Add the node to the local list + nodes.emplace_back(std::move(nodeinfo)); + } + tilebuilder.Update(nodes, directededges); + + if (reader.OverCommitted()) { + reader.Trim(); + } + } + + LOG_INFO("Aggregating edges"); + reader.Clear(); + // Iterate through all tiles in the local level + local_tiles = reader.GetTileSet(TileHierarchy::levels().back().level); + // Iterate through all tiles in the local level + for (const auto& tile_id : local_tiles) { + // Create a new tilebuilder - should copy header information + GraphTileBuilder tilebuilder(reader.tile_dir(), tile_id, false); + + // Get the graph tile. Read from this tile to create the new tile. + graph_tile_ptr tile = reader.GetGraphTile(tile_id); + assert(tile); + + std::hash hasher; + GraphId nodeid(tile_id.tileid(), tile_id.level(), 0); + for (uint32_t i = 0; i < tile->header()->nodecount(); ++i, ++nodeid) { + bool diff_names = false; + + // Count of edges added for this node + uint32_t edge_count = 0; + + // Current edge index for first edge from this node + uint32_t edge_index = tilebuilder.directededges().size(); + + // Iterate through directed edges outbound from this node + std::vector wayid; + std::vector endnode; + const NodeInfo* nodeinfo = tile->node(nodeid); + + // Nodes marked with mode_change = true are tossed. + if (nodeinfo->mode_change()) { + continue; + } + + GraphId edgeid(nodeid.tileid(), nodeid.level(), nodeinfo->edge_index()); + + for (uint32_t j = 0; j < nodeinfo->edge_count(); ++j, ++edgeid) { + // Check if the directed edge should be included + const DirectedEdge* directededge = tile->directededge(edgeid); + + // Copy the directed edge information + DirectedEdge newedge = *directededge; + + // Set opposing edge indexes to 0 (gets set in graph validator). + newedge.set_opp_index(0); + + // Get signs from the base directed edge + if (directededge->sign()) { + std::vector signs = tile->GetSigns(edgeid.id()); + if (signs.size() == 0) { + LOG_ERROR("Base edge should have signs, but none found"); + } + tilebuilder.AddSigns(tilebuilder.directededges().size(), signs); + } + + // Get turn lanes from the base directed edge + if (directededge->turnlanes()) { + uint32_t offset = tile->turnlanes_offset(edgeid.id()); + tilebuilder.AddTurnLanes(tilebuilder.directededges().size(), tile->GetName(offset)); + } + + // Get access restrictions from the base directed edge. Add these to + // the list of access restrictions in the new tile. Update the + // edge index in the restriction to be the current directed edge Id + if (directededge->access_restriction()) { + auto restrictions = tile->GetAccessRestrictions(edgeid.id(), kAllAccess); + for (const auto& res : restrictions) { + tilebuilder.AddAccessRestriction(AccessRestriction(tilebuilder.directededges().size(), + res.type(), res.modes(), res.value())); + } + } + + // Copy lane connectivity + if (directededge->laneconnectivity()) { + auto laneconnectivity = tile->GetLaneConnectivity(edgeid.id()); + if (laneconnectivity.size() == 0) { + LOG_ERROR("Base edge should have lane connectivity, but none found"); + } + for (auto& lc : laneconnectivity) { + lc.set_to(tilebuilder.directededges().size()); + } + tilebuilder.AddLaneConnectivity(laneconnectivity); + } + + // Names can be different in the forward and backward direction + diff_names = tilebuilder.OpposingEdgeInfoDiffers(tile, directededge); + + const auto& edgeinfo = tile->edgeinfo(directededge); + std::string encoded_shape = edgeinfo.encoded_shape(); + std::list shape = valhalla::midgard::decode7>(encoded_shape); + + // Aggregate if end node is marked and in same tile + bool aggregated = false; + GraphId en = directededge->endnode(); + + if (en.tile_value() == tile_id) { + if (tile->node(en.id())->mode_change()) { + GetAggregatedData(reader, shape, en, nodeid, tile, directededge); + newedge.set_endnode(en); + aggregated = true; + } + } + + // Hammerhead specific. bike network not saved to edgeinfo + bool added; + encoded_shape = encode7(shape); + uint32_t w = hasher(encoded_shape + std::to_string(edgeinfo.wayid())); + uint32_t edge_info_offset = + tilebuilder.AddEdgeInfo(w, nodeid, en, edgeinfo.wayid(), edgeinfo.mean_elevation(), + edgeinfo.bike_network(), edgeinfo.speed_limit(), encoded_shape, + edgeinfo.GetNames(), edgeinfo.GetTaggedValues(), + edgeinfo.GetLinguisticTaggedValues(), edgeinfo.GetTypes(), added, + diff_names); + newedge.set_edgeinfo_offset(edge_info_offset); + + // Update length and curvature if the edge was aggregated + if (aggregated) { + newedge.set_length(valhalla::midgard::length(shape)); + newedge.set_curvature(compute_curvature(shape)); + } + + // Add directed edge + tilebuilder.directededges().emplace_back(std::move(newedge)); + ++edge_count; + } + + // Add the node to the tilebuilder unless no edges remain + if (edge_count > 0) { + // Add a node builder to the tile. Update the edge count and edgeindex + GraphId new_node(nodeid.tileid(), nodeid.level(), tilebuilder.nodes().size()); + tilebuilder.nodes().push_back(*nodeinfo); + NodeInfo& node = tilebuilder.nodes().back(); + node.set_edge_count(edge_count); + node.set_edge_index(edge_index); + const auto& admin = tile->admininfo(nodeinfo->admin_index()); + node.set_admin_index(tilebuilder.AddAdmin(admin.country_text(), admin.state_text(), + admin.country_iso(), admin.state_iso())); + + // Get named signs from the base node + if (nodeinfo->named_intersection()) { + std::vector signs = tile->GetSigns(nodeid.id(), true); + if (signs.size() == 0) { + LOG_ERROR("Base node should have signs, but none found"); + } + node.set_named_intersection(true); + tilebuilder.AddSigns(tilebuilder.nodes().size() - 1, signs); + } + // Associate the old node to the new node. + old_to_new[nodeid] = new_node; + } + } + + // Store the updated tile data (or remove tile if all edges are filtered) + if (tilebuilder.nodes().size() > 0) { + tilebuilder.StoreTileData(); + } else { + // Remove the tile - all nodes and edges were filtered + std::string file_location = + reader.tile_dir() + filesystem::path::preferred_separator + GraphTile::FileSuffix(tile_id); + remove(file_location.c_str()); + LOG_INFO("Remove file: " + file_location + " all edges were filtered"); + } + + if (reader.OverCommitted()) { + reader.Trim(); + } + } + + LOG_INFO("Aggregated " + std::to_string(aggregated) + " directededges out of " + + std::to_string(n_original_edges)); } /** @@ -224,9 +789,6 @@ void FilterTiles(GraphReader& reader, */ void UpdateEndNodes(GraphReader& reader, std::unordered_map& old_to_new) { LOG_INFO("Update end nodes of directed edges"); - - int found = 0; - // Iterate through all tiles in the local level auto local_tiles = reader.GetTileSet(TileHierarchy::levels().back().level); for (const auto& tile_id : local_tiles) { @@ -255,9 +817,10 @@ void UpdateEndNodes(GraphReader& reader, std::unordered_map& o auto iter = old_to_new.find(edge->endnode()); if (iter == old_to_new.end()) { LOG_ERROR("UpdateEndNodes - failed to find associated node"); + std::cout << std::to_string(edge->endnode().value) << " " + << std::to_string(tile->edgeinfo(edge).wayid()) << std::endl; } else { end_node = iter->second; - found++; } // Copy the edge to the directededges vector and update the end node @@ -275,6 +838,66 @@ void UpdateEndNodes(GraphReader& reader, std::unordered_map& o } } +/** + * Update Opposing Edge Index of all directed edges. + * @param reader Graph reader. + */ +void UpdateOpposingEdgeIndex(GraphReader& reader) { + LOG_INFO("Update Opposing Edge Index of directed edges"); + + // Iterate through all tiles in the local level + auto local_tiles = reader.GetTileSet(TileHierarchy::levels().back().level); + for (const auto& tile_id : local_tiles) { + GraphTileBuilder tilebuilder(reader.tile_dir(), tile_id, false); + + // Get the graph tile. Read from this tile to create the new tile. + graph_tile_ptr tile = reader.GetGraphTile(tile_id); + assert(tile); + + // Copy nodes (they do not change) + std::vector nodes; + size_t n = tile->header()->nodecount(); + nodes.reserve(n); + const NodeInfo* orig_nodes = tile->node(0); + std::copy(orig_nodes, orig_nodes + n, std::back_inserter(nodes)); + + // Iterate through all directed edges - update end nodes + std::vector directededges; + + GraphId nodeid(tile_id.tileid(), tile_id.level(), 0); + for (uint32_t i = 0; i < tile->header()->nodecount(); ++i, ++nodeid) { + const NodeInfo* nodeinfo = tile->node(nodeid); + GraphId edgeid(nodeid.tileid(), nodeid.level(), nodeinfo->edge_index()); + for (uint32_t j = 0; j < nodeinfo->edge_count(); ++j, ++edgeid) { + // Check if the directed edge should be included + const DirectedEdge* edge = tile->directededge(edgeid); + + // Copy the edge to the directededges vector and update the end node + directededges.push_back(*edge); + DirectedEdge& new_edge = directededges.back(); + + // Get the tile at the end node + graph_tile_ptr endnodetile; + if (tile->id() == edge->endnode().Tile_Base()) { + endnodetile = tile; + } else { + endnodetile = reader.GetGraphTile(edge->endnode()); + } + + // Set the opposing index on the local level + new_edge.set_opp_local_idx(GetOpposingEdgeIndex(endnodetile, nodeid, tile, *edge)); + } + } + + // Update the tile with new directededges. + tilebuilder.Update(nodes, directededges); + + if (reader.OverCommitted()) { + reader.Trim(); + } + } +} + } // namespace namespace valhalla { @@ -313,13 +936,22 @@ void GraphFilter::Filter(const boost::property_tree::ptree& pt) { // Filter edges (and nodes) by access FilterTiles(reader, old_to_new, include_driving, include_bicycle, include_pedestrian); - // TODO - aggregate / combine edges across false nodes (only 2 directed edges) - // where way Ids are equal + // Update end nodes. Clear the GraphReader cache first. + reader.Clear(); + UpdateEndNodes(reader, old_to_new); + + reader.Clear(); + old_to_new.clear(); + AggregateTiles(reader, old_to_new); // Update end nodes. Clear the GraphReader cache first. reader.Clear(); UpdateEndNodes(reader, old_to_new); + // Update Opposing Edge Index. Clear the GraphReader cache first. + reader.Clear(); + UpdateOpposingEdgeIndex(reader); + LOG_INFO("Done GraphFilter"); } diff --git a/src/mjolnir/graphtilebuilder.cc b/src/mjolnir/graphtilebuilder.cc index ba2a910bb5..3abbfafa71 100644 --- a/src/mjolnir/graphtilebuilder.cc +++ b/src/mjolnir/graphtilebuilder.cc @@ -11,6 +11,9 @@ #include #include +#include "baldr/directededge.h" +#include "baldr/graphconstants.h" + using namespace valhalla::baldr; namespace valhalla { @@ -154,10 +157,11 @@ GraphTileBuilder::GraphTileBuilder(const std::string& tile_dir, // Edge bins are gotten by parent - // Create an ordered set of edge info offsets - std::set edge_info_offsets; + // Create an ordered map with edge info offsets as the key and the edge length + // as the value. Length is needed so elevation data can be read (if present). + std::map edge_info_offsets; for (auto& diredge : directededges_builder_) { - edge_info_offsets.insert(diredge.edgeinfo_offset()); + edge_info_offsets[diredge.edgeinfo_offset()] = diredge.length(); } // At this time, complex restrictions are created AFTER all need for @@ -171,14 +175,18 @@ GraphTileBuilder::GraphTileBuilder(const std::string& tile_dir, // EdgeInfo. Create list of EdgeInfoBuilders. Add to text offset set. edge_info_offset_ = 0; edgeinfo_offset_map_.clear(); - for (auto offset : edge_info_offsets) { + for (auto edgemap : edge_info_offsets) { + auto offset = edgemap.first; + // Verify the offsets match as we create the edge info builder list if (offset != edge_info_offset_) { - LOG_WARN("GraphTileBuilder TileID: " + std::to_string(header_->graphid().tileid()) + - " offset stored in directed edge: = " + std::to_string(offset) + - " current ei offset= " + std::to_string(edge_info_offset_)); + LOG_ERROR("GraphTileBuilder TileID: " + std::to_string(header_->graphid().tileid()) + + " offset stored in directed edge: = " + std::to_string(offset) + + " current ei offset= " + std::to_string(edge_info_offset_)); + throw std::runtime_error("EdgeInfo offsets incorrect when reading GraphTile"); } + // At this time, encoded elevation is empty and does not need to be serialized... EdgeInfo ei(edgeinfo_ + offset, textlist_, textlist_size_); EdgeInfoBuilder eib; eib.set_wayid(ei.wayid()); @@ -191,6 +199,14 @@ GraphTileBuilder::GraphTileBuilder(const std::string& tile_dir, eib.AddNameInfo(info); } eib.set_encoded_shape(ei.encoded_shape()); + + // Add encoded elevation (if present) + if (ei.has_elevation()) { + auto length = edgemap.second; + double interval = 0.0f; + eib.set_encoded_elevation(ei.encoded_elevation(length, interval)); + } + edge_info_offset_ += eib.SizeOf(); edgeinfo_list_.emplace_back(std::move(eib)); @@ -344,15 +360,17 @@ void GraphTileBuilder::StoreTileData() { reverse_restriction_size += complex_restriction.SizeOf(); } - // Write the edge data + // Write the edge data (update edge_info_offset_) + int64_t current_size = in_mem.tellp(); header_builder_.set_edgeinfo_offset(header_builder_.complex_restriction_reverse_offset() + reverse_restriction_size); for (const auto& edgeinfo : edgeinfo_list_) { in_mem << edgeinfo; } + int64_t edge_info_size = in_mem.tellp() - current_size; // Write the names - header_builder_.set_textlist_offset(header_builder_.edgeinfo_offset() + edge_info_offset_); + header_builder_.set_textlist_offset(header_builder_.edgeinfo_offset() + edge_info_size); for (const auto& text : textlistbuilder_) { in_mem << text << '\0'; } @@ -508,35 +526,44 @@ void GraphTileBuilder::AddAccessRestrictions(const std::vector& signs, - const std::vector& pronunciations) { + const std::vector& linguistics) { // Iterate through the list of sign info (with sign text) and add sign // text to the text list. Skip signs with no text. + + auto process_linguistic_header = [](const uint32_t ling_start_index, const uint32_t ling_count, + const std::vector& linguistics, + const size_t index) { + std::string updated_linguistics; + for (uint32_t x = ling_start_index; x <= ling_count; x++) { + auto* p = const_cast(linguistics[x].c_str()); + + while (*p != '\0') { + linguistic_text_header_t header = midgard::unaligned_read(p); + + if (header.name_index_ == index) { + updated_linguistics.append( + std::string(reinterpret_cast(&header), kLinguisticHeaderSize) + + (p + kLinguisticHeaderSize)); + } + p += header.length_ + kLinguisticHeaderSize; + } + } + return updated_linguistics; + }; + for (size_t i = 0; i < signs.size(); ++i) { - auto sign = signs[i]; + const auto& sign = signs[i]; if (!(sign.text().empty())) { uint32_t offset = AddName(sign.text()); + signs_builder_.emplace_back(idx, sign.type(), sign.is_route_num(), sign.is_tagged(), offset); - if (sign.has_phoneme()) { - bool phoneme_on_node = sign.type() == Sign::Type::kJunctionName; - uint32_t count = (sign.phoneme_start_index() + sign.phoneme_count()) - 1; - for (uint32_t x = sign.phoneme_start_index(); x <= count; x++) { - auto* p = const_cast(pronunciations[x].c_str()); - size_t pos = 0; - std::string updated_pronunciation; - - while (pos < strlen(p)) { - linguistic_text_header_t header = - midgard::unaligned_read(p + pos); - pos += 3; - header.name_index_ = i; - updated_pronunciation.append(std::string(reinterpret_cast(&header), 3) + - (p + pos)); - pos += header.length_; - } - - uint32_t offset = AddName(updated_pronunciation); - signs_builder_.emplace_back(idx, Sign::Type::kPronunciation, phoneme_on_node, true, offset); - } + if (sign.has_linguistic()) { + bool linguistic_on_node = + sign.type() == Sign::Type::kJunctionName || (sign.type() == Sign::Type::kTollName); + uint32_t count = (sign.linguistic_start_index() + sign.linguistic_count()) - 1; + uint32_t offset = + AddName(process_linguistic_header(sign.linguistic_start_index(), count, linguistics, i)); + signs_builder_.emplace_back(idx, Sign::Type::kLinguistic, linguistic_on_node, true, offset); } } } @@ -584,19 +611,45 @@ bool GraphTileBuilder::HasEdgeInfo(const uint32_t edgeindex, return false; } +void GraphTileBuilder::ProcessTaggedValues([[maybe_unused]] const uint32_t edgeindex, + const std::vector& names, + size_t& name_count, + std::vector& name_info_list) { + auto encode_tag = + std::string(1, static_cast(valhalla::baldr::TaggedValue::kLinguistic)); + if (names.size()) { + if (name_count != kMaxNamesPerEdge) { + std::stringstream ss; + for (const auto& name : names) { + ss << name; + } + + // Add linguistics and add its offset to edge info's list. + NameInfo ni{AddName(encode_tag + ss.str())}; + + ni.is_route_num_ = 0; + ni.tagged_ = 1; + name_info_list.emplace_back(ni); + ++name_count; + } else { + LOG_WARN("Too many names for edgeindex: " + std::to_string(edgeindex)); + } + } +} + // Add edge info template uint32_t GraphTileBuilder::AddEdgeInfo(const uint32_t edgeindex, - const GraphId& nodea, - const baldr::GraphId& nodeb, + baldr::GraphId nodea, + baldr::GraphId nodeb, const uint64_t wayid, const float elev, - const uint32_t bike_network, - const uint32_t speed_limit, + const uint32_t bn, + const uint32_t spd, const shape_container_t& lls, const std::vector& names, const std::vector& tagged_values, - const std::vector& pronunciations, + const std::vector& linguistics, const uint16_t types, bool& added, bool diff_names) { @@ -609,8 +662,8 @@ uint32_t GraphTileBuilder::AddEdgeInfo(const uint32_t edgeindex, EdgeInfoBuilder& edgeinfo = edgeinfo_list_.back(); edgeinfo.set_wayid(wayid); edgeinfo.set_mean_elevation(elev); - edgeinfo.set_bike_network(bike_network); - edgeinfo.set_speed_limit(speed_limit); + edgeinfo.set_bike_network(bn); + edgeinfo.set_speed_limit(spd); edgeinfo.set_shape(lls); // Add names to the common text/name list. Skip blank names. @@ -657,27 +710,7 @@ uint32_t GraphTileBuilder::AddEdgeInfo(const uint32_t edgeindex, } } - if (pronunciations.size()) { - if (name_count != kMaxNamesPerEdge) { - std::stringstream ss; - for (const auto& pronunciation : pronunciations) { - ss << pronunciation; - } - - auto encode_tag = [](valhalla::baldr::TaggedValue tag) { - return std::string(1, static_cast(tag)); - }; - - // Add pronunciations and add its offset to edge info's list. - NameInfo ni{AddName(encode_tag(valhalla::baldr::TaggedValue::kPronunciation) + ss.str())}; - - ni.is_route_num_ = 0; - ni.tagged_ = 1; - name_info_list.emplace_back(ni); - ++name_count; - } else - LOG_WARN("Too many names for edgeindex: " + std::to_string(edgeindex)); - } + ProcessTaggedValues(edgeindex, linguistics, name_count, name_info_list); edgeinfo.set_name_info_list(name_info_list); @@ -699,9 +732,10 @@ uint32_t GraphTileBuilder::AddEdgeInfo(const uint32_t edgeindex, added = false; return existing_edge_offset_item->second; } + template uint32_t GraphTileBuilder::AddEdgeInfo>(const uint32_t edgeindex, - const GraphId&, - const baldr::GraphId&, + GraphId, + GraphId, const uint64_t, const float, const uint32_t, @@ -714,8 +748,8 @@ template uint32_t GraphTileBuilder::AddEdgeInfo>(const uint bool&, bool); template uint32_t GraphTileBuilder::AddEdgeInfo>(const uint32_t edgeindex, - const GraphId&, - const baldr::GraphId&, + GraphId, + baldr::GraphId, const uint64_t, const float, const uint32_t, @@ -730,16 +764,16 @@ template uint32_t GraphTileBuilder::AddEdgeInfo>(const uint32 // AddEdgeInfo - accepts an encoded shape string. uint32_t GraphTileBuilder::AddEdgeInfo(const uint32_t edgeindex, - const baldr::GraphId& nodea, - const baldr::GraphId& nodeb, + baldr::GraphId nodea, + baldr::GraphId nodeb, const uint64_t wayid, const float elev, - const uint32_t bike_network, - const uint32_t speed_limit, + const uint32_t bn, + const uint32_t spd, const std::string& llstr, const std::vector& names, const std::vector& tagged_values, - const std::vector& pronunciations, + const std::vector& linguistics, const uint16_t types, bool& added, bool diff_names) { @@ -752,8 +786,8 @@ uint32_t GraphTileBuilder::AddEdgeInfo(const uint32_t edgeindex, EdgeInfoBuilder& edgeinfo = edgeinfo_list_.back(); edgeinfo.set_wayid(wayid); edgeinfo.set_mean_elevation(elev); - edgeinfo.set_bike_network(bike_network); - edgeinfo.set_speed_limit(speed_limit); + edgeinfo.set_bike_network(bn); + edgeinfo.set_speed_limit(spd); edgeinfo.set_encoded_shape(llstr); // Add names to the common text/name list. Skip blank names. @@ -800,28 +834,7 @@ uint32_t GraphTileBuilder::AddEdgeInfo(const uint32_t edgeindex, } } - if (pronunciations.size()) { - if (name_count != kMaxNamesPerEdge) { - std::stringstream ss; - for (const auto& pronunciation : pronunciations) { - ss << pronunciation; - } - - auto encode_tag = [](valhalla::baldr::TaggedValue tag) { - return std::string(1, static_cast(tag)); - }; - - // Add pronunciations and add its offset to edge info's list. - NameInfo ni{AddName(encode_tag(valhalla::baldr::TaggedValue::kPronunciation) + ss.str())}; - - ni.is_route_num_ = 0; - ni.tagged_ = 1; - name_info_list.emplace_back(ni); - ++name_count; - } else - LOG_WARN("Too many names for edgeindex: " + std::to_string(edgeindex)); - } - + ProcessTaggedValues(edgeindex, linguistics, name_count, name_info_list); edgeinfo.set_name_info_list(name_info_list); // Add to the map @@ -849,15 +862,22 @@ void GraphTileBuilder::set_mean_elevation(const float elev) { edgeinfo.set_mean_elevation(elev); } -// Set the mean elevation to the EdgeInfo given the edge info offset. This requires -// a serialized tile builder. -void GraphTileBuilder::set_mean_elevation(const uint32_t offset, const float elev) { +// Set the mean elevation and encoded elevation along an edge to the EdgeInfo given the edge +// info offset. This requires a serialized tile builder. +uint32_t GraphTileBuilder::set_elevation(const uint32_t offset, + const float mean_elevation, + const std::vector& encoded_elevation) { auto e = edgeinfo_offset_map_.find(offset); if (e == edgeinfo_offset_map_.end()) { - LOG_ERROR("set_mean_elevation - could not find the EdgeInfo index given the offset"); - return; + LOG_ERROR("set_elevation - could not find the EdgeInfo index given the offset"); + return 0; } - e->second->set_mean_elevation(elev); + e->second->set_mean_elevation(mean_elevation); + if (!encoded_elevation.empty()) { + e->second->set_encoded_elevation(encoded_elevation); + e->second->set_has_elevation(true); + } + return e->second->SizeOf(); } // Add a name to the text list @@ -1258,5 +1278,82 @@ void GraphTileBuilder::UpdatePredictedSpeeds(const std::vector& di } } +void GraphTileBuilder::AddLandmark(const GraphId& edge_id, const Landmark& landmark) { + // check the edge id makes sense + if (header_builder_.graphid().Tile_Base() != edge_id.Tile_Base()) { + throw std::runtime_error( + "Can't add landmark: tile id or hierarchy level doesn't match with the current builder"); + } + if (header_builder_.directededgecount() <= edge_id.id()) { + throw std::runtime_error( + "Given edge doesn't exist: edge id is larger than total edge size in this tile"); + } + + // get the edge info / edge info builder + const auto& edge = directededges_builder_[edge_id.id()]; + const auto original_offset = edge.edgeinfo_offset(); + auto eib = edgeinfo_offset_map_.find(original_offset); + + if (eib == edgeinfo_offset_map_.end()) { + throw std::runtime_error("Couldn't find edge info for the given edge: " + + std::to_string(edge_id)); + } + + // get the value and prepend the tag to it + std::string tagged_value = landmark.to_str(); + tagged_value.insert(tagged_value.begin(), static_cast(baldr::TaggedValue::kLandmark)); + + auto name_offset = AddName(tagged_value); // where we are storing this tagged_value in the tile + // avoid adding existing landmark to edges (e.g. adding the same landmark to twin edges) + if (eib->second->has_name_info(name_offset)) { + return; + } + + // record in the edge info builder of where the tagged_value is + NameInfo ni{name_offset, 0, 0, 1}; + eib->second->AddNameInfo(ni); + + // update edge info offset + const auto shift = sizeof(ni); + edge_info_offset_ += shift; + + // update edgeinfo_offset for all directededges behind + for (auto& e : directededges_builder_) { + const auto offset = e.edgeinfo_offset(); + if (offset > original_offset) { + e.set_edgeinfo_offset(offset + shift); + } + } + + // update edgeinfo_offset_map by updating the offsets (keys) + // TODO: optimize this in a better way + std::unordered_map new_edgeinfo_offset_map_{}; + + for (auto& e : edgeinfo_offset_map_) { + if (e.first > original_offset) { + new_edgeinfo_offset_map_.emplace(e.first + shift, e.second); + } else { + new_edgeinfo_offset_map_.insert(e); + } + } + edgeinfo_offset_map_ = std::move(new_edgeinfo_offset_map_); +} + +bool GraphTileBuilder::OpposingEdgeInfoDiffers(const graph_tile_ptr& tile, const DirectedEdge* edge) { + if (edge->endnode().tile_value() == tile->header()->graphid().tile_value()) { + // Get the nodeinfo at the end of the edge. Iterate through the directed edges and return + // true if a matching edgeinfo offset if found. + const NodeInfo* nodeinfo = tile->node(edge->endnode().id()); + const DirectedEdge* de = tile->directededge(nodeinfo->edge_index()); + for (uint32_t i = 0; i < nodeinfo->edge_count(); i++, de++) { + // Return true if the edge info matches (same name, shape, etc.) + if (de->edgeinfo_offset() == edge->edgeinfo_offset()) { + return false; + } + } + } + return true; +} + } // namespace mjolnir } // namespace valhalla diff --git a/src/mjolnir/graphvalidator.cc b/src/mjolnir/graphvalidator.cc index 116b3dc509..4a111b2182 100644 --- a/src/mjolnir/graphvalidator.cc +++ b/src/mjolnir/graphvalidator.cc @@ -1,18 +1,14 @@ - #include "mjolnir/graphvalidator.h" #include "mjolnir/graphtilebuilder.h" #include "mjolnir/util.h" #include #include -#include #include #include #include -#include -#include +#include #include -#include #include #include #include diff --git a/src/mjolnir/hierarchybuilder.cc b/src/mjolnir/hierarchybuilder.cc index c29e813bc2..de0e9f6506 100644 --- a/src/mjolnir/hierarchybuilder.cc +++ b/src/mjolnir/hierarchybuilder.cc @@ -1,13 +1,8 @@ #include "mjolnir/hierarchybuilder.h" #include "mjolnir/graphtilebuilder.h" -#include #include -#include -#include -#include -#include #include #include #include @@ -101,26 +96,6 @@ OldToNewNodes find_nodes(sequence& old_to_new, const GraphId& nod } } -/** - * Is there an opposing edge with matching edgeinfo offset. The end node of the directed edge - * must be in the same tile as the directed edge. - * @param tile Graph tile of the edge - * @param directededge Directed edge to match. - */ -bool OpposingEdgeInfoMatches(const graph_tile_ptr& tile, const DirectedEdge* edge) { - // Get the nodeinfo at the end of the edge. Iterate through the directed edges and return - // true if a matching edgeinfo offset if found. - const NodeInfo* nodeinfo = tile->node(edge->endnode().id()); - const DirectedEdge* directededge = tile->directededge(nodeinfo->edge_index()); - for (uint32_t i = 0; i < nodeinfo->edge_count(); i++, directededge++) { - // Return true if the edge info matches (same name, shape, etc.) - if (directededge->edgeinfo_offset() == edge->edgeinfo_offset()) { - return true; - } - } - return false; -} - // Form tiles in the new level. void FormTilesInNewLevel(GraphReader& reader, const std::string& new_to_old_file, @@ -305,11 +280,8 @@ void FormTilesInNewLevel(GraphReader& reader, tilebuilder->AddLaneConnectivity(laneconnectivity); } - // Do we need to force adding edgeinfo (opposing edge could have diff names)? - // If end node is in the same tile and there is no opposing edge with matching - // edge_info_offset). - bool diff_names = directededge->endnode().tile_value() == base_edge_id.tile_value() && - !OpposingEdgeInfoMatches(tile, directededge); + // Names can be different in the forward and backward direction + bool diff_names = tilebuilder->OpposingEdgeInfoDiffers(tile, directededge); // Get edge info, shape, and names from the old tile and add to the // new. Cannot use edge info offset since edges in arterial and @@ -322,7 +294,7 @@ void FormTilesInNewLevel(GraphReader& reader, tilebuilder->AddEdgeInfo(w, nodea, nodeb, edgeinfo.wayid(), edgeinfo.mean_elevation(), edgeinfo.bike_network(), edgeinfo.speed_limit(), encoded_shape, edgeinfo.GetNames(), edgeinfo.GetTaggedValues(), - edgeinfo.GetTaggedValues(true), edgeinfo.GetTypes(), added, + edgeinfo.GetLinguisticTaggedValues(), edgeinfo.GetTypes(), added, diff_names); newedge.set_edgeinfo_offset(edge_info_offset); diff --git a/src/mjolnir/idtable.h b/src/mjolnir/idtable.h index d02f5b8dad..9ca8eaa3db 100644 --- a/src/mjolnir/idtable.h +++ b/src/mjolnir/idtable.h @@ -5,7 +5,6 @@ #include #include #include -#include #include @@ -71,7 +70,7 @@ class UnorderedIdTable final { /** * Deserializes the table from file * @param file_name the file from which to deserialize the table - * @return true if it was succesfully deserialized + * @return true if it was successfully deserialized */ bool deserialize(const std::string& file_name) { std::ifstream file(file_name, std::ios::in | std::ios::binary | std::ios::ate); diff --git a/src/mjolnir/ingest_transit.cc b/src/mjolnir/ingest_transit.cc index db2621241b..f63842b667 100644 --- a/src/mjolnir/ingest_transit.cc +++ b/src/mjolnir/ingest_transit.cc @@ -1,13 +1,10 @@ #include #include #include -#include #include -#include #include #include #include -#include #include #include #include @@ -15,10 +12,8 @@ #include #include -#include #include #include -#include #include "baldr/graphconstants.h" #include "baldr/graphid.h" @@ -170,13 +165,22 @@ std::priority_queue select_transit_tiles(const std::string& filesystem::recursive_directory_iterator end_file_itr; for (; gtfs_feed_itr != end_file_itr; ++gtfs_feed_itr) { const auto& feed_path = gtfs_feed_itr->path(); + if (gtfs_feed_itr->is_directory() && filesystem::is_empty(feed_path)) { + LOG_ERROR("Feed directory " + feed_path.string() + " is empty"); + continue; + } if (filesystem::is_directory(feed_path)) { // feed_path has a trailing separator const auto feed_name = feed_path.filename().string(); LOG_INFO("Loading " + feed_name); gtfs::Feed feed(feed_path.string()); - feed.read_feed(); + auto read_result = feed.read_feed(); + if (read_result.code != gtfs::ResultCode::OK) { + LOG_ERROR("Couldn't find a required file for feed " + feed_path.filename().string() + ": " + + read_result.message); + continue; + } LOG_INFO("Done loading, now parsing " + feed_name); const auto& stops = feed.get_stops(); @@ -207,13 +211,13 @@ std::priority_queue select_transit_tiles(const std::string& // we don't have the parent station, if // 1) this stop has none or 2) its parent station is in another tile // TODO: need to handle the 2nd case somehow! Fow now, log it as ERROR - auto parent_station = feed.get_stop(stop.parent_station); - if (parent_station && + const auto& parent_station = feed.get_stop(stop.parent_station); + if (gtfs::valid(parent_station) && tile_info.graphid != - GraphId(local_tiles.TileId(parent_station->stop_lat, parent_station->stop_lon), + GraphId(local_tiles.TileId(parent_station.stop_lat, parent_station.stop_lon), TileHierarchy::GetTransitLevel().level, 0)) { - LOG_WARN("Station ID " + stop.parent_station + " is not in stop's " + stop.stop_id + - " tile: " + std::to_string(tile_info.graphid)); + LOG_WARN("Station ID " + stop.parent_station + " is not in stop " + stop.stop_id + + "'s tile: " + std::to_string(tile_info.graphid)); } tile_info.stations.insert({stop.stop_id, feed_name}); tile_info.station_children.insert({{stop.stop_id, feed_name}, stop.stop_id}); @@ -223,18 +227,18 @@ std::priority_queue select_transit_tiles(const std::string& // add trip, route, agency and service_id from stop_time, it's the only place with that info // TODO: should we throw here? auto trip = feed.get_trip(stopTime.trip_id); - auto route = feed.get_route(trip->route_id); - if (!trip || !route || trip->service_id.empty()) { + auto route = feed.get_route(trip.route_id); + if (!gtfs::valid(trip) || !gtfs::valid(route) || trip.service_id.empty()) { LOG_ERROR("Missing trip or route or service_id for trip"); continue; } - tile_info.trips.insert({trip->trip_id, feed_name}); - tile_info.routes.insert({{route->route_id, feed_name}, tile_info.routes.size()}); + tile_info.trips.insert({trip.trip_id, feed_name}); + tile_info.routes.insert({{route.route_id, feed_name}, tile_info.routes.size()}); // shapes are optional, don't keep non-existing shapes around - if (!trip->shape_id.empty()) { - tile_info.shapes.insert({{trip->shape_id, feed_name}, tile_info.shapes.size()}); + if (!trip.shape_id.empty()) { + tile_info.shapes.insert({{trip.shape_id, feed_name}, tile_info.shapes.size()}); } } } @@ -285,7 +289,7 @@ void setup_stops(Transit& tile, // when this platform is not generated, we can use its actual given id verbatim because thats how // other gtfs entities will refer to it. however when it is generated we must use its parents id // and not the generated one because the references in the feed have no idea that we are doing - // the generation of new ideas for non-existant platforms + // the generation of new ideas for non-existent platforms platform_node_ids[{tile_stop.stop_id, feed_name}] = node_id; } node_id++; @@ -315,27 +319,27 @@ write_stops(Transit& tile, const tile_transit_info_t& tile_info, feed_cache_t& f for (const auto& child : tile_children) { auto child_stop = feed.get_stop(child.second); if (child.first.id == station.id && child.first.feed == station.feed && - child_stop->location_type == gtfs::StopLocationType::EntranceExit) { - setup_stops(tile, *child_stop, node_id, platform_node_ids, station.feed, + child_stop.location_type == gtfs::StopLocationType::EntranceExit) { + setup_stops(tile, child_stop, node_id, platform_node_ids, station.feed, NodeType::kTransitEgress, false); } } // We require an in/egress so if we didnt add one we need to fake one if (tile.nodes_size() == node_count) { - setup_stops(tile, *station_as_stop, node_id, platform_node_ids, station.feed, + setup_stops(tile, station_as_stop, node_id, platform_node_ids, station.feed, NodeType::kTransitEgress, true); } // Add the Station GraphId prev_id(tile.nodes(node_count).graphid()); - if (station_as_stop->location_type == gtfs::StopLocationType::Station) { + if (station_as_stop.location_type == gtfs::StopLocationType::Station) { // TODO(nils): what happens to the station if it was actually in another tile but still recorded // here (see above)?! - setup_stops(tile, *station_as_stop, node_id, platform_node_ids, station.feed, + setup_stops(tile, station_as_stop, node_id, platform_node_ids, station.feed, NodeType::kTransitStation, false, prev_id); } else { // if there was a platform/egress with no parent station, we add one - setup_stops(tile, *station_as_stop, node_id, platform_node_ids, station.feed, + setup_stops(tile, station_as_stop, node_id, platform_node_ids, station.feed, NodeType::kTransitStation, true, prev_id); } @@ -346,8 +350,8 @@ write_stops(Transit& tile, const tile_transit_info_t& tile_info, feed_cache_t& f auto child_stop = feed.get_stop(child.second); if (child.first.id == station.id && child.first.feed == station.feed && - child_stop->location_type == gtfs::StopLocationType::StopOrPlatform) { - setup_stops(tile, *child_stop, node_id, platform_node_ids, station.feed, + child_stop.location_type == gtfs::StopLocationType::StopOrPlatform) { + setup_stops(tile, child_stop, node_id, platform_node_ids, station.feed, NodeType::kMultiUseTransitPlatform, false, prev_id); } } @@ -357,8 +361,8 @@ write_stops(Transit& tile, const tile_transit_info_t& tile_info, feed_cache_t& f // to properly remove its associated station/egress IF they're not referenced // by other platforms. if (tile.nodes_size() == node_count) { - LOG_ERROR("Generated platform for station " + station_as_stop->stop_id); - setup_stops(tile, *station_as_stop, node_id, platform_node_ids, station.feed, + LOG_ERROR("Generated platform for station " + station_as_stop.stop_id); + setup_stops(tile, station_as_stop, node_id, platform_node_ids, station.feed, NodeType::kMultiUseTransitPlatform, true, prev_id); } } @@ -367,12 +371,12 @@ write_stops(Transit& tile, const tile_transit_info_t& tile_info, feed_cache_t& f // read feed data per stop, given shape float get_stop_pair_dist(const gtfs::Stop& stop_connect, - const gtfs::Shape& trip_shape, + gtfs::ShapeRange trip_shape, const gtfs::StopTime& pointStopTime) { // check which segment would belong to which tile if (pointStopTime.shape_dist_traveled > 0) { return pointStopTime.shape_dist_traveled; - } else if (!trip_shape.size()) { + } else if (trip_shape.first == trip_shape.second) { return 0.f; } @@ -383,9 +387,9 @@ float get_stop_pair_dist(const gtfs::Stop& stop_connect, PointLL stopPoint = PointLL(stop_connect.stop_lon, stop_connect.stop_lat); projector_t project(stopPoint); - for (size_t segment = 0; segment < trip_shape.size() - 1; segment++) { - auto currOrigin = trip_shape[segment]; - auto currDest = trip_shape[segment + 1]; + for (; trip_shape.first < trip_shape.second - 1; ++trip_shape.first) { + auto currOrigin = *trip_shape.first; + auto currDest = *std::next(trip_shape.first); // TODO: we can use the trip_shape.shape_dist_traveled here too and early exit if it's there PointLL originPoint = PointLL(currOrigin.shape_pt_lon, currOrigin.shape_pt_lat); PointLL destPoint = PointLL(currDest.shape_pt_lon, currDest.shape_pt_lat); @@ -424,30 +428,33 @@ bool write_stop_pair( const std::string currFeedPath = feed_trip.feed; const auto& currTrip = feed.get_trip(tile_tripId); - const auto& trip_calendar = feed.get_calendar(currTrip->service_id); - const gtfs::CalendarDates& trip_calDates = feed.get_calendar_dates(currTrip->service_id); - if (!currTrip || !trip_calendar) { + const auto& trip_calendar = feed.get_calendar_item(currTrip.service_id); + auto trip_calDates = feed.get_calendar_dates(currTrip.service_id); + if (!gtfs::valid(currTrip) || !gtfs::valid(trip_calendar)) { LOG_ERROR("Feed " + feed_trip.feed + ", trip ID" + tile_tripId + " can't be found or has no calendar.txt entry, skipping..."); return false; } + uint8_t dow_mask = gtfs::availability(trip_calendar); + + auto currFrequencies = feed.get_frequencies(currTrip.trip_id); + // get the gtfs shape and our pbf shape_id if present - const auto& currShape = feed.get_shape(currTrip->shape_id); - auto pbf_shape_it = tile_info.shapes.find({currTrip->shape_id, feed_trip.feed}); + auto currShape = feed.get_shape(currTrip.shape_id); + auto pbf_shape_it = tile_info.shapes.find({currTrip.shape_id, feed_trip.feed}); // already sorted by stop_sequence - const auto tile_stopTimes = feed.get_stop_times_for_trip(tile_tripId); - - for (size_t stop_sequence = 0; stop_sequence < tile_stopTimes.size() - 1; stop_sequence++) { - const auto& origin_stopTime = tile_stopTimes[stop_sequence]; + auto tile_stopTimes = feed.get_stop_times_for_trip(tile_tripId); + for (; tile_stopTimes.first < tile_stopTimes.second - 1; ++tile_stopTimes.first) { + const auto& origin_stopTime = *tile_stopTimes.first; const auto& origin_stopId = origin_stopTime.stop_id; const auto& origin_stop = feed.get_stop(origin_stopId); - assert(origin_stop); - const auto& dest_stopTime = tile_stopTimes[stop_sequence + 1]; + assert(gtfs::valid(origin_stop)); + const auto& dest_stopTime = *std::next(tile_stopTimes.first); const auto& dest_stopId = dest_stopTime.stop_id; const auto& dest_stop = feed.get_stop(dest_stopId); - assert(dest_stop); + assert(gtfs::valid(dest_stop)); const auto origin_graphid_it = platform_node_ids.find({origin_stopId, currFeedPath}); const auto dest_graphid_it = platform_node_ids.find({dest_stopId, currFeedPath}); const bool origin_is_in_tile = origin_graphid_it != platform_node_ids.end(); @@ -474,18 +481,18 @@ bool write_stop_pair( // add information from calendar.txt and calendar_dates.txt auto* service_dow = stop_pair->mutable_service_days_of_week(); - service_dow->Add(trip_calendar->monday == gtfs::CalendarAvailability::Available); - service_dow->Add(trip_calendar->tuesday == gtfs::CalendarAvailability::Available); - service_dow->Add(trip_calendar->wednesday == gtfs::CalendarAvailability::Available); - service_dow->Add(trip_calendar->thursday == gtfs::CalendarAvailability::Available); - service_dow->Add(trip_calendar->friday == gtfs::CalendarAvailability::Available); - service_dow->Add(trip_calendar->saturday == gtfs::CalendarAvailability::Available); - service_dow->Add(trip_calendar->sunday == gtfs::CalendarAvailability::Available); + service_dow->Add(gtfs::Monday & dow_mask); + service_dow->Add(gtfs::Tuesday & dow_mask); + service_dow->Add(gtfs::Wednesday & dow_mask); + service_dow->Add(gtfs::Thursday & dow_mask); + service_dow->Add(gtfs::Friday & dow_mask); + service_dow->Add(gtfs::Saturday & dow_mask); + service_dow->Add(gtfs::Sunday & dow_mask); bool had_added_date = false; - for (const auto& cal_date_item : trip_calDates) { - auto d = to_local_pivot_sec(cal_date_item.date.get_raw_date()); - if (cal_date_item.exception_type == gtfs::CalendarDateException::Added) { + for (auto cal_itr = trip_calDates.first; cal_itr != trip_calDates.second; ++cal_itr) { + auto d = to_local_pivot_sec(cal_itr->date.get_raw_date()); + if (cal_itr->exception_type == gtfs::CalendarDateException::Added) { stop_pair->add_service_added_dates(d); had_added_date = true; } else @@ -494,8 +501,8 @@ bool write_stop_pair( // this shouldn't happen, but let's make sure it doesn't // in convert_transit we'll check if there was a valid date for this service and skip if not - if (!service_dow->size() && !had_added_date) { - LOG_WARN("Service ID " + currTrip->service_id + + if (!dow_mask && !had_added_date) { + LOG_WARN("Service ID " + currTrip.service_id + " has no valid calendar or calendar_dates entry, skipping..."); tile.mutable_stop_pairs()->RemoveLast(); continue; @@ -504,21 +511,21 @@ bool write_stop_pair( // test this, but careful, we might have to adjust the test's dist_shape_traveled or whatever // for the test shapes to be a bit more realistic with the actual map where it travels much // further than the GTFS objects indicate - if (!currShape.empty()) { + if (currShape.first != currShape.second) { stop_pair->set_shape_id(pbf_shape_it->second); } - stop_pair->set_service_start_date(to_local_pivot_sec(trip_calendar->start_date.get_raw_date())); + stop_pair->set_service_start_date(to_local_pivot_sec(trip_calendar.start_date.get_raw_date())); // TODO: add a day worth of seconds - 1 to get the last second of that day stop_pair->set_service_end_date( - to_local_pivot_sec(trip_calendar->end_date.get_raw_date(), true)); + to_local_pivot_sec(trip_calendar.end_date.get_raw_date(), true)); dangles = dangles || !origin_is_in_tile || !dest_is_in_tile; - stop_pair->set_bikes_allowed(currTrip->bikes_allowed == gtfs::TripAccess::Yes); + stop_pair->set_bikes_allowed(currTrip.bikes_allowed == gtfs::TripAccess::Yes); - if (currTrip->block_id != "") { + if (currTrip.block_id != "") { uniques.lock.lock(); - auto inserted = uniques.block_ids.insert({currTrip->block_id, uniques.block_ids.size() + 1}); + auto inserted = uniques.block_ids.insert({currTrip.block_id, uniques.block_ids.size() + 1}); stop_pair->set_block_id(inserted.first->second); uniques.lock.unlock(); } @@ -530,10 +537,10 @@ bool write_stop_pair( stop_pair->set_origin_departure_time(origin_stopTime.departure_time.get_total_seconds()); // maybe set the dist_traveled - if (const auto dist = get_stop_pair_dist(*origin_stop, currShape, origin_stopTime)) { + if (const auto dist = get_stop_pair_dist(origin_stop, currShape, origin_stopTime)) { stop_pair->set_origin_dist_traveled(dist); } - if (const auto dist = get_stop_pair_dist(*dest_stop, currShape, dest_stopTime)) { + if (const auto dist = get_stop_pair_dist(dest_stop, currShape, dest_stopTime)) { stop_pair->set_destination_dist_traveled(dist); } @@ -554,30 +561,30 @@ bool write_stop_pair( } // set the proper route_index which will be referred to later in convert_transit - stop_pair->set_route_index(routes_ids.at({currTrip->route_id, currFeedPath})); + stop_pair->set_route_index(routes_ids.at({currTrip.route_id, currFeedPath})); // grab the headsign - stop_pair->set_trip_headsign(currTrip->trip_headsign); + stop_pair->set_trip_headsign(currTrip.trip_headsign); uniques.lock.lock(); // trips should never have ID=0, it messes up the triplegbuilder logic - auto inserted = uniques.trips.insert({currTrip->trip_id, uniques.trips.size() + 1}); + auto inserted = uniques.trips.insert({currTrip.trip_id, uniques.trips.size() + 1}); stop_pair->set_trip_id(inserted.first->second); uniques.lock.unlock(); - stop_pair->set_wheelchair_accessible(currTrip->wheelchair_accessible == gtfs::TripAccess::Yes); + stop_pair->set_wheelchair_accessible(currTrip.wheelchair_accessible == gtfs::TripAccess::Yes); // get frequency info - if (!feed.get_frequencies(currTrip->trip_id).empty()) { - const auto& currFrequencies = feed.get_frequencies(currTrip->trip_id); - if (currFrequencies.size() > 1) { + if (currFrequencies.first != currFrequencies.second) { + auto num_frequencies = std::distance(currFrequencies.first, currFrequencies.second); + if (num_frequencies > 1) { // TODO(nils): this should be properly handled as 1 trip id can have - // multiple frequencies, e.g. the example Google feed does - LOG_WARN("More than one frequencies based schedule for " + currTrip->trip_id); + // multiple frequencies, e.g. the example Google feed does + LOG_WARN("More than one frequencies based schedule for " + currTrip.trip_id); } - auto freq_start_time = (currFrequencies[0].start_time.get_raw_time()); - auto freq_end_time = (currFrequencies[0].end_time.get_raw_time()); + auto freq_start_time = (currFrequencies.first->start_time.get_raw_time()); + auto freq_end_time = (currFrequencies.first->end_time.get_raw_time()); auto freq_time = freq_start_time + freq_end_time; // TODO: check which type of frequency it is, could be exact_time = true (meaning schedule @@ -586,16 +593,14 @@ bool write_stop_pair( // (start_time is approximate, i.e. we don't know when the departure really is, only how long // it'll take) - if (currFrequencies.size() > 0) { - stop_pair->set_frequency_end_time(DateTime::seconds_from_midnight(freq_end_time)); - stop_pair->set_frequency_headway_seconds(currFrequencies[0].headway_secs); - } + stop_pair->set_frequency_end_time(DateTime::seconds_from_midnight(freq_end_time)); + stop_pair->set_frequency_headway_seconds(currFrequencies.first->headway_secs); auto line_id = stop_pair->origin_onestop_id() < stop_pair->destination_onestop_id() ? stop_pair->origin_onestop_id() + stop_pair->destination_onestop_id() + - currTrip->route_id + freq_time + currTrip.route_id + freq_time : stop_pair->destination_onestop_id() + stop_pair->origin_onestop_id() + - currTrip->route_id + freq_time; + currTrip.route_id + freq_time; uniques.lock.lock(); uniques.lines.insert({line_id, uniques.lines.size()}); uniques.lock.unlock(); @@ -622,23 +627,23 @@ write_routes(Transit& tile, const tile_transit_info_t& tile_info, feed_cache_t& auto* route = tile.add_routes(); auto currRoute = feed.get_route(tile_routeId); - route->set_name(currRoute->route_short_name); - route->set_onestop_id(get_onestop_id_base(currRoute->route_id, feed_route.first.feed)); + route->set_name(currRoute.route_short_name); + route->set_onestop_id(get_onestop_id_base(currRoute.route_id, feed_route.first.feed)); route->set_operated_by_onestop_id( - get_onestop_id_base(currRoute->agency_id, feed_route.first.feed)); + get_onestop_id_base(currRoute.agency_id, feed_route.first.feed)); - auto currAgency = feed.get_agency(currRoute->agency_id); - route->set_operated_by_name(currAgency->agency_name); - route->set_operated_by_website(currAgency->agency_url); + auto currAgency = feed.get_agency(currRoute.agency_id); + route->set_operated_by_name(currAgency.agency_name); + route->set_operated_by_website(currAgency.agency_url); // TODO(nils): add operated_by_onestop_id to the route, convert transit sets it and it's // used for filtering - route->set_route_color(strtol(currRoute->route_color.c_str(), nullptr, 16)); - route->set_route_desc(currRoute->route_desc); - route->set_route_long_name(currRoute->route_long_name); - route->set_route_text_color(strtol(currRoute->route_text_color.c_str(), nullptr, 16)); + route->set_route_color(strtol(currRoute.route_color.c_str(), nullptr, 16)); + route->set_route_desc(currRoute.route_desc); + route->set_route_long_name(currRoute.route_long_name); + route->set_route_text_color(strtol(currRoute.route_text_color.c_str(), nullptr, 16)); route->set_vehicle_type( - (valhalla::mjolnir::Transit_VehicleType)(static_cast(currRoute->route_type))); + (valhalla::mjolnir::Transit_VehicleType)(static_cast(currRoute.route_type))); routes_ids.emplace(feed_route.first, idx); idx++; @@ -655,11 +660,11 @@ void write_shapes(Transit& tile, const tile_transit_info_t& tile_info, feed_cach const auto& tile_shape = feed_shape.first.id; const auto& feed = feeds(feed_shape.first); auto* shape = tile.add_shapes(); - const gtfs::Shape& currShape = feed.get_shape(tile_shape, true); + auto currShape = feed.get_shape(tile_shape); shape->set_shape_id(feed_shape.second); std::vector trip_shape; - for (const auto& shape_pt : currShape) { - trip_shape.emplace_back(PointLL(shape_pt.shape_pt_lon, shape_pt.shape_pt_lat)); + for (; currShape.first != currShape.second; ++currShape.first) { + trip_shape.emplace_back(PointLL(currShape.first->shape_pt_lon, currShape.first->shape_pt_lat)); } shape->set_encoded_shape(encode7(trip_shape)); } @@ -874,6 +879,9 @@ std::list ingest_transit(const boost::property_tree::ptree& pt) { // go get information about what transit tiles we should be fetching LOG_INFO("Tiling GTFS Feeds"); auto tiles = select_transit_tiles(gtfs_dir); + if (tiles.empty()) { + throw std::runtime_error("Couldn't find any usable GTFS feeds."); + } LOG_INFO("Writing " + std::to_string(tiles.size()) + " transit pbf tiles with " + std::to_string(thread_count) + " threads..."); diff --git a/src/mjolnir/landmarks.cc b/src/mjolnir/landmarks.cc new file mode 100644 index 0000000000..9355c1e4ce --- /dev/null +++ b/src/mjolnir/landmarks.cc @@ -0,0 +1,569 @@ +#include "mjolnir/landmarks.h" +#include "filesystem.h" + +#include "baldr/graphreader.h" +#include "midgard/sequence.h" +#include "mjolnir/osmpbfparser.h" +#include "mjolnir/util.h" + +#include "baldr/location.h" +#include "baldr/pathlocation.h" +#include "baldr/tilehierarchy.h" +#include "loki/search.h" +#include "mjolnir/graphtilebuilder.h" +#include "sif/nocost.h" + +#include +#include +#include + +using namespace valhalla::baldr; +using namespace valhalla::mjolnir; +using namespace valhalla::midgard; +using namespace valhalla; + +namespace { +// a 25m radius used to associate edges to landmarks, which allows us to only keep the close edges in +// the tight cities +constexpr unsigned long kLandmarkRadius = 25; +// a 75m search cutoff used to associate edges to landmarks, which should allow us to get gas stations +// that are off the road a bit (for parking) +constexpr float kLandmarkSearchCutoff = 75.; +// a slight buffer to add to landmark queries to avoid near misses in the data due to precision +constexpr double kLandmarkQueryBuffer = .000001; + +struct landmark_callback : public OSMPBF::Callback { +public: + landmark_callback(const std::string& db_name) : db_(db_name, false) { + } + virtual ~landmark_callback() { + } + + virtual void + node_callback(const uint64_t /*osmid*/, double lng, double lat, const OSMPBF::Tags& tags) override { + auto iter = tags.find("amenity"); + if (iter != tags.cend() && !iter->second.empty()) { + try { + auto landmark_type = string_to_landmark_type(iter->second); + + std::string name = ""; + auto it = tags.find("name"); + if (it != tags.cend() && !it->second.empty()) { + name = it->second; + } + + // insert parsed landmark directly into database + db_.insert_landmark(name, landmark_type, lng, lat); + } catch (...) {} + } + } + + virtual void changeset_callback(const uint64_t /*changeset_id*/) override { + LOG_WARN("landmark changeset callback shouldn't be called!"); + } + + virtual void way_callback(const uint64_t /*osmid*/, + const OSMPBF::Tags& /*tags*/, + const std::vector& /*nodes*/) override { + LOG_WARN("landmark way callback shouldn't be called!"); + } + + virtual void relation_callback(const uint64_t /*osmid*/, + const OSMPBF::Tags& /*tags*/, + const std::vector& /*members*/) override { + LOG_WARN("landmark relation callback shouldn't be called!"); + } + + valhalla::mjolnir::LandmarkDatabase db_; +}; + +// sort a sequence file to put the edges in the same tile together +bool sort_seq_file(const std::pair& a, const std::pair& b) { + if (a.first.Tile_Base() == b.first.Tile_Base()) { + return a.first.id() < b.first.id(); + } + return a.first.Tile_Base() < b.first.Tile_Base(); +} +} // namespace + +namespace valhalla { +namespace mjolnir { +// TODO: this can be a utility and be more generic with a few more options, we could make the prepared +// statements on the fly and retrievable by the caller, then anything in the code base that wants to +// use sqlite can make use of this utility class. for now its ok to be specific to landmarks though +struct LandmarkDatabase::db_pimpl { + sqlite3* db; + sqlite3_stmt* insert_stmt; + sqlite3_stmt* bounding_box_stmt; + std::shared_ptr spatial_lite; + bool vacuum_analyze = false; + + db_pimpl(const std::string& db_name, bool read_only) + : insert_stmt(nullptr), bounding_box_stmt(nullptr) { + // create parent directory if it doesn't exist + const filesystem::path parent_dir = filesystem::path(db_name).parent_path(); + if (!filesystem::exists(parent_dir) && !filesystem::create_directories(parent_dir)) { + throw std::runtime_error("Can't create parent directory " + parent_dir.string()); + } + + // figure out if we need to create database or can just open it up + auto flags = read_only ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; + if (!filesystem::exists(db_name)) { + if (read_only) + throw std::logic_error("Cannot open sqlite database in read-only mode if it does not exist"); + } else if (!read_only) { + filesystem::remove(db_name); + LOG_INFO("deleting existing landmark database " + db_name + ", creating a new one"); + } + + // get a connection to the database + auto ret = sqlite3_open_v2(db_name.c_str(), &db, flags, NULL); + if (ret != SQLITE_OK) { + throw std::runtime_error("Failed to open sqlite database: " + db_name); + } + + // loading spatiaLite as an extension + spatial_lite = make_spatialite_cache(db); + + // if the db was empty we need to initialize the schema + char* err_msg = nullptr; + if (flags & SQLITE_OPEN_CREATE) { + // make the table + const char* table = + "SELECT InitSpatialMetaData(1); CREATE TABLE IF NOT EXISTS landmarks (id INTEGER PRIMARY KEY, name TEXT, type TEXT)"; + ret = sqlite3_exec(db, table, NULL, NULL, &err_msg); + if (ret != SQLITE_OK) { + sqlite3_free(err_msg); + throw std::runtime_error("Sqlite table creation error: " + std::string(err_msg)); + } + + // add geom column + const char* geom = "SELECT AddGeometryColumn('landmarks', 'geom', 4326, 'POINT', 2)"; + ret = sqlite3_exec(db, geom, NULL, NULL, &err_msg); + if (ret != SQLITE_OK) { + sqlite3_free(err_msg); + throw std::runtime_error("Sqlite geom column creation error: " + std::string(err_msg)); + } + + // make the index + const char* index = "SELECT CreateSpatialIndex('landmarks', 'geom')"; + ret = sqlite3_exec(db, index, NULL, NULL, &err_msg); + if (ret != SQLITE_OK) { + sqlite3_free(err_msg); + throw std::runtime_error("Sqlite spatial index creation error: " + std::string(err_msg)); + } + + // prep the insert statement + const char* insert = + "INSERT INTO landmarks (name, type, geom) VALUES (?, ?, MakePoint(?, ?, 4326))"; + ret = sqlite3_prepare_v2(db, insert, strlen(insert), &insert_stmt, NULL); + if (ret != SQLITE_OK) + throw std::runtime_error("Sqlite prepared insert statement error: " + + std::string(sqlite3_errmsg(db))); + } + + // prep the select statement + const char* select = + "SELECT id, name, type, X(geom), Y(geom) FROM landmarks WHERE ST_Covers(BuildMbr(?, ?, ?, ?, 4326), geom)"; + ret = sqlite3_prepare_v2(db, select, strlen(select), &bounding_box_stmt, NULL); + if (ret != SQLITE_OK) { + throw std::runtime_error("Sqlite prepared select statement error: " + + std::string(sqlite3_errmsg(db))); + } + } + ~db_pimpl() { + char* err_msg = nullptr; + if (vacuum_analyze && sqlite3_exec(db, "VACUUM", NULL, NULL, &err_msg) != SQLITE_OK) { + sqlite3_free(err_msg); + LOG_ERROR("Sqlite vacuum error: " + std::string(err_msg)); + } + + if (vacuum_analyze && sqlite3_exec(db, "ANALYZE", NULL, NULL, &err_msg) != SQLITE_OK) { + sqlite3_free(err_msg); + LOG_ERROR("Sqlite analyze error: " + std::string(err_msg)); + } + + sqlite3_finalize(insert_stmt); + sqlite3_finalize(bounding_box_stmt); + sqlite3_close_v2(db); + } + std::string last_error() { + return std::string(sqlite3_errmsg(db)); + } +}; + +LandmarkDatabase::LandmarkDatabase(const std::string& db_name, bool read_only) + : pimpl(new db_pimpl(db_name, read_only)) { +} + +void LandmarkDatabase::insert_landmark(const std::string& name, + const LandmarkType& type, + const double lng, + const double lat) { + auto* insert_stmt = pimpl->insert_stmt; + if (!insert_stmt) + throw std::logic_error("Sqlite database connection is read-only"); + + sqlite3_reset(insert_stmt); + sqlite3_clear_bindings(insert_stmt); + + sqlite3_bind_text(insert_stmt, 1, name.c_str(), name.length(), SQLITE_STATIC); + sqlite3_bind_int(insert_stmt, 2, static_cast(type)); + sqlite3_bind_double(insert_stmt, 3, lng); + sqlite3_bind_double(insert_stmt, 4, lat); + + LOG_TRACE(sqlite3_expanded_sql(insert_stmt)); + if (sqlite3_step(insert_stmt) != SQLITE_DONE) + throw std::runtime_error("Sqlite could not insert landmark: " + pimpl->last_error()); + pimpl->vacuum_analyze = true; +} + +// get multiple landmarks by their ids +/** TODO: Currently this function dynamically creates query statement based on + * the number of provided primary keys. + * In the future, we may consider implementing a fix-sized batch retrieval approach, where + * multiple landmarks are retrieved in batches using a prepared SQL statement with a fixed + * number of placeholders (e.g., 10 question marks) to be filled with corresponding inputs. + * If the caller provides more than the fixed number of inputs, the function will automatically + * perform multiple batch retrieves. + */ +std::vector LandmarkDatabase::get_landmarks_by_ids(const std::vector& pkeys) { + // create the sql statement with inputs + std::string sql = "SELECT id, name, type, X(geom), Y(geom) FROM landmarks WHERE id IN ("; + for (size_t i = 0; i < pkeys.size(); ++i) { + if (i > 0) { + sql += ", "; + } + sql += std::to_string(static_cast(pkeys[i])); + } + sql += ")"; + + // callback for the sql query + auto populate_landmarks = [](void* data, int argc, char** argv, char** col_names) { + std::vector* landmarks = static_cast*>(data); + + int64_t landmark_id = static_cast(std::stoi(argv[0])); + const char* landmark_name = argv[1]; + int landmark_type = std::stoi(argv[2]); + double lng = std::stod(argv[3]); + double lat = std::stod(argv[4]); + + landmarks->emplace_back( + Landmark(landmark_id, landmark_name, static_cast(landmark_type), lng, lat)); + return 0; + }; + + std::vector landmarks; + char* err_msg = nullptr; + // execute query + int ret = sqlite3_exec(pimpl->db, sql.c_str(), populate_landmarks, &landmarks, &err_msg); + + // check for errors in the sql execution + if (ret != SQLITE_OK) { + throw std::runtime_error("Sqlite execution error: " + std::string(err_msg)); + } + + return landmarks; +} + +std::vector LandmarkDatabase::get_landmarks_by_bbox(const double minlng, + const double minlat, + const double maxlng, + const double maxlat) { + std::vector landmarks; + + auto* bounding_box_stmt = pimpl->bounding_box_stmt; + sqlite3_reset(bounding_box_stmt); + sqlite3_clear_bindings(bounding_box_stmt); + + sqlite3_bind_double(bounding_box_stmt, 1, minlng); + sqlite3_bind_double(bounding_box_stmt, 2, minlat); + sqlite3_bind_double(bounding_box_stmt, 3, maxlng); + sqlite3_bind_double(bounding_box_stmt, 4, maxlat); + + LOG_TRACE(sqlite3_expanded_sql(bounding_box_stmt)); + + int ret = sqlite3_step(bounding_box_stmt); + while (ret == SQLITE_ROW) { + auto landmark_id = static_cast(sqlite3_column_int64(bounding_box_stmt, 0)); + const char* name = reinterpret_cast(sqlite3_column_text(bounding_box_stmt, 1)); + int landmark_type = sqlite3_column_int(bounding_box_stmt, 2); + double lng = sqlite3_column_double(bounding_box_stmt, 3); + double lat = sqlite3_column_double(bounding_box_stmt, 4); + + landmarks.emplace_back( + Landmark(landmark_id, name, static_cast(landmark_type), lng, lat)); + + ret = sqlite3_step(bounding_box_stmt); + } + + if (ret != SQLITE_DONE && ret != SQLITE_OK) { + throw std::runtime_error("Sqlite could not query landmarks in bounding box: " + + pimpl->last_error()); + } + + return landmarks; +} + +bool BuildLandmarkFromPBF(const boost::property_tree::ptree& pt, + const std::vector& input_files) { + // parse pbf to get landmark nodes + const std::string db_name = pt.get("landmarks", ""); + landmark_callback callback(db_name); + + LOG_INFO("Parsing files..."); + // hold open all the files so that if something else (like diff application) + // needs to mess with them we wont have troubles with inodes changing underneath us + std::list file_handles; + for (const auto& input_file : input_files) { + file_handles.emplace_back(input_file, std::ios::binary); + if (!file_handles.back().is_open()) { + throw std::runtime_error("Unable to open: " + input_file); + } + } + + LOG_INFO("Parsing nodes and storing landmarks..."); + for (auto& file_handle : file_handles) { + OSMPBF::Parser::parse(file_handle, static_cast(OSMPBF::Interest::NODES), + callback); + } + + LOG_INFO("Successfully built landmark database from PBF"); + return true; +} + +// Find landmarks in the tiles and the edges correlated to each landmark, +// and return the sequence file name where we wrote the correlations +void FindLandmarkEdges(const boost::property_tree::ptree& pt, + const std::vector& tileset, + const size_t& thread_number, + const size_t& total_threads, + std::promise& seq_file_name) { + // Open the database and create a graph reader + const std::string db_name = pt.get("landmarks", ""); + + LandmarkDatabase db(db_name, true); + GraphReader reader(pt); + // create the sequence file + std::string file_name = "landmark_dump_" + std::to_string(thread_number); + midgard::sequence> seq_file(file_name, true); + + for (size_t i = 0; i < tileset.size(); ++i) { + // every i'th thread works on every i'th tile + if (i % total_threads == thread_number) { + // get landmarks in the tile + midgard::AABB2 bbox = baldr::TileHierarchy::GetGraphIdBoundingBox(tileset[i]); + + std::vector landmarks = db.get_landmarks_by_bbox(bbox.minx() - kLandmarkQueryBuffer, + bbox.miny() - kLandmarkQueryBuffer, + bbox.maxx() + kLandmarkQueryBuffer, + bbox.maxy() + kLandmarkQueryBuffer); + + // find and collect all nearby path locations for the landmarks + for (const auto& landmark : landmarks) { + baldr::Location landmark_location(midgard::PointLL{landmark.lng, landmark.lat}, + baldr::Location::StopType::BREAK, 0, 0, kLandmarkRadius); + landmark_location.search_cutoff_ = kLandmarkSearchCutoff; + + // call loki::Search to get nearby edges to each landmark + std::unordered_map result = + loki::Search({landmark_location}, reader, sif::CreateNoCost({})); + + // we only have one landmark as input so the return size should be no more than one + if (result.size() > 1) { + throw std::logic_error( + "Error occurred in finding nearby edges to a landmark. Result size is " + + std::to_string(result.size()) + ", but should be one or zero"); + } + // if the landmark should not be associated with any edge + if (result.size() == 0) { + continue; + } + + std::vector edges = result.begin()->second.edges; + // for each edge insert edgeid - landmark_pkey pair into the sequence file + // TODO: maybe do some filtering and only keep some of the edges it finds? (now we have the + // 75m search cutoff) + for (const auto& edge : edges) { + seq_file.push_back(std::make_pair(edge.id, landmark.id)); + } + } + } + } + + seq_file_name.set_value(file_name); +} + +// Update tiles to associate landmarks with edges, return some stats about the numbers of updated +// tiles, edges, and landmarks. NOTE: the input sequence file seq_file is passed by reference, but +// should not be modified by these threads. +void UpdateTiles(midgard::sequence>& seq_file, + const std::string& tile_dir, + const std::string& db_name, + const size_t& thread_number, + const size_t& total_threads, + std::promise>& stats) { + // open the database and initialize a unique pointer to graph tile builder + std::unique_ptr tile_builder_ptr = nullptr; + + LandmarkDatabase db(db_name, true); + + // stats to record how many tiles, edges and landmarks are updated + size_t updated_tiles = 0, updated_edges = 0, updated_landmarks = 0; + GraphId last_edge, last_tile; + + size_t tile_count = static_cast(-1); + // every i'th thread works on every i'th tile + for (auto it = seq_file.begin(); it != seq_file.end(); ++it) { + // if the current tile is not the same as the last one, increase counter by one + if ((*it).first.Tile_Base() != last_tile) { + last_tile = (*it).first.Tile_Base(); + tile_count++; + } + // decide whether this tile is a "every i'th tile". if not, the thread should skip it + if (tile_count % total_threads != thread_number) { + continue; + } + + // now this pair is on a "every i'th tile". the thread should process it. + + // if this pair is on a new tile, then store the previous tile and move to the new tile + if (!tile_builder_ptr || + tile_builder_ptr->header_builder().graphid().Tile_Base() != (*it).first.Tile_Base()) { + // store the previously updated tile + if (tile_builder_ptr) { + tile_builder_ptr->StoreTileData(); + updated_tiles++; + } + // reset the tile builder to this new tile + tile_builder_ptr.reset(new GraphTileBuilder(tile_dir, (*it).first.Tile_Base(), true)); + } + + // retrieve the landmark to be added + // TODO: in the future we can do batches of ids, though it will complicate the code it will likely + // speed up the processing + const std::vector landmark = + db.get_landmarks_by_ids({static_cast((*it).second)}); + if (landmark.size() != 1) { + throw std::logic_error("Incorrect result size " + std::to_string(landmark.size()) + + " of retrieved landmarks, which should be 1"); + } + // add the landmark to the tile + GraphId edge_id = (*it).first; + tile_builder_ptr->AddLandmark(edge_id, landmark[0]); + + // update the stats + updated_landmarks++; + // a single edge can have multiple landmarks + // record the number of unique edges updated (pairs with the same edge should appear consecutively + // in the sequence) + if (last_edge != (*it).first) { + updated_edges++; + last_edge = (*it).first; + } + } + // store the last updated tile + if (tile_builder_ptr) { + tile_builder_ptr->StoreTileData(); + updated_tiles++; + } + + // set the stats + stats.set_value(std::make_tuple(updated_tiles, updated_edges, updated_landmarks)); +} + +// Add all landmarks to tiles +bool AddLandmarks(const boost::property_tree::ptree& pt) { + LOG_INFO("Starting adding landmarks to tiles..."); + + const size_t num_threads = + pt.get("mjolnir.concurrency", std::thread::hardware_concurrency()); + const std::string db_name = pt.get_child("mjolnir").get("landmarks_db", ""); + + // get tile access + baldr::GraphReader reader(pt.get_child("mjolnir")); + + // get all tile ids and sort the tiles in descending order by size to balance the threads + // TODO: it is possible in a global tileset that we have coverage only at level 2 for some places + // and we'd still like to get landmarks there. we'll probably need to fix this. + auto tileset = reader.GetTileSet(1); + std::vector vec_tileset(tileset.begin(), + tileset.end()); // turn the unordered_set into a vector for sorting + + std::sort(vec_tileset.begin(), vec_tileset.end(), [&](const auto id_a, const auto id_b) { + return reader.GetGraphTile(id_a)->header()->nodecount() > + reader.GetGraphTile(id_b)->header()->nodecount(); + }); + + LOG_INFO("Finding landmarks and their correlated edges..."); + + std::vector> threads(num_threads); + std::vector> sequence_file_names(num_threads); + for (size_t i = 0; i < num_threads; ++i) { + threads[i].reset(new std::thread(FindLandmarkEdges, std::cref(pt.get_child("mjolnir")), + std::cref(vec_tileset), i, num_threads, + std::ref(sequence_file_names[i]))); + } + + // join all the threads and collect the sequence file names + for (auto& thread : threads) { + thread->join(); + } + + std::vector seq_names{}; + seq_names.reserve(sequence_file_names.size()); + for (std::promise& s : sequence_file_names) { + seq_names.push_back(s.get_future().get()); + } + + LOG_INFO("Sorting landmark edge pairs by tile..."); + + // concatenate all sequence files and sort the merged sequence file + std::string merged_seq_file = seq_names.back(); + seq_names.pop_back(); + + std::ofstream seq_file(merged_seq_file, std::ios_base::binary | std::ios_base::app); + for (std::string& s : seq_names) { + std::ifstream seq(s, std::ios_base::binary); + seq_file << seq.rdbuf(); + } + seq_file.close(); + + midgard::sequence> merged_sequence_file(merged_seq_file, false); + merged_sequence_file.sort(sort_seq_file); + + LOG_INFO("Updating tiles..."); + + // re-open the thread pool to update tiles + std::vector>> stats_info( + num_threads); // tiles, edges, landmarks + + const std::string tile_dir = reader.tile_dir(); + for (size_t i = 0; i < num_threads; ++i) { + // assume the data size that each thread processes doesn't affect performance a lot + threads[i].reset(new std::thread(UpdateTiles, std::ref(merged_sequence_file), tile_dir, db_name, + i, num_threads, std::ref(stats_info[i]))); + } + + for (auto& thread : threads) { + thread->join(); + } + + // collect and log the stats + size_t tiles = 0, edges = 0, landmarks = 0; + for (std::promise>& s : stats_info) { + std::tuple data = s.get_future().get(); + tiles += std::get<0>(data); + edges += std::get<1>(data); + landmarks += std::get<2>(data); + } + + LOG_INFO("Updated " + std::to_string(tiles) + " unique tiles, " + std::to_string(edges) + + " unique directed edges, and wrote " + std::to_string(landmarks) + + " landmarks (including repeated ones)"); + + return true; +} + +} // end namespace mjolnir +} // end namespace valhalla diff --git a/src/mjolnir/linkclassification.cc b/src/mjolnir/linkclassification.cc index 119a453ca7..d7551d0ceb 100644 --- a/src/mjolnir/linkclassification.cc +++ b/src/mjolnir/linkclassification.cc @@ -1,4 +1,3 @@ -#include #include #include #include @@ -45,7 +44,7 @@ struct LinkGraphNode { } }; -inline bool IsDriveableNonLink(const Edge& edge) { +inline bool IsdrivableNonLink(const Edge& edge) { return !edge.attributes.link && ((edge.fwd_access & kAutoAccess) || (edge.rev_access & kAutoAccess)) && edge.attributes.importance != kServiceClass; @@ -55,11 +54,11 @@ inline bool IsDriveForwardLink(const Edge& edge) { return edge.attributes.link && edge.attributes.driveforward; } -// Get the best classification for any driveable non-link edges from a node. +// Get the best classification for any drivable non-link edges from a node. uint32_t GetBestNonLinkClass(const std::map& edges) { uint32_t bestrc = kAbsurdRoadClass; for (const auto& edge : edges) { - if (IsDriveableNonLink(edge.first)) { + if (IsdrivableNonLink(edge.first)) { bestrc = std::min(bestrc, edge.first.attributes.importance); } } @@ -113,12 +112,12 @@ nodelist_t FormExitNodes(sequence& nodes, sequence& edges) { // If the node has a both links and non links at it auto bundle = collect_node_edges(node_itr, nodes, edges); if (bundle.node.link_edge_ && bundle.node.non_link_edge_) { - // Check if this node has a link edge that is driveable from the node + // Check if this node has a link edge that is drivable from the node for (const auto& edge : bundle.node_edges) { if (edge.first.attributes.link && (edge.first.attributes.driveforward)) { // Get the highest classification of non-link edges at this node. // Add to the exit node list if a valid classification...if no - // connecting edge is driveable the node will be skipped. + // connecting edge is drivable the node will be skipped. uint32_t rc = GetBestNonLinkClass(bundle.node_edges); if (rc < kMaxClassification) { exit_nodes[rc].push_back(node_itr); @@ -215,7 +214,7 @@ bool IsDestinationNode(const node_bundle& node, const WayTags& link, Data& data) return false; for (const auto& edge : node.node_edges) { - if (IsDriveableNonLink(edge.first)) { + if (IsdrivableNonLink(edge.first)) { const auto road = WayTags::Parse(*data.ways[edge.first.wayindex_], data.osmdata); if (IsTheSameRoad(road, link) || IsDestinationRoad(road, link)) return true; @@ -324,7 +323,7 @@ struct LinkGraphBuilder { // Expand link edges from the exit node for (const auto& startedge : exit_bundle.node_edges) { // Get the edge information. Skip non-link edges, link edges that are - // not driveable in the forward direction, and link edges already + // not drivable in the forward direction, and link edges already // tested for reclassification if (!IsDriveForwardLink(startedge.first) || startedge.first.attributes.reclass_link) { continue; @@ -519,7 +518,7 @@ struct RoadName { } }; -bool IsEdgeDriveableInDirection(uint32_t from_node, const Edge& edge, bool forward) { +bool IsEdgedrivableInDirection(uint32_t from_node, const Edge& edge, bool forward) { bool right_direction = true; if (forward) { right_direction = (edge.sourcenode_ == from_node && (edge.fwd_access & kAutoAccess)) || @@ -567,7 +566,7 @@ std::vector GoTowardsIntersection(uint32_t start_node, // looking for a non link edge with right direction for (const auto& node_edge : bundle.node_edges) { const Edge& edge = node_edge.first; - if (!edge.attributes.link && IsEdgeDriveableInDirection(node, edge, forward) && + if (!edge.attributes.link && IsEdgedrivableInDirection(node, edge, forward) && visited_nodes.find(EndNode(node, node_edge.first)) == visited_nodes.end()) { candidates.push_back(node_edge); } @@ -675,7 +674,7 @@ SlipLaneInput GetSlipLaneInput(Data& data, const std::vector& link_edg double origin_heading = edge_heading(node, edge); auto bundle = collect_node_edges(data.nodes[node], data.nodes, data.edges); for (auto to : bundle.node_edges) { - if (to.first.attributes.link || !IsEdgeDriveableInDirection(node, to.first, forward)) + if (to.first.attributes.link || !IsEdgedrivableInDirection(node, to.first, forward)) continue; double neighbour_heading = edge_heading(node, to.first); @@ -747,6 +746,7 @@ bool IsTurnChannel(Data& data, const std::vector& link_edges) { std::pair ReclassifyLinkGraph(std::vector& link_graph, uint32_t exit_classification, Data& data, + bool reclassify_links, bool infer_turn_channels) { // number of reclassified edges uint32_t reclass_count = 0; @@ -850,7 +850,8 @@ std::pair ReclassifyLinkGraph(std::vector& li sequence::iterator element = data.edges[edge_idx]; auto edge = *element; - if (rc > edge.attributes.importance) { + // Reclassify edge (if reclassify_links is true). + if (reclassify_links && rc > edge.attributes.importance) { if (rc < static_cast(RoadClass::kUnclassified)) edge.attributes.importance = rc; else @@ -858,6 +859,7 @@ std::pair ReclassifyLinkGraph(std::vector& li ++reclass_count; } + if (turn_channel) { edge.attributes.turn_channel = true; ++tc_count; @@ -886,11 +888,12 @@ void ReclassifyLinks(const std::string& ways_file, const std::string& edges_file, const std::string& way_nodes_file, const OSMData& osmdata, + bool reclassify_links, bool infer_turn_channels) { LOG_INFO("Reclassifying_V2 link graph edges..."); Data data(nodes_file, edges_file, ways_file, way_nodes_file, osmdata); - // Find list of exit nodes - nodes where driveable outbound links connect to + // Find list of exit nodes - nodes where drivable outbound links connect to // non-link edges. Group by best road class of the non-link connecting edges. nodelist_t exit_nodes = FormExitNodes(data.nodes, data.edges); @@ -905,14 +908,15 @@ void ReclassifyLinks(const std::string& ways_file, // build link graph auto link_graph = build_graph(node, classification); // reclassify links and infer turn channels - auto counts = ReclassifyLinkGraph(link_graph, classification, data, infer_turn_channels); + auto counts = ReclassifyLinkGraph(link_graph, classification, data, reclassify_links, + infer_turn_channels); // update counters reclass_count += counts.first; tc_count += counts.second; } } - LOG_INFO("Finished with " + std::to_string(reclass_count) + " reclassified. " + + LOG_INFO("Finished with " + std::to_string(reclass_count) + " link edges reclassified. " + " Turn channel count = " + std::to_string(tc_count)); } diff --git a/src/mjolnir/luatagtransform.cc b/src/mjolnir/luatagtransform.cc index 4047c32ca6..82290731dd 100644 --- a/src/mjolnir/luatagtransform.cc +++ b/src/mjolnir/luatagtransform.cc @@ -126,7 +126,6 @@ Tags LuaTagTransform::Transform(OSMType type, uint64_t osmid, const Tags& maptag result.clear(); } } catch (std::exception& e) { - // ..gets sent back to the main thread LOG_ERROR((boost::format("Exception in Lua function: %1%: %2%") % lua_func % e.what()).str()); } catch (...) { LOG_ERROR((boost::format("Unknown exception in Lua function: %1%.") % lua_func).str()); diff --git a/src/mjolnir/node_expander.cc b/src/mjolnir/node_expander.cc index 2fd743604d..bccb76d15c 100644 --- a/src/mjolnir/node_expander.cc +++ b/src/mjolnir/node_expander.cc @@ -19,9 +19,9 @@ node_bundle collect_node_edges(const sequence::iterator& node_itr, // Set driveforward - this edge is traversed in forward direction bundle.node_edges.emplace(std::make_pair(edge, node.start_of)); bundle.node.link_edge_ = bundle.node.link_edge_ || edge.attributes.link; - bundle.node.ferry_edge_ = bundle.node.ferry_edge_ || edge.attributes.driveable_ferry; + bundle.node.ferry_edge_ = bundle.node.ferry_edge_ || edge.attributes.drivable_ferry; bundle.node.shortlink_ |= edge.attributes.shortlink; - // Do not count non-driveable (e.g. emergency service roads) as a + // Do not count non-drivable (e.g. emergency service roads) as a // non-link edge if (edge.attributes.driveforward || (edge.rev_access & baldr::kAutoAccess)) { bundle.node.non_link_edge_ = bundle.node.non_link_edge_ || !edge.attributes.link; @@ -29,7 +29,7 @@ node_bundle collect_node_edges(const sequence::iterator& node_itr, // Non-ferry edges need access to _some_ vehicular mode if ((edge.fwd_access & baldr::kVehicularAccess) || (edge.rev_access & baldr::kVehicularAccess)) { - bundle.node.non_ferry_edge_ = bundle.node.non_ferry_edge_ || !edge.attributes.driveable_ferry; + bundle.node.non_ferry_edge_ = bundle.node.non_ferry_edge_ || !edge.attributes.drivable_ferry; } if (edge.attributes.link) { bundle.link_count++; @@ -46,16 +46,16 @@ node_bundle collect_node_edges(const sequence::iterator& node_itr, edge.attributes.driveforward = edge.rev_access & baldr::kAutoAccess; bundle.node_edges.emplace(std::make_pair(edge, node.end_of)); bundle.node.link_edge_ = bundle.node.link_edge_ || edge.attributes.link; - bundle.node.ferry_edge_ = bundle.node.ferry_edge_ || edge.attributes.driveable_ferry; + bundle.node.ferry_edge_ = bundle.node.ferry_edge_ || edge.attributes.drivable_ferry; bundle.node.shortlink_ |= edge.attributes.shortlink; - // Do not count non-driveable (e.g. emergency service roads) as a non-link edge + // Do not count non-drivable (e.g. emergency service roads) as a non-link edge if ((edge.fwd_access & baldr::kAutoAccess) || edge.attributes.driveforward) { bundle.node.non_link_edge_ = bundle.node.non_link_edge_ || !edge.attributes.link; } // Non-ferry edges need access to _some_ vehicular mode if ((edge.fwd_access & baldr::kVehicularAccess) || (edge.rev_access & baldr::kVehicularAccess)) { - bundle.node.non_ferry_edge_ = bundle.node.non_ferry_edge_ || !edge.attributes.driveable_ferry; + bundle.node.non_ferry_edge_ = bundle.node.non_ferry_edge_ || !edge.attributes.drivable_ferry; } if (edge.attributes.link) { bundle.link_count++; diff --git a/src/mjolnir/osmaccessrestriction.cc b/src/mjolnir/osmaccessrestriction.cc index 61cf4c1e4f..2a772cac4a 100644 --- a/src/mjolnir/osmaccessrestriction.cc +++ b/src/mjolnir/osmaccessrestriction.cc @@ -44,5 +44,13 @@ uint16_t OSMAccessRestriction::modes() const { return attributes_.modes_; } +AccessRestrictionDirection OSMAccessRestriction::direction() const { + return static_cast(direction_); +} + +void OSMAccessRestriction::set_direction(AccessRestrictionDirection direction) { + direction_ = direction; +}; + } // namespace mjolnir } // namespace valhalla diff --git a/src/mjolnir/osmdata.cc b/src/mjolnir/osmdata.cc index 80b9575e25..3c38a828f0 100644 --- a/src/mjolnir/osmdata.cc +++ b/src/mjolnir/osmdata.cc @@ -1,7 +1,6 @@ #include #include #include -#include #include @@ -25,6 +24,8 @@ const std::string way_ref_rev_file = "osmdata_way_refs_rev.bin"; const std::string node_names_file = "osmdata_node_names.bin"; const std::string unique_names_file = "osmdata_unique_strings.bin"; const std::string lane_connectivity_file = "osmdata_lane_connectivity.bin"; +const std::string pronunciation_file = "osmdata_pronunciation_file.bin"; +const std::string language_file = "osmdata_language_file.bin"; // Data structures to assist writing and reading data struct TempRestriction { @@ -72,6 +73,15 @@ struct TempLaneConnectivity { } }; +struct TempLinguistic { + uint64_t way_id; + OSMLinguistic linguistic; + TempLinguistic() : way_id(0), linguistic(OSMLinguistic()) { + } + TempLinguistic(const uint64_t w, const OSMLinguistic& l) : way_id(w), linguistic(l) { + } +}; + bool write_restrictions(const std::string& filename, const RestrictionsMultiMap& res_map) { // Open file and truncate std::ofstream file(filename.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); @@ -276,6 +286,28 @@ bool write_lane_connectivity(const std::string& filename, return true; } +bool write_linguistic(const std::string& filename, const LinguisticMultiMap& ling_map) { + // Open file and truncate + std::ofstream file(filename.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + if (!file.is_open()) { + LOG_ERROR("write_linguistic failed to open output file: " + filename); + return false; + } + + // Convert the multi map into a vector of TempLinguistic + std::vector ling; + for (auto it = ling_map.cbegin(); it != ling_map.cend(); ++it) { + ling.emplace_back(it->first, it->second); + } + + // Write the count and then the via ids + uint32_t sz = ling.size(); + file.write(reinterpret_cast(&sz), sizeof(uint32_t)); + file.write(reinterpret_cast(ling.data()), ling.size() * sizeof(TempLinguistic)); + file.close(); + return true; +} + bool read_restrictions(const std::string& filename, RestrictionsMultiMap& res_map) { // Open file and truncate std::ifstream file(filename, std::ios::in | std::ios::binary); @@ -471,6 +503,28 @@ bool read_lane_connectivity(const std::string& filename, OSMLaneConnectivityMult return true; } +bool read_linguistic(const std::string& filename, LinguisticMultiMap& ling_map) { + // Open file and truncate + std::ifstream file(filename, std::ios::in | std::ios::binary); + if (!file.is_open()) { + LOG_ERROR("read_linguistic failed to open input file: " + filename); + return false; + } + + // Read the count and then the temporary access restriction list + uint32_t count = 0; + file.read(reinterpret_cast(&count), sizeof(uint32_t)); + std::vector ling(count); + file.read(reinterpret_cast(ling.data()), count * sizeof(TempLinguistic)); + file.close(); + + // Iterate through the temporary access restriction list and add to the restriction multi-map + for (const auto& l : ling) { + ling_map.insert({l.way_id, l.linguistic}); + } + return true; +} + } // namespace namespace valhalla { @@ -496,6 +550,7 @@ bool OSMData::write_to_temp_files(const std::string& tile_dir) { file.write(reinterpret_cast(&node_ref_count), sizeof(uint64_t)); file.write(reinterpret_cast(&node_name_count), sizeof(uint64_t)); file.write(reinterpret_cast(&node_exit_to_count), sizeof(uint64_t)); + file.write(reinterpret_cast(&node_linguistic_count), sizeof(uint64_t)); file.close(); // Write the rest of OSMData @@ -507,7 +562,9 @@ bool OSMData::write_to_temp_files(const std::string& tile_dir) { write_way_refs(tile_dir + way_ref_rev_file, way_ref_rev) && write_node_names(tile_dir + node_names_file, node_names) && write_unique_names(tile_dir + unique_names_file, name_offset_map) && - write_lane_connectivity(tile_dir + lane_connectivity_file, lane_connectivity_map); + write_lane_connectivity(tile_dir + lane_connectivity_file, lane_connectivity_map) && + write_linguistic(tile_dir + pronunciation_file, pronunciations) && + write_linguistic(tile_dir + language_file, langs); LOG_INFO("Done"); return status; } @@ -537,6 +594,7 @@ bool OSMData::read_from_temp_files(const std::string& tile_dir) { file.read(reinterpret_cast(&node_ref_count), sizeof(uint64_t)); file.read(reinterpret_cast(&node_name_count), sizeof(uint64_t)); file.read(reinterpret_cast(&node_exit_to_count), sizeof(uint64_t)); + file.read(reinterpret_cast(&node_linguistic_count), sizeof(uint64_t)); file.close(); // Read the other data @@ -549,7 +607,9 @@ bool OSMData::read_from_temp_files(const std::string& tile_dir) { read_way_refs(tile_directory + way_ref_rev_file, way_ref_rev) && read_node_names(tile_directory + node_names_file, node_names) && read_unique_names(tile_directory + unique_names_file, name_offset_map) && - read_lane_connectivity(tile_directory + lane_connectivity_file, lane_connectivity_map); + read_lane_connectivity(tile_directory + lane_connectivity_file, lane_connectivity_map) && + read_linguistic(tile_directory + pronunciation_file, pronunciations) && + read_linguistic(tile_directory + language_file, langs); LOG_INFO("Done"); initialized = status; return status; @@ -617,6 +677,8 @@ void OSMData::cleanup_temp_files(const std::string& tile_dir) { remove_temp_file(tile_dir + node_names_file); remove_temp_file(tile_dir + unique_names_file); remove_temp_file(tile_dir + lane_connectivity_file); + remove_temp_file(tile_dir + pronunciation_file); + remove_temp_file(tile_dir + language_file); } } // namespace mjolnir diff --git a/src/mjolnir/osmpbfparser.cc b/src/mjolnir/osmpbfparser.cc index 0c2af9d7c6..05a105a206 100644 --- a/src/mjolnir/osmpbfparser.cc +++ b/src/mjolnir/osmpbfparser.cc @@ -33,7 +33,6 @@ #else #include #endif -#include #include #include diff --git a/src/mjolnir/osmway.cc b/src/mjolnir/osmway.cc index a5d4d54dd7..45db3aade7 100644 --- a/src/mjolnir/osmway.cc +++ b/src/mjolnir/osmway.cc @@ -4,7 +4,6 @@ #include "midgard/logging.h" #include -#include using namespace valhalla::baldr; @@ -81,6 +80,24 @@ void OSMWay::set_truck_speed(const float speed) { } } +void OSMWay::set_truck_speed_forward(const float truck_speed_forward) { + if (truck_speed_forward > kMaxOSMSpeed) { + LOG_WARN("Exceeded max forward truck speed for way id: " + std::to_string(osmwayid_)); + truck_speed_forward_ = kMaxOSMSpeed; + } else { + truck_speed_forward_ = static_cast(truck_speed_forward + 0.5f); + } +} + +void OSMWay::set_truck_speed_backward(const float truck_speed_backward) { + if (truck_speed_backward > kMaxOSMSpeed) { + LOG_WARN("Exceeded max backward truck speed for way id: " + std::to_string(osmwayid_)); + truck_speed_backward_ = kMaxOSMSpeed; + } else { + truck_speed_backward_ = static_cast(truck_speed_backward + 0.5f); + } +} + // Sets the number of lanes void OSMWay::set_lanes(const uint32_t lanes) { lanes_ = (lanes > kMaxLaneCount) ? kMaxLaneCount : lanes; @@ -100,93 +117,486 @@ void OSMWay::set_layer(int8_t layer) { layer_ = layer; } -void OSMWay::AddPronunciations(std::vector& pronunciations, - const UniqueNames& name_offset_map, - const uint32_t ipa_index, - const uint32_t nt_sampa_index, - const uint32_t katakana_index, - const uint32_t jeita_index, - const size_t name_tokens_size, - const size_t key) const { +void OSMWay::AddPronunciationsWithLang(std::vector& pronunciations, + std::map& lang_map, + const baldr::PronunciationAlphabet verbal_type, + const std::vector& pronunciation_tokens, + const std::vector& pronunciation_langs, + const std::vector& token_langs, + const size_t token_size, + const size_t key) const { - auto get_pronunciations = [](const std::vector& pronunciation_tokens, const size_t key, + auto get_pronunciations = [](const std::vector& pronunciation_tokens, + const std::vector& pronunciation_langs, + const std::map indexMap, const size_t key, const baldr::PronunciationAlphabet verbal_type) { - linguistic_text_header_t header{static_cast(baldr::Language::kNone), 0, - static_cast(verbal_type), static_cast(key)}; + linguistic_text_header_t header{static_cast(baldr::Language::kNone), + 0, + static_cast(verbal_type), + static_cast(key), + 0, + 0}; std::string pronunciation; - // TODO: We need to address the fact that a name/ref value could of been entered incorrectly - // For example, name="XYZ Street;;ABC Street" - // name:pronunciation="pronunciation1;pronunciation2;pronunciation3" So we check for - // name_tokens_size == pronunciation_tokens.size() and we will not toss the second record in the - // vector, but we should as it is blank. We actually address with blank name in edgeinfo via - // tossing the name if GraphTileBuilder::AddName returns 0. Thought is we could address this now - // in GetTagTokens and if we have a mismatch then don't add the pronunciations. To date no data - // has been found as described, but it could happen. Address this issue with the Language - // updates. - for (const auto& t : pronunciation_tokens) { - if (!t.size()) { // pronunciation is blank. skip and increment the index - ++header.name_index_; - continue; + for (size_t i = 0; i < pronunciation_tokens.size(); i++) { + auto& t = pronunciation_tokens[i]; + + if (indexMap.size() != 0) { + auto index = indexMap.find(i); + if (index != indexMap.end()) + header.name_index_ = index->second; + else + continue; + } + + if (!t.size()) { // pronunciation is blank. just add the lang + + if (!pronunciation_langs.size()) + continue; + + header.language_ = static_cast(pronunciation_langs.at(i)); + header.length_ = 0; + header.phonetic_alphabet_ = static_cast(baldr::PronunciationAlphabet::kNone); + + pronunciation.append( + std::string(reinterpret_cast(&header), kLinguisticHeaderSize)); + } else { + + header.phonetic_alphabet_ = static_cast(verbal_type); + + header.length_ = t.size(); + header.language_ = + (pronunciation_langs.size() ? static_cast(pronunciation_langs.at(i)) + : static_cast(baldr::Language::kNone)); + pronunciation.append( + std::string(reinterpret_cast(&header), kLinguisticHeaderSize) + t); } - header.length_ = t.size(); - pronunciation.append(std::string(reinterpret_cast(&header), 3) + t); - ++header.name_index_; + if (indexMap.size() == 0) + ++header.name_index_; } return pronunciation; }; - std::vector pronunciation_tokens; + bool process = false, found = false; + std::map indexMap; + size_t k = key; + + if ((pronunciation_langs.size() == 0 && token_langs.size() == 0) || + ((pronunciation_langs.size() != 0 && token_langs.size() == 0) && + (pronunciation_langs.size() <= token_size))) + process = true; + else { + std::pair::iterator, bool> ret; + for (size_t i = 0; i < token_langs.size(); i++) { + for (size_t j = 0; j < pronunciation_langs.size(); j++) { + if (token_langs[i] == pronunciation_langs[j]) { + ret = indexMap.insert(std::make_pair(j, k)); + if (ret.second == false) // already used + continue; + else { + k++; + process = true; + found = true; + break; + } + } + } + if (!found) { + lang_map.emplace(k, token_langs[i]); + k++; + } + found = false; + } + } + + if (lang_map.size() == 0 && pronunciation_langs.size() == 0 && token_langs.size() != 0) { + for (size_t i = 0; i < token_langs.size(); i++) { + lang_map.emplace(k, token_langs[i]); + k++; + } + } + + if (process) { + const auto& p = + get_pronunciations(pronunciation_tokens, pronunciation_langs, indexMap, key, verbal_type); + if (!p.empty()) + pronunciations.emplace_back(p); + } +} + +void OSMWay::AddPronunciations(std::vector& pronunciations, + std::map& lang_map, + const UniqueNames& name_offset_map, + const std::vector>& default_languages, + const std::vector& token_langs, + const uint32_t ipa_index, + const uint32_t ipa_lang_index, + const uint32_t nt_sampa_index, + const uint32_t nt_sampa_lang_index, + const uint32_t katakana_index, + const uint32_t katakana_lang_index, + const uint32_t jeita_index, + const uint32_t jeita_lang_index, + const size_t token_size, + const size_t key, + bool diff_names) const { + if (ipa_index != 0) { - pronunciation_tokens = GetTagTokens(name_offset_map.name(ipa_index)); - if (pronunciation_tokens.size() && name_tokens_size == pronunciation_tokens.size()) - pronunciations.emplace_back( - get_pronunciations(pronunciation_tokens, key, baldr::PronunciationAlphabet::kIpa)); + std::vector pronunciation_langs; + std::vector pronunciation_tokens; + + ProcessNamesPronunciations(name_offset_map, default_languages, ipa_index, ipa_lang_index, + pronunciation_tokens, pronunciation_langs, diff_names, true); + + AddPronunciationsWithLang(pronunciations, lang_map, baldr::PronunciationAlphabet::kIpa, + pronunciation_tokens, pronunciation_langs, token_langs, token_size, + key); } if (nt_sampa_index != 0) { - pronunciation_tokens = GetTagTokens(name_offset_map.name(nt_sampa_index)); - if (pronunciation_tokens.size() && name_tokens_size == pronunciation_tokens.size()) - pronunciations.emplace_back( - get_pronunciations(pronunciation_tokens, key, baldr::PronunciationAlphabet::kNtSampa)); + + std::vector pronunciation_langs; + std::vector pronunciation_tokens; + + ProcessNamesPronunciations(name_offset_map, default_languages, nt_sampa_index, + nt_sampa_lang_index, pronunciation_tokens, pronunciation_langs, + diff_names, true); + + AddPronunciationsWithLang(pronunciations, lang_map, baldr::PronunciationAlphabet::kNtSampa, + pronunciation_tokens, pronunciation_langs, token_langs, token_size, + key); } if (katakana_index != 0) { - pronunciation_tokens = GetTagTokens(name_offset_map.name(katakana_index)); - if (pronunciation_tokens.size() && name_tokens_size == pronunciation_tokens.size()) - pronunciations.emplace_back( - get_pronunciations(pronunciation_tokens, key, baldr::PronunciationAlphabet::kXKatakana)); + std::vector pronunciation_langs; + std::vector pronunciation_tokens; + + ProcessNamesPronunciations(name_offset_map, default_languages, katakana_index, + katakana_lang_index, pronunciation_tokens, pronunciation_langs, + diff_names, true); + + AddPronunciationsWithLang(pronunciations, lang_map, baldr::PronunciationAlphabet::kKatakana, + pronunciation_tokens, pronunciation_langs, token_langs, token_size, + key); } if (jeita_index != 0) { - pronunciation_tokens = GetTagTokens(name_offset_map.name(jeita_index)); - if (pronunciation_tokens.size() && name_tokens_size == pronunciation_tokens.size()) - pronunciations.emplace_back( - get_pronunciations(pronunciation_tokens, key, baldr::PronunciationAlphabet::kXJeita)); + std::vector pronunciation_langs; + std::vector pronunciation_tokens; + + ProcessNamesPronunciations(name_offset_map, default_languages, jeita_index, jeita_lang_index, + pronunciation_tokens, pronunciation_langs, diff_names, true); + + AddPronunciationsWithLang(pronunciations, lang_map, baldr::PronunciationAlphabet::kJeita, + pronunciation_tokens, pronunciation_langs, token_langs, token_size, + key); + } +} + +void OSMWay::AddLanguage(std::vector& linguistics, + const size_t index, + const baldr::Language& lang) const { + + if (lang != baldr::Language::kNone) { + linguistic_text_header_t header{static_cast(lang), + 0, + static_cast(baldr::PronunciationAlphabet::kNone), + static_cast(index), + 0, + 0}; + linguistics.emplace_back( + std::string(reinterpret_cast(&header), kLinguisticHeaderSize)); + } +} + +void OSMWay::ProcessNamesPronunciations( + const UniqueNames& name_offset_map, + const std::vector>& default_languages, + const uint32_t name_index, + const uint32_t name_lang_index, + std::vector& tokens, + std::vector& token_langs, + bool diff_names, + bool allow_empty_tokens) { + + std::vector token_languages, found_languages, new_sort_order; + std::vector> updated_token_languages, tokens_w_langs; + tokens = GetTagTokens(name_offset_map.name(name_index)); + token_languages = GetTagTokens(name_offset_map.name(name_lang_index)); + + if (default_languages.size() == 0) + return; + + bool all_default = false, all_blank = true; + + // todo move this out to builder? + if (default_languages.size() > 1) { + if (std::find_if(default_languages.begin() + 1, default_languages.end(), + [](const std::pair& p) { return p.second == false; }) == + default_languages.end()) { + all_default = true; + } + } + + if (name_index != 0 && name_lang_index == 0) { + token_languages.resize(tokens.size()); + fill(token_languages.begin(), token_languages.end(), ""); + } + + // remove any entries that are not in our country language list + // then sort our names based on the list. + if (default_languages.size() && + (tokens.size() == token_languages.size())) { // should always be equal + for (size_t i = 0; i < token_languages.size(); i++) { + const auto& current_lang = token_languages[i]; + if (std::find_if(default_languages.begin(), default_languages.end(), + [¤t_lang](const std::pair& p) { + return p.first == current_lang; + }) != default_languages.end()) { + if (!token_languages[i].empty()) + all_blank = false; + if (allow_empty_tokens || !tokens[i].empty()) { + updated_token_languages.emplace_back(tokens[i], token_languages[i]); + if (!token_languages[i].empty()) + tokens_w_langs.emplace_back(tokens[i], token_languages[i]); + } + } + } + + std::unordered_map lang_sort_order; + new_sort_order.emplace_back(""); + + for (size_t i = 0; i < default_languages.size(); i++) { + if (i != 0) + found_languages.emplace_back(default_languages[i].first); + lang_sort_order[default_languages[i].first] = i; + } + + auto cmp = [&lang_sort_order](const std::pair& p1, + const std::pair& p2) { + return lang_sort_order[p1.second] < lang_sort_order[p2.second]; + }; + + std::stable_sort(updated_token_languages.begin(), updated_token_languages.end(), cmp); + std::stable_sort(tokens_w_langs.begin(), tokens_w_langs.end(), cmp); + + std::vector multilingual_names, multilingual_names_found, names_w_no_lang, + supported_names; + std::vector multilingual_langs_found, supported_langs; + + for (size_t i = 0; i < updated_token_languages.size(); ++i) { + + const auto& current_lang = updated_token_languages[i].second; + + if (current_lang.empty()) { + // multilingual name + // name = Place Saint-Pierre - Sint-Pietersplein + std::vector temp_names = GetTagTokens(updated_token_languages[i].first, " - "); + if (temp_names.size() >= 2) { + multilingual_names.insert(multilingual_names.end(), temp_names.begin(), temp_names.end()); + if (!diff_names) + names_w_no_lang.insert(names_w_no_lang.end(), temp_names.begin(), temp_names.end()); + } else { + temp_names = GetTagTokens(updated_token_languages[i].first, " / "); + if (temp_names.size() >= 2) { + multilingual_names.insert(multilingual_names.end(), temp_names.begin(), temp_names.end()); + if (!diff_names) + names_w_no_lang.insert(names_w_no_lang.end(), temp_names.begin(), temp_names.end()); + } else { + const auto& current_token = updated_token_languages[i].first; + const auto& it = + std::find_if(tokens_w_langs.begin(), tokens_w_langs.end(), + [¤t_token](const std::pair& p) { + return p.first == current_token; + }); + if (it != tokens_w_langs.end()) { + new_sort_order.emplace_back(it->second); + } else + names_w_no_lang.emplace_back(updated_token_languages[i].first); + } + } + } else if (std::find(multilingual_names.begin(), multilingual_names.end(), + updated_token_languages[i].first) != multilingual_names.end()) { + multilingual_names_found.emplace_back(updated_token_languages[i].first); + multilingual_langs_found.emplace_back(stringLanguage(current_lang)); + found_languages.erase(std::remove(found_languages.begin(), found_languages.end(), + current_lang), + found_languages.end()); + if (!diff_names) + names_w_no_lang.erase(std::remove(names_w_no_lang.begin(), names_w_no_lang.end(), + updated_token_languages[i].first), + names_w_no_lang.end()); + + } else if (std::find_if(default_languages.begin(), default_languages.end(), + [¤t_lang](const std::pair& p) { + return p.first == current_lang; + }) != default_languages.end()) { + + supported_names.emplace_back(updated_token_languages[i].first); + supported_langs.emplace_back(stringLanguage(current_lang)); + + found_languages.erase(std::remove(found_languages.begin(), found_languages.end(), + current_lang), + found_languages.end()); + } + } + bool multi_names = (multilingual_names_found.size()); + bool allowed_names = (supported_names.size() != 0); + + if (multi_names || allowed_names) { + + // did we find a name with a language in the name/destination key? if so we need to redo the + // sort order for the keys + if (new_sort_order.size() != 1) { + + uint32_t count = 0; + lang_sort_order.clear(); + + for (const auto& lang : new_sort_order) { + lang_sort_order[lang] = count++; + } + + for (size_t i = 0; i < default_languages.size(); i++) { + if (lang_sort_order.find(default_languages[i].first) == lang_sort_order.end()) + lang_sort_order[default_languages[i].first] = count++; + } + } + + tokens.clear(); + token_langs.clear(); + if (multi_names) { + + if (!diff_names && names_w_no_lang.size() >= 1 && found_languages.size() == 1) { + + std::vector> temp_token_languages; + for (size_t i = 0; i < names_w_no_lang.size(); ++i) { + temp_token_languages.emplace_back(names_w_no_lang[i], found_languages.at(0)); + } + + for (size_t i = 0; i < multilingual_names_found.size(); ++i) { + temp_token_languages.emplace_back(multilingual_names_found[i], + to_string(multilingual_langs_found[i])); + } + + std::stable_sort(temp_token_languages.begin(), temp_token_languages.end(), cmp); + + for (size_t i = 0; i < temp_token_languages.size(); ++i) { + tokens.emplace_back(temp_token_languages[i].first); + token_langs.emplace_back(stringLanguage(temp_token_languages[i].second)); + } + + } else { + tokens.insert(tokens.end(), multilingual_names_found.begin(), + multilingual_names_found.end()); + token_langs.insert(token_langs.end(), multilingual_langs_found.begin(), + multilingual_langs_found.end()); + } + } + if (allowed_names) { + // assume the lang. + if (names_w_no_lang.size() >= 1 && found_languages.size() == 1) { + for (size_t i = 0; i < names_w_no_lang.size(); ++i) { + tokens.emplace_back(names_w_no_lang.at(i)); + token_langs.emplace_back(stringLanguage(found_languages.at(0))); + } + + for (size_t i = 0; i < supported_names.size(); ++i) { + tokens.emplace_back(supported_names[i]); + token_langs.emplace_back(supported_langs[i]); + } + // name key not found but all the langs were found. + } else { + std::vector> temp_token_languages; + + for (size_t i = 0; i < names_w_no_lang.size(); ++i) { + temp_token_languages.emplace_back(names_w_no_lang[i], ""); + } + + for (size_t i = 0; i < supported_names.size(); ++i) { + temp_token_languages.emplace_back(supported_names[i], to_string(supported_langs[i])); + } + + std::stable_sort(temp_token_languages.begin(), temp_token_languages.end(), cmp); + + for (size_t i = 0; i < temp_token_languages.size(); ++i) { + tokens.emplace_back(temp_token_languages[i].first); + token_langs.emplace_back(stringLanguage(temp_token_languages[i].second)); + } + } + } + } else { // bail + tokens.clear(); + token_langs.clear(); + if ((updated_token_languages.size() > 1 && !all_blank && name_lang_index != 0) || + (default_languages.size() > 2 && all_blank && name_lang_index == 0)) + all_default = false; + for (size_t i = 0; i < updated_token_languages.size(); ++i) { + if (updated_token_languages[i].second.empty()) { + tokens.emplace_back(updated_token_languages[i].first); + if (all_default) + token_langs.emplace_back(stringLanguage(default_languages.at(1).first)); + } + } + } } } // Get the names for the edge info based on the road class. void OSMWay::GetNames(const std::string& ref, const UniqueNames& name_offset_map, - const OSMPronunciation& pronunciation, + const std::map, uint32_t>& pronunciationMap, + const std::map, uint32_t>& langMap, + const std::vector>& default_languages, + const uint32_t ref_index, + const uint32_t ref_lang_index, + const uint32_t name_index, + const uint32_t name_lang_index, + const uint32_t official_name_index, + const uint32_t official_name_lang_index, + const uint32_t alt_name_index, + const uint32_t alt_name_lang_index, uint16_t& types, std::vector& names, - std::vector& pronunciations) const { + std::vector& linguistics, + OSMLinguistic::DiffType type, + bool diff_names) const { uint16_t location = 0; types = 0; + const uint8_t ipa = static_cast(PronunciationAlphabet::kIpa); + const uint8_t nt_sampa = static_cast(PronunciationAlphabet::kNtSampa); + const uint8_t katakana = static_cast(PronunciationAlphabet::kKatakana); + const uint8_t jeita = static_cast(PronunciationAlphabet::kJeita); + + auto get_pronunciation_index = [&pronunciationMap](const uint8_t type, const uint8_t alpha) { + auto itr = pronunciationMap.find(std::make_pair(type, alpha)); + if (itr != pronunciationMap.end()) { + return itr->second; + } + return static_cast(0); + }; + + auto get_lang_index = [&langMap](const uint8_t type, const uint8_t alpha) { + auto itr = langMap.find(std::make_pair(type, alpha)); + if (itr != langMap.end()) { + return itr->second; + } + return static_cast(0); + }; // Process motorway and trunk refs - if ((ref_index_ != 0 || !ref.empty()) && + if ((ref_index != 0 || !ref.empty()) && ((static_cast(road_class_) == RoadClass::kMotorway) || (static_cast(road_class_) == RoadClass::kTrunk))) { std::vector tokens; + std::vector token_langs; std::vector pronunciation_tokens; + std::map lang_map; if (!ref.empty()) { tokens = GetTagTokens(ref); // use updated refs from relations. } else { - tokens = GetTagTokens(name_offset_map.name(ref_index_)); + ProcessNamesPronunciations(name_offset_map, default_languages, ref_index, ref_lang_index, + tokens, token_langs, diff_names); } for (size_t i = 0; i < tokens.size(); ++i) { @@ -197,40 +607,159 @@ void OSMWay::GetNames(const std::string& ref, names.insert(names.end(), tokens.begin(), tokens.end()); size_t key = names.size() - tokens.size(); - AddPronunciations(pronunciations, name_offset_map, pronunciation.ref_pronunciation_ipa_index(), - pronunciation.ref_pronunciation_nt_sampa_index(), - pronunciation.ref_pronunciation_katakana_index(), - pronunciation.ref_pronunciation_jeita_index(), tokens.size(), key); + size_t phoneme_start_index = linguistics.size(); + + if (diff_names) { + switch (type) { + case OSMLinguistic::DiffType::kRight: { + const uint8_t t = static_cast(OSMLinguistic::Type::kRefRight); + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), + tokens.size(), key, diff_names); + break; + } + case OSMLinguistic::DiffType::kLeft: { + const uint8_t t = static_cast(OSMLinguistic::Type::kRefLeft); + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), + tokens.size(), key, diff_names); + break; + } + case OSMLinguistic::DiffType::kForward: + case OSMLinguistic::DiffType::kBackward: + break; + } + } else { + const uint8_t t = static_cast(OSMLinguistic::Type::kRef); + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), tokens.size(), + key, diff_names); + } + + bool add_phoneme = (linguistics.size() - phoneme_start_index); + + for (size_t l = 0; l < token_langs.size(); ++l) { + if (lang_map.size() != 0) { + auto itr = lang_map.find(key); + if (itr != lang_map.end()) { + AddLanguage(linguistics, key, itr->second); + } + } else if (!add_phoneme) + AddLanguage(linguistics, key, token_langs.at(l)); + key++; + } } // TODO int_ref // Process name - if (name_index_ != 0) { + if (name_index != 0) { std::vector tokens; - tokens = GetTagTokens(name_offset_map.name(name_index_)); - location += tokens.size(); + std::vector token_langs; + std::vector pronunciation_tokens; + std::map lang_map; + + ProcessNamesPronunciations(name_offset_map, default_languages, name_index, name_lang_index, + tokens, token_langs, diff_names); + location += tokens.size(); names.insert(names.end(), tokens.begin(), tokens.end()); size_t key = names.size() - tokens.size(); + size_t phoneme_start_index = linguistics.size(); - AddPronunciations(pronunciations, name_offset_map, pronunciation.name_pronunciation_ipa_index(), - pronunciation.name_pronunciation_nt_sampa_index(), - pronunciation.name_pronunciation_katakana_index(), - pronunciation.name_pronunciation_jeita_index(), tokens.size(), key); + if (diff_names) { + switch (type) { + case OSMLinguistic::DiffType::kRight: { + const uint8_t t = static_cast(OSMLinguistic::Type::kNameRight); + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), + tokens.size(), key, diff_names); + break; + } + case OSMLinguistic::DiffType::kLeft: { + const uint8_t t = static_cast(OSMLinguistic::Type::kNameLeft); + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), + tokens.size(), key, diff_names); + break; + } + case OSMLinguistic::DiffType::kForward: { + const uint8_t t = static_cast(OSMLinguistic::Type::kNameForward); + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), + tokens.size(), key, diff_names); + break; + } + case OSMLinguistic::DiffType::kBackward: { + const uint8_t t = static_cast(OSMLinguistic::Type::kNameBackward); + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), + tokens.size(), key, diff_names); + break; + } + } + } else { + const uint8_t t = static_cast(OSMLinguistic::Type::kName); + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), tokens.size(), + key, diff_names); + } + + bool add_phoneme = (linguistics.size() - phoneme_start_index); + + for (size_t l = 0; l < token_langs.size(); ++l) { + if (lang_map.size() != 0) { + auto itr = lang_map.find(key); + if (itr != lang_map.end()) { + AddLanguage(linguistics, key, itr->second); + } + } else if (!add_phoneme) + AddLanguage(linguistics, key, token_langs.at(l)); + key++; + } } + // create a set of keys for name list. send to AddEdgeInfo? + // Process non limited access refs - if (ref_index_ != 0 && (static_cast(road_class_) != RoadClass::kMotorway) && + if (ref_index != 0 && (static_cast(road_class_) != RoadClass::kMotorway) && (static_cast(road_class_) != RoadClass::kTrunk)) { std::vector tokens; + std::vector token_langs; + std::map lang_map; if (!ref.empty()) { tokens = GetTagTokens(ref); // use updated refs from relations. } else { - tokens = GetTagTokens(name_offset_map.name(ref_index_)); + + ProcessNamesPronunciations(name_offset_map, default_languages, ref_index, ref_lang_index, + tokens, token_langs, diff_names); } for (size_t i = 0; i < tokens.size(); ++i) { @@ -241,90 +770,295 @@ void OSMWay::GetNames(const std::string& ref, names.insert(names.end(), tokens.begin(), tokens.end()); size_t key = names.size() - tokens.size(); - AddPronunciations(pronunciations, name_offset_map, pronunciation.ref_pronunciation_ipa_index(), - pronunciation.ref_pronunciation_nt_sampa_index(), - pronunciation.ref_pronunciation_katakana_index(), - pronunciation.ref_pronunciation_jeita_index(), tokens.size(), key); + size_t phoneme_start_index = linguistics.size(); + + if (diff_names) { + switch (type) { + case OSMLinguistic::DiffType::kRight: { + const uint8_t t = static_cast(OSMLinguistic::Type::kRefRight); + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), + tokens.size(), key, diff_names); + break; + } + case OSMLinguistic::DiffType::kLeft: { + const uint8_t t = static_cast(OSMLinguistic::Type::kRefLeft); + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), + tokens.size(), key, diff_names); + break; + } + case OSMLinguistic::DiffType::kForward: + case OSMLinguistic::DiffType::kBackward: + break; + } + } else { + const uint8_t t = static_cast(OSMLinguistic::Type::kRef); + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), tokens.size(), + key, diff_names); + } + bool add_phoneme = (linguistics.size() - phoneme_start_index); + + for (size_t l = 0; l < token_langs.size(); ++l) { + if (lang_map.size() != 0) { + auto itr = lang_map.find(key); + if (itr != lang_map.end()) { + AddLanguage(linguistics, key, itr->second); + } + } else if (!add_phoneme) + AddLanguage(linguistics, key, token_langs.at(l)); + key++; + } } // Process alt_name - if (alt_name_index_ != 0 && alt_name_index_ != name_index_) { + if (alt_name_index != 0 && alt_name_index != name_index) { std::vector tokens; - tokens = GetTagTokens(name_offset_map.name(alt_name_index_)); + std::vector token_langs; + std::map lang_map; + + ProcessNamesPronunciations(name_offset_map, default_languages, alt_name_index, + alt_name_lang_index, tokens, token_langs, diff_names); location += tokens.size(); names.insert(names.end(), tokens.begin(), tokens.end()); size_t key = names.size() - tokens.size(); - AddPronunciations(pronunciations, name_offset_map, - pronunciation.alt_name_pronunciation_ipa_index(), - pronunciation.alt_name_pronunciation_nt_sampa_index(), - pronunciation.alt_name_pronunciation_katakana_index(), - pronunciation.alt_name_pronunciation_jeita_index(), tokens.size(), key); + size_t phoneme_start_index = linguistics.size(); + + if (diff_names) { + switch (type) { + case OSMLinguistic::DiffType::kRight: { + const uint8_t t = static_cast(OSMLinguistic::Type::kAltNameRight); + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), + tokens.size(), key, diff_names); + break; + } + case OSMLinguistic::DiffType::kLeft: { + const uint8_t t = static_cast(OSMLinguistic::Type::kAltNameLeft); + + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), + tokens.size(), key, diff_names); + break; + } + case OSMLinguistic::DiffType::kForward: + case OSMLinguistic::DiffType::kBackward: + break; + } + } else { + const uint8_t t = static_cast(OSMLinguistic::Type::kAltName); + + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), tokens.size(), + key, diff_names); + } + bool add_phoneme = (linguistics.size() - phoneme_start_index); + + for (size_t l = 0; l < token_langs.size(); ++l) { + if (lang_map.size() != 0) { + auto itr = lang_map.find(key); + if (itr != lang_map.end()) { + AddLanguage(linguistics, key, itr->second); + } + } else if (!add_phoneme) + AddLanguage(linguistics, key, token_langs.at(l)); + key++; + } } // Process official_name - if (official_name_index_ != 0 && official_name_index_ != name_index_ && - official_name_index_ != alt_name_index_) { + if (official_name_index != 0 && official_name_index != name_index && + official_name_index != alt_name_index) { std::vector tokens; - tokens = GetTagTokens(name_offset_map.name(official_name_index_)); + std::vector token_langs; + std::map lang_map; + + ProcessNamesPronunciations(name_offset_map, default_languages, official_name_index, + official_name_lang_index, tokens, token_langs, diff_names); location += tokens.size(); names.insert(names.end(), tokens.begin(), tokens.end()); size_t key = names.size() - tokens.size(); - AddPronunciations(pronunciations, name_offset_map, - pronunciation.official_name_pronunciation_ipa_index(), - pronunciation.official_name_pronunciation_nt_sampa_index(), - pronunciation.official_name_pronunciation_katakana_index(), - pronunciation.official_name_pronunciation_jeita_index(), tokens.size(), key); - } - // Process name_en_ - // TODO: process country specific names - if (name_en_index_ != 0 && name_en_index_ != name_index_ && name_en_index_ != alt_name_index_ && - name_en_index_ != official_name_index_) { + size_t phoneme_start_index = linguistics.size(); - std::vector tokens; - tokens = GetTagTokens(name_offset_map.name(name_en_index_)); - location += tokens.size(); + if (diff_names) { + switch (type) { + case OSMLinguistic::DiffType::kRight: { + const uint8_t t = static_cast(OSMLinguistic::Type::kOfficialNameRight); - names.insert(names.end(), tokens.begin(), tokens.end()); + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), + tokens.size(), key, diff_names); + break; + } + case OSMLinguistic::DiffType::kLeft: { + const uint8_t t = static_cast(OSMLinguistic::Type::kOfficialNameLeft); - size_t key = names.size() - tokens.size(); - AddPronunciations(pronunciations, name_offset_map, - pronunciation.name_en_pronunciation_ipa_index(), - pronunciation.name_en_pronunciation_nt_sampa_index(), - pronunciation.name_en_pronunciation_katakana_index(), - pronunciation.name_en_pronunciation_jeita_index(), tokens.size(), key); + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), + tokens.size(), key, diff_names); + break; + } + case OSMLinguistic::DiffType::kForward: + case OSMLinguistic::DiffType::kBackward: + break; + } + } else { + const uint8_t t = static_cast(OSMLinguistic::Type::kOfficialName); + + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), tokens.size(), + key, diff_names); + } + bool add_phoneme = (linguistics.size() - phoneme_start_index); + + for (size_t l = 0; l < token_langs.size(); ++l) { + if (lang_map.size() != 0) { + auto itr = lang_map.find(key); + if (itr != lang_map.end()) { + AddLanguage(linguistics, key, itr->second); + } + } else if (!add_phoneme) + AddLanguage(linguistics, key, token_langs.at(l)); + key++; + } } } // Get the tagged names for an edge void OSMWay::GetTaggedValues(const UniqueNames& name_offset_map, - const OSMPronunciation& pronunciation, + const std::map, uint32_t>& pronunciationMap, + const std::map, uint32_t>& langMap, + const std::vector>& default_languages, + const uint32_t tunnel_name_index, + const uint32_t tunnel_name_lang_index, const size_t& names_size, std::vector& names, - std::vector& pronunciations) const { + std::vector& linguistics, + OSMLinguistic::DiffType type, + bool diff_names) const { - std::vector tokens; + auto get_pronunciation_index = [&pronunciationMap](const uint8_t type, const uint8_t alpha) { + auto itr = pronunciationMap.find(std::make_pair(type, alpha)); + if (itr != pronunciationMap.end()) { + return itr->second; + } + return static_cast(0); + }; + auto get_lang_index = [&langMap](const uint8_t type, const uint8_t alpha) { + auto itr = langMap.find(std::make_pair(type, alpha)); + if (itr != langMap.end()) { + return itr->second; + } + return static_cast(0); + }; auto encode_tag = [](TaggedValue tag) { return std::string(1, static_cast(tag)); }; - if (tunnel_name_index_ != 0) { + if (tunnel_name_index != 0) { // tunnel names - auto tokens = GetTagTokens(name_offset_map.name(tunnel_name_index_)); + + std::vector tokens; + std::vector token_langs; + std::map lang_map; + const uint8_t ipa = static_cast(PronunciationAlphabet::kIpa); + const uint8_t nt_sampa = static_cast(PronunciationAlphabet::kNtSampa); + const uint8_t katakana = static_cast(PronunciationAlphabet::kKatakana); + const uint8_t jeita = static_cast(PronunciationAlphabet::kJeita); + + ProcessNamesPronunciations(name_offset_map, default_languages, tunnel_name_index, + tunnel_name_lang_index, tokens, token_langs, diff_names); + for (const auto& t : tokens) { names.emplace_back(encode_tag(TaggedValue::kTunnel) + t); } size_t key = (names_size + names.size()) - tokens.size(); - AddPronunciations(pronunciations, name_offset_map, - pronunciation.tunnel_name_pronunciation_ipa_index(), - pronunciation.tunnel_name_pronunciation_nt_sampa_index(), - pronunciation.tunnel_name_pronunciation_katakana_index(), - pronunciation.tunnel_name_pronunciation_jeita_index(), tokens.size(), key); + size_t phoneme_start_index = linguistics.size(); + + if (diff_names) { + switch (type) { + case OSMLinguistic::DiffType::kRight: { + const uint8_t t = static_cast(OSMLinguistic::Type::kTunnelNameRight); + + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), + tokens.size(), key, diff_names); + break; + } + case OSMLinguistic::DiffType::kLeft: { + const uint8_t t = static_cast(OSMLinguistic::Type::kTunnelNameLeft); + + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), + tokens.size(), key, diff_names); + break; + } + case OSMLinguistic::DiffType::kForward: + case OSMLinguistic::DiffType::kBackward: + break; + } + } else { + const uint8_t t = static_cast(OSMLinguistic::Type::kTunnelName); + + AddPronunciations(linguistics, lang_map, name_offset_map, default_languages, token_langs, + get_pronunciation_index(t, ipa), get_lang_index(t, ipa), + get_pronunciation_index(t, nt_sampa), get_lang_index(t, nt_sampa), + get_pronunciation_index(t, katakana), get_lang_index(t, katakana), + get_pronunciation_index(t, jeita), get_lang_index(t, jeita), tokens.size(), + key, diff_names); + } + bool add_phoneme = (linguistics.size() - phoneme_start_index); + + for (size_t l = 0; l < token_langs.size(); ++l) { + if (lang_map.size() != 0) { + auto itr = lang_map.find(key); + if (itr != lang_map.end()) { + AddLanguage(linguistics, key, itr->second); + } + } else if (!add_phoneme) + AddLanguage(linguistics, key, token_langs.at(l)); + key++; + } } if (layer_ != 0) { diff --git a/src/mjolnir/pbfadminparser.cc b/src/mjolnir/pbfadminparser.cc index 58449a6d3d..6da91859a0 100644 --- a/src/mjolnir/pbfadminparser.cc +++ b/src/mjolnir/pbfadminparser.cc @@ -95,6 +95,8 @@ struct admin_callback : public OSMPBF::Callback { admin.allow_intersection_names = tag.second == "true" ? true : false; } else if (tag.first == "iso_code" && !tag.second.empty()) { admin.iso_code_index = osm_admin_data_.name_offset_map.index(tag.second); + } else if (tag.first == "default_language" && !tag.second.empty()) { + admin.default_language_index = osm_admin_data_.name_offset_map.index(tag.second); } } diff --git a/src/mjolnir/pbfgraphparser.cc b/src/mjolnir/pbfgraphparser.cc index d35c279dc1..ecdcb62ec9 100644 --- a/src/mjolnir/pbfgraphparser.cc +++ b/src/mjolnir/pbfgraphparser.cc @@ -1,10 +1,8 @@ -#include #include #include #include #include -#include #include #include "baldr/complexrestriction.h" @@ -20,8 +18,8 @@ #include "midgard/tiles.h" #include "mjolnir/luatagtransform.h" #include "mjolnir/osmaccess.h" +#include "mjolnir/osmlinguistic.h" #include "mjolnir/osmpbfparser.h" -#include "mjolnir/osmpronunciation.h" #include "mjolnir/pbfgraphparser.h" #include "mjolnir/timeparsing.h" #include "mjolnir/util.h" @@ -347,6 +345,10 @@ struct graph_callback : public OSMPBF::Callback { if (tag_.second == "true") way_.set_destination_only(true); }; + tag_handlers_["private_hgv"] = [this]() { + if (tag_.second == "true") + way_.set_destination_only_hgv(true); + }; tag_handlers_["service"] = [this]() { if (tag_.second == "rest_area") { service_ = tag_.second; @@ -479,21 +481,57 @@ struct graph_callback : public OSMPBF::Callback { if (!tag_.second.empty()) name_ = tag_.second; }; - tag_handlers_["name:en"] = [this]() { + tag_handlers_["name:left"] = [this]() { + if (!tag_.second.empty()) + name_left_ = tag_.second; + }; + tag_handlers_["name:right"] = [this]() { + if (!tag_.second.empty()) + name_right_ = tag_.second; + }; + tag_handlers_["name:forward"] = [this]() { + if (!tag_.second.empty()) + name_forward_ = tag_.second; + }; + tag_handlers_["name:backward"] = [this]() { if (!tag_.second.empty()) - way_.set_name_en_index(osmdata_.name_offset_map.index(tag_.second)); + name_backward_ = tag_.second; }; tag_handlers_["alt_name"] = [this]() { if (!tag_.second.empty() && allow_alt_name_) - way_.set_alt_name_index(osmdata_.name_offset_map.index(tag_.second)); + alt_name_ = tag_.second; + }; + tag_handlers_["alt_name:left"] = [this]() { + if (!tag_.second.empty() && allow_alt_name_) + alt_name_left_ = tag_.second; + }; + tag_handlers_["alt_name:right"] = [this]() { + if (!tag_.second.empty() && allow_alt_name_) + alt_name_right_ = tag_.second; }; tag_handlers_["official_name"] = [this]() { if (!tag_.second.empty()) - way_.set_official_name_index(osmdata_.name_offset_map.index(tag_.second)); + official_name_ = tag_.second; + }; + tag_handlers_["official_name:left"] = [this]() { + if (!tag_.second.empty()) + official_name_left_ = tag_.second; + }; + tag_handlers_["official_name:right"] = [this]() { + if (!tag_.second.empty()) + official_name_right_ = tag_.second; }; tag_handlers_["tunnel:name"] = [this]() { if (!tag_.second.empty()) - way_.set_tunnel_name_index(osmdata_.name_offset_map.index(tag_.second)); + tunnel_name_ = tag_.second; + }; + tag_handlers_["tunnel:name:left"] = [this]() { + if (!tag_.second.empty()) + tunnel_name_left_ = tag_.second; + }; + tag_handlers_["tunnel:name:right"] = [this]() { + if (!tag_.second.empty()) + tunnel_name_right_ = tag_.second; }; tag_handlers_["level"] = [this]() { if (!tag_.second.empty()) @@ -506,141 +544,337 @@ struct graph_callback : public OSMPBF::Callback { tag_handlers_["name:pronunciation"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_name_pronunciation_ipa_index( - osmdata_.name_offset_map.index(tag_.second)); + name_ipa_ = tag_.second; } }; - tag_handlers_["name:en:pronunciation"] = [this]() { + tag_handlers_["name:left:pronunciation"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_name_en_pronunciation_ipa_index( - osmdata_.name_offset_map.index(tag_.second)); + name_left_ipa_ = tag_.second; + } + }; + tag_handlers_["name:right:pronunciation"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + name_right_ipa_ = tag_.second; + } + }; + tag_handlers_["name:forward:pronunciation"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + name_forward_ipa_ = tag_.second; + } + }; + tag_handlers_["name:backward:pronunciation"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + name_backward_ipa_ = tag_.second; } }; tag_handlers_["alt_name:pronunciation"] = [this]() { if (!tag_.second.empty() && allow_alt_name_) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_alt_name_pronunciation_ipa_index( - osmdata_.name_offset_map.index(tag_.second)); + alt_name_ipa_ = tag_.second; + } + }; + tag_handlers_["alt_name:left:pronunciation"] = [this]() { + if (!tag_.second.empty() && allow_alt_name_) { + has_pronunciation_tags_ = true; + alt_name_left_ipa_ = tag_.second; + } + }; + tag_handlers_["alt_name:right:pronunciation"] = [this]() { + if (!tag_.second.empty() && allow_alt_name_) { + has_pronunciation_tags_ = true; + alt_name_right_ipa_ = tag_.second; } }; tag_handlers_["official_name:pronunciation"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_official_name_pronunciation_ipa_index( - osmdata_.name_offset_map.index(tag_.second)); + official_name_ipa_ = tag_.second; + } + }; + tag_handlers_["official_name:left:pronunciation"] = [this]() { + if (!tag_.second.empty()) { + official_name_left_ipa_ = tag_.second; + has_pronunciation_tags_ = true; + } + }; + tag_handlers_["official_name:right:pronunciation"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + official_name_right_ipa_ = tag_.second; } }; tag_handlers_["tunnel:name:pronunciation"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_tunnel_name_pronunciation_ipa_index( - osmdata_.name_offset_map.index(tag_.second)); + tunnel_name_ipa_ = tag_.second; + } + }; + tag_handlers_["tunnel:name:left:pronunciation"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + tunnel_name_left_ipa_ = tag_.second; + } + }; + tag_handlers_["tunnel:name:right:pronunciation"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + tunnel_name_right_ipa_ = tag_.second; } }; tag_handlers_["name:pronunciation:nt-sampa"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_name_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(tag_.second)); + name_nt_sampa_ = tag_.second; + } + }; + tag_handlers_["name:left:pronunciation:nt-sampa"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + name_left_nt_sampa_ = tag_.second; } }; - tag_handlers_["name:en:pronunciation:nt-sampa"] = [this]() { + tag_handlers_["name:right:pronunciation:nt-sampa"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_name_en_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(tag_.second)); + name_right_nt_sampa_ = tag_.second; + } + }; + tag_handlers_["name:forward:pronunciation:nt-sampa"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + name_forward_nt_sampa_ = tag_.second; + } + }; + tag_handlers_["name:backward:pronunciation:nt-sampa"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + name_backward_nt_sampa_ = tag_.second; } }; tag_handlers_["alt_name:pronunciation:nt-sampa"] = [this]() { if (!tag_.second.empty() && allow_alt_name_) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_alt_name_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(tag_.second)); + alt_name_nt_sampa_ = tag_.second; + } + }; + tag_handlers_["alt_name:left:pronunciation:nt-sampa"] = [this]() { + if (!tag_.second.empty() && allow_alt_name_) { + has_pronunciation_tags_ = true; + alt_name_left_nt_sampa_ = tag_.second; + } + }; + tag_handlers_["alt_name:right:pronunciation:nt-sampa"] = [this]() { + if (!tag_.second.empty() && allow_alt_name_) { + has_pronunciation_tags_ = true; + alt_name_right_nt_sampa_ = tag_.second; } }; tag_handlers_["official_name:pronunciation:nt-sampa"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_official_name_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(tag_.second)); + official_name_nt_sampa_ = tag_.second; + } + }; + tag_handlers_["official_name:left:pronunciation:nt-sampa"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + official_name_left_nt_sampa_ = tag_.second; + } + }; + tag_handlers_["official_name:right:pronunciation:nt-sampa"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + official_name_right_nt_sampa_ = tag_.second; } }; tag_handlers_["tunnel:name:pronunciation:nt-sampa"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_tunnel_name_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(tag_.second)); + tunnel_name_nt_sampa_ = tag_.second; + } + }; + tag_handlers_["tunnel:name:left:pronunciation:nt-sampa"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + tunnel_name_left_nt_sampa_ = tag_.second; + } + }; + tag_handlers_["tunnel:name:right:pronunciation:nt-sampa"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + tunnel_name_right_nt_sampa_ = tag_.second; + } + }; + tag_handlers_["name:pronunciation:katakana"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + name_katakana_ = tag_.second; + } + }; + tag_handlers_["name:left:pronunciation:katakana"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + name_left_katakana_ = tag_.second; } }; - tag_handlers_["name:pronunciation:x-katakana"] = [this]() { + tag_handlers_["name:right:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_name_pronunciation_katakana_index( - osmdata_.name_offset_map.index(tag_.second)); + name_right_katakana_ = tag_.second; } }; - tag_handlers_["name:en:pronunciation:x-katakana"] = [this]() { + tag_handlers_["name:forward:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_name_en_pronunciation_katakana_index( - osmdata_.name_offset_map.index(tag_.second)); + name_forward_katakana_ = tag_.second; } }; - tag_handlers_["alt_name:pronunciation:x-katakana"] = [this]() { + tag_handlers_["name:backward:pronunciation:katakana"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + name_backward_katakana_ = tag_.second; + } + }; + tag_handlers_["alt_name:pronunciation:katakana"] = [this]() { + if (!tag_.second.empty() && allow_alt_name_) { + has_pronunciation_tags_ = true; + alt_name_katakana_ = tag_.second; + } + }; + tag_handlers_["alt_name:left:pronunciation:katakana"] = [this]() { + if (!tag_.second.empty() && allow_alt_name_) { + has_pronunciation_tags_ = true; + alt_name_left_katakana_ = tag_.second; + } + }; + tag_handlers_["alt_name:right:pronunciation:katakana"] = [this]() { if (!tag_.second.empty() && allow_alt_name_) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_alt_name_pronunciation_katakana_index( - osmdata_.name_offset_map.index(tag_.second)); + alt_name_right_katakana_ = tag_.second; + } + }; + tag_handlers_["official_name:pronunciation:katakana"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + official_name_katakana_ = tag_.second; + } + }; + tag_handlers_["official_name:left:pronunciation:katakana"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + official_name_left_katakana_ = tag_.second; } }; - tag_handlers_["official_name:pronunciation:x-katakana"] = [this]() { + tag_handlers_["official_name:right:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_official_name_pronunciation_katakana_index( - osmdata_.name_offset_map.index(tag_.second)); + official_name_right_katakana_ = tag_.second; } }; - tag_handlers_["tunnel:name:pronunciation:x-katakana"] = [this]() { + tag_handlers_["tunnel:name:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_tunnel_name_pronunciation_katakana_index( - osmdata_.name_offset_map.index(tag_.second)); + tunnel_name_katakana_ = tag_.second; } }; - tag_handlers_["name:pronunciation:x-jeita"] = [this]() { + tag_handlers_["tunnel:name:left:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_name_pronunciation_jeita_index( - osmdata_.name_offset_map.index(tag_.second)); + tunnel_name_left_katakana_ = tag_.second; } }; - tag_handlers_["name:en:pronunciation:x-jeita"] = [this]() { + tag_handlers_["tunnel:name:right:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_name_en_pronunciation_jeita_index( - osmdata_.name_offset_map.index(tag_.second)); + tunnel_name_right_katakana_ = tag_.second; } }; - tag_handlers_["alt_name:pronunciation:x-jeita"] = [this]() { + tag_handlers_["name:pronunciation:jeita"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + name_jeita_ = tag_.second; + } + }; + tag_handlers_["name:left:pronunciation:jeita"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + name_left_jeita_ = tag_.second; + } + }; + tag_handlers_["name:right:pronunciation:jeita"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + name_right_jeita_ = tag_.second; + } + }; + tag_handlers_["name:forward:pronunciation:jeita"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + name_forward_jeita_ = tag_.second; + } + }; + tag_handlers_["name:backward:pronunciation:jeita"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + name_backward_jeita_ = tag_.second; + } + }; + tag_handlers_["alt_name:pronunciation:jeita"] = [this]() { if (!tag_.second.empty() && allow_alt_name_) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_alt_name_pronunciation_jeita_index( - osmdata_.name_offset_map.index(tag_.second)); + alt_name_jeita_ = tag_.second; } }; - tag_handlers_["official_name:pronunciation:x-jeita"] = [this]() { + tag_handlers_["alt_name:left:pronunciation:jeita"] = [this]() { + if (!tag_.second.empty() && allow_alt_name_) { + has_pronunciation_tags_ = true; + alt_name_left_jeita_ = tag_.second; + } + }; + tag_handlers_["alt_name:right:pronunciation:jeita"] = [this]() { + if (!tag_.second.empty() && allow_alt_name_) { + has_pronunciation_tags_ = true; + alt_name_right_jeita_ = tag_.second; + } + }; + tag_handlers_["official_name:pronunciation:jeita"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_official_name_pronunciation_jeita_index( - osmdata_.name_offset_map.index(tag_.second)); + official_name_jeita_ = tag_.second; } }; - tag_handlers_["tunnel:name:pronunciation:x-jeita"] = [this]() { + tag_handlers_["official_name:left:pronunciation:jeita"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_tunnel_name_pronunciation_jeita_index( - osmdata_.name_offset_map.index(tag_.second)); + official_name_left_jeita_ = tag_.second; + } + }; + tag_handlers_["official_name:right:pronunciation:jeita"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + official_name_right_jeita_ = tag_.second; + } + }; + tag_handlers_["tunnel:name:pronunciation:jeita"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + tunnel_name_jeita_ = tag_.second; + } + }; + tag_handlers_["tunnel:name:left:pronunciation:jeita"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + tunnel_name_left_jeita_ = tag_.second; + } + }; + tag_handlers_["tunnel:name:right:pronunciation:jeita"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + tunnel_name_right_jeita_ = tag_.second; } }; tag_handlers_["max_speed"] = [this]() { @@ -698,6 +932,20 @@ struct graph_callback : public OSMPBF::Callback { LOG_INFO("out_of_range thrown for way id: " + std::to_string(osmid_)); } }; + tag_handlers_["maxspeed:hgv:forward"] = [this]() { + try { + way_.set_truck_speed_forward(std::stof(tag_.second)); + } catch (const std::out_of_range& oor) { + LOG_INFO("out_of_range thrown for way id: " + std::to_string(osmid_)); + } + }; + tag_handlers_["maxspeed:hgv:backward"] = [this]() { + try { + way_.set_truck_speed_backward(std::stof(tag_.second)); + } catch (const std::out_of_range& oor) { + LOG_INFO("out_of_range thrown for way id: " + std::to_string(osmid_)); + } + }; tag_handlers_["truck_route"] = [this]() { way_.set_truck_route(tag_.second == "true" ? true : false); }; @@ -709,6 +957,24 @@ struct graph_callback : public OSMPBF::Callback { osmdata_.access_restrictions.insert( AccessRestrictionsMultiMap::value_type(osmid_, restriction)); }; + tag_handlers_["hazmat_forward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kHazmat); + restriction.set_value(tag_.second == "true" ? true : false); + restriction.set_modes(kTruckAccess); + restriction.set_direction(AccessRestrictionDirection::kForward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; + tag_handlers_["hazmat_backward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kHazmat); + restriction.set_value(tag_.second == "true" ? true : false); + restriction.set_modes(kTruckAccess); + restriction.set_direction(AccessRestrictionDirection::kBackward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; tag_handlers_["maxheight"] = [this]() { OSMAccessRestriction restriction; restriction.set_type(AccessType::kMaxHeight); @@ -717,6 +983,24 @@ struct graph_callback : public OSMPBF::Callback { osmdata_.access_restrictions.insert( AccessRestrictionsMultiMap::value_type(osmid_, restriction)); }; + tag_handlers_["maxheight_forward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kMaxHeight); + restriction.set_value(std::stof(tag_.second) * 100); + restriction.set_modes(kTruckAccess | kAutoAccess | kHOVAccess | kTaxiAccess | kBusAccess); + restriction.set_direction(AccessRestrictionDirection::kForward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; + tag_handlers_["maxheight_backward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kMaxHeight); + restriction.set_value(std::stof(tag_.second) * 100); + restriction.set_modes(kTruckAccess | kAutoAccess | kHOVAccess | kTaxiAccess | kBusAccess); + restriction.set_direction(AccessRestrictionDirection::kBackward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; tag_handlers_["maxwidth"] = [this]() { OSMAccessRestriction restriction; restriction.set_type(AccessType::kMaxWidth); @@ -725,6 +1009,24 @@ struct graph_callback : public OSMPBF::Callback { osmdata_.access_restrictions.insert( AccessRestrictionsMultiMap::value_type(osmid_, restriction)); }; + tag_handlers_["maxwidth_forward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kMaxWidth); + restriction.set_value(std::stof(tag_.second) * 100); + restriction.set_modes(kTruckAccess | kAutoAccess | kHOVAccess | kTaxiAccess | kBusAccess); + restriction.set_direction(AccessRestrictionDirection::kForward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; + tag_handlers_["maxwidth_backward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kMaxWidth); + restriction.set_value(std::stof(tag_.second) * 100); + restriction.set_modes(kTruckAccess | kAutoAccess | kHOVAccess | kTaxiAccess | kBusAccess); + restriction.set_direction(AccessRestrictionDirection::kBackward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; tag_handlers_["maxlength"] = [this]() { OSMAccessRestriction restriction; restriction.set_type(AccessType::kMaxLength); @@ -733,6 +1035,24 @@ struct graph_callback : public OSMPBF::Callback { osmdata_.access_restrictions.insert( AccessRestrictionsMultiMap::value_type(osmid_, restriction)); }; + tag_handlers_["maxlength_forward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kMaxLength); + restriction.set_value(std::stof(tag_.second) * 100); + restriction.set_modes(kTruckAccess); + restriction.set_direction(AccessRestrictionDirection::kForward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; + tag_handlers_["maxlength_backward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kMaxLength); + restriction.set_value(std::stof(tag_.second) * 100); + restriction.set_modes(kTruckAccess); + restriction.set_direction(AccessRestrictionDirection::kBackward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; tag_handlers_["maxweight"] = [this]() { OSMAccessRestriction restriction; restriction.set_type(AccessType::kMaxWeight); @@ -741,6 +1061,24 @@ struct graph_callback : public OSMPBF::Callback { osmdata_.access_restrictions.insert( AccessRestrictionsMultiMap::value_type(osmid_, restriction)); }; + tag_handlers_["maxweight_forward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kMaxWeight); + restriction.set_value(std::stof(tag_.second) * 100); + restriction.set_modes(kTruckAccess); + restriction.set_direction(AccessRestrictionDirection::kForward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; + tag_handlers_["maxweight_backward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kMaxWeight); + restriction.set_value(std::stof(tag_.second) * 100); + restriction.set_modes(kTruckAccess); + restriction.set_direction(AccessRestrictionDirection::kBackward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; tag_handlers_["maxaxleload"] = [this]() { OSMAccessRestriction restriction; restriction.set_type(AccessType::kMaxAxleLoad); @@ -770,109 +1108,223 @@ struct graph_callback : public OSMPBF::Callback { way_.set_hov_type(valhalla::baldr::HOVEdgeType::kHOV3); } }; - tag_handlers_["default_speed"] = [this]() { - try { - default_speed_ = std::stof(tag_.second); - has_default_speed_ = true; - } catch (const std::out_of_range& oor) { - LOG_INFO("out_of_range thrown for way id: " + std::to_string(osmid_)); + tag_handlers_["default_speed"] = [this]() { + try { + default_speed_ = std::stof(tag_.second); + has_default_speed_ = true; + } catch (const std::out_of_range& oor) { + LOG_INFO("out_of_range thrown for way id: " + std::to_string(osmid_)); + } + }; + + tag_handlers_["ref"] = [this]() { + if (!tag_.second.empty()) { + ref_ = tag_.second; + } + }; + tag_handlers_["ref:left"] = [this]() { + if (!tag_.second.empty()) + ref_left_ = tag_.second; + }; + tag_handlers_["ref:right"] = [this]() { + if (!tag_.second.empty()) + ref_right_ = tag_.second; + }; + tag_handlers_["int_ref"] = [this]() { + if (!tag_.second.empty()) { + int_ref_ = tag_.second; + } + }; + tag_handlers_["int_ref:left"] = [this]() { + if (!tag_.second.empty()) + int_ref_left_ = tag_.second; + }; + tag_handlers_["int_ref:right"] = [this]() { + if (!tag_.second.empty()) + int_ref_right_ = tag_.second; + }; + tag_handlers_["ref:pronunciation"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + if (use_direction_on_ways_) + ref_ipa_ = tag_.second; + else { + const uint8_t t = static_cast(OSMLinguistic::Type::kRef); + pronunciationMap[std::make_pair(t, ipa)] = osmdata_.name_offset_map.index(tag_.second); + } + } + }; + tag_handlers_["ref:left:pronunciation"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + ref_left_ipa_ = tag_.second; + } + }; + tag_handlers_["ref:right:pronunciation"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + ref_right_ipa_ = tag_.second; + } + }; + tag_handlers_["int_ref:pronunciation"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + if (use_direction_on_ways_) + int_ref_ipa_ = tag_.second; + else { + const uint8_t t = static_cast(OSMLinguistic::Type::kIntRef); + pronunciationMap[std::make_pair(t, ipa)] = osmdata_.name_offset_map.index(tag_.second); + } + } + }; + tag_handlers_["int_ref:left:pronunciation"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + int_ref_left_ipa_ = tag_.second; + } + }; + tag_handlers_["int_ref:right:pronunciation"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + int_ref_right_ipa_ = tag_.second; + } + }; + tag_handlers_["ref:pronunciation:nt-sampa"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + if (use_direction_on_ways_) + ref_nt_sampa_ = tag_.second; + else { + const uint8_t t = static_cast(OSMLinguistic::Type::kRef); + pronunciationMap[std::make_pair(t, nt_sampa)] = osmdata_.name_offset_map.index(tag_.second); + } + } + }; + tag_handlers_["ref:left:pronunciation:nt-sampa"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + ref_left_nt_sampa_ = tag_.second; + } + }; + tag_handlers_["ref:right:pronunciation:nt-sampa"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + ref_right_nt_sampa_ = tag_.second; + } + }; + tag_handlers_["int_ref:pronunciation:nt-sampa"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + if (use_direction_on_ways_) + int_ref_nt_sampa_ = tag_.second; + else { + const uint8_t t = static_cast(OSMLinguistic::Type::kIntRef); + pronunciationMap[std::make_pair(t, nt_sampa)] = osmdata_.name_offset_map.index(tag_.second); + } + } + }; + tag_handlers_["int_ref:left:pronunciation:nt-sampa"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + int_ref_left_nt_sampa_ = tag_.second; } }; - - tag_handlers_["ref"] = [this]() { + tag_handlers_["int_ref:right:pronunciation:nt-sampa"] = [this]() { if (!tag_.second.empty()) { - if (!use_direction_on_ways_) - way_.set_ref_index(osmdata_.name_offset_map.index(tag_.second)); - else - ref_ = tag_.second; + has_pronunciation_tags_ = true; + int_ref_right_nt_sampa_ = tag_.second; } }; - tag_handlers_["int_ref"] = [this]() { + tag_handlers_["ref:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { - if (!use_direction_on_ways_) - way_.set_int_ref_index(osmdata_.name_offset_map.index(tag_.second)); - else - int_ref_ = tag_.second; + has_pronunciation_tags_ = true; + if (use_direction_on_ways_) + ref_katakana_ = tag_.second; + else { + const uint8_t t = static_cast(OSMLinguistic::Type::kRef); + pronunciationMap[std::make_pair(t, katakana)] = osmdata_.name_offset_map.index(tag_.second); + } } }; - tag_handlers_["ref:pronunciation"] = [this]() { + tag_handlers_["ref:left:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - if (!use_direction_on_ways_) - osm_pronunciation_.set_ref_pronunciation_ipa_index( - osmdata_.name_offset_map.index(tag_.second)); - else - ref_pronunciation_ = tag_.second; + ref_left_katakana_ = tag_.second; } }; - tag_handlers_["int_ref:pronunciation"] = [this]() { + tag_handlers_["ref:right:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - if (!use_direction_on_ways_) - osm_pronunciation_.set_int_ref_pronunciation_ipa_index( - osmdata_.name_offset_map.index(tag_.second)); - else - int_ref_pronunciation_ = tag_.second; + ref_right_katakana_ = tag_.second; } }; - tag_handlers_["ref:pronunciation:nt-sampa"] = [this]() { + tag_handlers_["int_ref:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - if (!use_direction_on_ways_) - osm_pronunciation_.set_ref_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(tag_.second)); - else - ref_pronunciation_nt_sampa_ = tag_.second; + if (use_direction_on_ways_) + int_ref_katakana_ = tag_.second; + else { + const uint8_t t = static_cast(OSMLinguistic::Type::kIntRef); + pronunciationMap[std::make_pair(t, katakana)] = osmdata_.name_offset_map.index(tag_.second); + } } }; - tag_handlers_["int_ref:pronunciation:nt-sampa"] = [this]() { + tag_handlers_["int_ref:left:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - if (!use_direction_on_ways_) - osm_pronunciation_.set_int_ref_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(tag_.second)); - else - int_ref_pronunciation_nt_sampa_ = tag_.second; + int_ref_left_katakana_ = tag_.second; } }; - tag_handlers_["ref:pronunciation:x-katakana"] = [this]() { + tag_handlers_["int_ref:right:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - if (!use_direction_on_ways_) - osm_pronunciation_.set_ref_pronunciation_katakana_index( - osmdata_.name_offset_map.index(tag_.second)); - else - ref_pronunciation_katakana_ = tag_.second; + int_ref_right_katakana_ = tag_.second; } }; - tag_handlers_["int_ref:pronunciation:x-katakana"] = [this]() { + tag_handlers_["ref:pronunciation:jeita"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - if (!use_direction_on_ways_) - osm_pronunciation_.set_int_ref_pronunciation_katakana_index( - osmdata_.name_offset_map.index(tag_.second)); - else - int_ref_pronunciation_katakana_ = tag_.second; + if (use_direction_on_ways_) + ref_jeita_ = tag_.second; + else { + const uint8_t t = static_cast(OSMLinguistic::Type::kRef); + pronunciationMap[std::make_pair(t, jeita)] = osmdata_.name_offset_map.index(tag_.second); + } } }; - tag_handlers_["ref:pronunciation:x-jeita"] = [this]() { + tag_handlers_["ref:left:pronunciation:jeita"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - if (!use_direction_on_ways_) - osm_pronunciation_.set_ref_pronunciation_jeita_index( - osmdata_.name_offset_map.index(tag_.second)); - else - ref_pronunciation_jeita_ = tag_.second; + ref_left_jeita_ = tag_.second; } }; - tag_handlers_["int_ref:pronunciation:x-jeita"] = [this]() { + tag_handlers_["ref:right:pronunciation:jeita"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - if (!use_direction_on_ways_) - osm_pronunciation_.set_int_ref_pronunciation_jeita_index( - osmdata_.name_offset_map.index(tag_.second)); - else - int_ref_pronunciation_jeita_ = tag_.second; + ref_right_jeita_ = tag_.second; + } + }; + tag_handlers_["int_ref:pronunciation:jeita"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + if (use_direction_on_ways_) + int_ref_jeita_ = tag_.second; + else { + const uint8_t t = static_cast(OSMLinguistic::Type::kIntRef); + pronunciationMap[std::make_pair(t, jeita)] = osmdata_.name_offset_map.index(tag_.second); + } + } + }; + tag_handlers_["int_ref:left:pronunciation:jeita"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + int_ref_left_jeita_ = tag_.second; + } + }; + tag_handlers_["int_ref:right:pronunciation:jeita"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + int_ref_right_jeita_ = tag_.second; } }; tag_handlers_["direction"] = [this]() { @@ -899,19 +1351,19 @@ struct graph_callback : public OSMPBF::Callback { if (!tag_.second.empty() && use_direction_on_ways_) int_direction_pronunciation_nt_sampa_ = tag_.second; }; - tag_handlers_["direction:pronunciation:x-katakana"] = [this]() { + tag_handlers_["direction:pronunciation:katakana"] = [this]() { if (!tag_.second.empty() && use_direction_on_ways_) direction_pronunciation_katakana_ = tag_.second; }; - tag_handlers_["int_direction:pronunciation:x-katakana"] = [this]() { + tag_handlers_["int_direction:pronunciation:katakana"] = [this]() { if (!tag_.second.empty() && use_direction_on_ways_) int_direction_pronunciation_katakana_ = tag_.second; }; - tag_handlers_["direction:pronunciation:x-jeita"] = [this]() { + tag_handlers_["direction:pronunciation:jeita"] = [this]() { if (!tag_.second.empty() && use_direction_on_ways_) direction_pronunciation_jeita_ = tag_.second; }; - tag_handlers_["int_direction:pronunciation:x-jeita"] = [this]() { + tag_handlers_["int_direction:pronunciation:jeita"] = [this]() { if (!tag_.second.empty() && use_direction_on_ways_) int_direction_pronunciation_jeita_ = tag_.second; }; @@ -1100,274 +1552,284 @@ struct graph_callback : public OSMPBF::Callback { // }; tag_handlers_["destination"] = [this]() { if (!tag_.second.empty()) { - way_.set_destination_index(osmdata_.name_offset_map.index(tag_.second)); + destination_ = tag_.second; way_.set_exit(true); } }; tag_handlers_["destination:forward"] = [this]() { if (!tag_.second.empty()) { - way_.set_destination_forward_index(osmdata_.name_offset_map.index(tag_.second)); + destination_forward_ = tag_.second; way_.set_exit(true); } }; tag_handlers_["destination:backward"] = [this]() { if (!tag_.second.empty()) { - way_.set_destination_backward_index(osmdata_.name_offset_map.index(tag_.second)); + destination_backward_ = tag_.second; way_.set_exit(true); } }; tag_handlers_["destination:ref"] = [this]() { if (!tag_.second.empty()) { - way_.set_destination_ref_index(osmdata_.name_offset_map.index(tag_.second)); + destination_ref_ = tag_.second; way_.set_exit(true); } }; tag_handlers_["destination:ref:to"] = [this]() { if (!tag_.second.empty()) { - way_.set_destination_ref_to_index(osmdata_.name_offset_map.index(tag_.second)); + destination_ref_to_ = tag_.second; + way_.set_exit(true); + } + }; + tag_handlers_["destination:int_ref"] = [this]() { + if (!tag_.second.empty()) { + way_.set_destination_int_ref_index(osmdata_.name_offset_map.index(tag_.second)); + way_.set_exit(true); + } + }; + tag_handlers_["destination:int_ref:to"] = [this]() { + if (!tag_.second.empty()) { + way_.set_destination_int_ref_to_index(osmdata_.name_offset_map.index(tag_.second)); way_.set_exit(true); } }; tag_handlers_["destination:street"] = [this]() { if (!tag_.second.empty()) { - way_.set_destination_street_index(osmdata_.name_offset_map.index(tag_.second)); + destination_street_ = tag_.second; way_.set_exit(true); } }; tag_handlers_["destination:street:to"] = [this]() { if (!tag_.second.empty()) { - way_.set_destination_street_to_index(osmdata_.name_offset_map.index(tag_.second)); + destination_street_to_ = tag_.second; way_.set_exit(true); } }; tag_handlers_["junction:ref"] = [this]() { if (!tag_.second.empty()) { - way_.set_junction_ref_index(osmdata_.name_offset_map.index(tag_.second)); + junction_ref_ = tag_.second; + way_.set_exit(true); + } + }; + tag_handlers_["junction:name"] = [this]() { + if (!tag_.second.empty()) { + junction_name_ = tag_.second; way_.set_exit(true); } }; tag_handlers_["destination:pronunciation"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_pronunciation_ipa_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_ipa_ = tag_.second; } }; tag_handlers_["destination:forward:pronunciation"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_forward_pronunciation_ipa_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_forward_ipa_ = tag_.second; } }; tag_handlers_["destination:backward:pronunciation"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_backward_pronunciation_ipa_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_backward_ipa_ = tag_.second; } }; tag_handlers_["destination:ref:pronunciation"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_ref_pronunciation_ipa_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_ref_ipa_ = tag_.second; } }; tag_handlers_["destination:ref:to:pronunciation"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_ref_to_pronunciation_ipa_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_ref_to_ipa_ = tag_.second; } }; tag_handlers_["destination:street:pronunciation"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_street_pronunciation_ipa_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_street_ipa_ = tag_.second; } }; tag_handlers_["destination:street:to:pronunciation"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_street_to_pronunciation_ipa_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_street_to_ipa_ = tag_.second; } }; tag_handlers_["junction:ref:pronunciation"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_junction_ref_pronunciation_ipa_index( - osmdata_.name_offset_map.index(tag_.second)); + junction_ref_ipa_ = tag_.second; + } + }; + tag_handlers_["junction:name:pronunciation"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + junction_name_ipa_ = tag_.second; } }; tag_handlers_["destination:pronunciation:nt-sampa"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_nt_sampa_ = tag_.second; } }; tag_handlers_["destination:forward:pronunciation:nt-sampa"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_forward_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_forward_nt_sampa_ = tag_.second; } }; tag_handlers_["destination:backward:pronunciation:nt-sampa"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_backward_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_backward_nt_sampa_ = tag_.second; } }; tag_handlers_["destination:ref:pronunciation:nt-sampa"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_ref_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_ref_nt_sampa_ = tag_.second; } }; tag_handlers_["destination:ref:to:pronunciation:nt-sampa"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_ref_to_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_ref_to_nt_sampa_ = tag_.second; } }; tag_handlers_["destination:street:pronunciation:nt-sampa"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_street_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_street_nt_sampa_ = tag_.second; } }; tag_handlers_["destination:street:to:pronunciation:nt-sampa"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_street_to_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_street_to_nt_sampa_ = tag_.second; } }; tag_handlers_["junction:ref:pronunciation:nt-sampa"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_junction_ref_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(tag_.second)); + junction_ref_nt_sampa_ = tag_.second; + } + }; + tag_handlers_["junction:name:pronunciation:nt-sampa"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + junction_name_nt_sampa_ = tag_.second; + } + }; + tag_handlers_["destination:pronunciation:katakana"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + destination_katakana_ = tag_.second; + } + }; + tag_handlers_["destination:forward:pronunciation:katakana"] = [this]() { + if (!tag_.second.empty()) { + has_pronunciation_tags_ = true; + destination_forward_katakana_ = tag_.second; } }; - tag_handlers_["destination:pronunciation:x-katakana"] = [this]() { + tag_handlers_["destination:backward:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_pronunciation_katakana_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_backward_katakana_ = tag_.second; } }; - tag_handlers_["destination:forward:pronunciation:x-katakana"] = [this]() { + tag_handlers_["destination:ref:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_forward_pronunciation_katakana_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_ref_katakana_ = tag_.second; } }; - tag_handlers_["destination:backward:pronunciation:x-katakana"] = [this]() { + tag_handlers_["destination:ref:to:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_backward_pronunciation_katakana_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_ref_to_katakana_ = tag_.second; } }; - tag_handlers_["destination:ref:pronunciation:x-katakana"] = [this]() { + tag_handlers_["destination:street:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_ref_pronunciation_katakana_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_street_katakana_ = tag_.second; } }; - tag_handlers_["destination:ref:to:pronunciation:x-katakana"] = [this]() { + tag_handlers_["destination:street:to:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_ref_to_pronunciation_katakana_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_street_to_katakana_ = tag_.second; } }; - tag_handlers_["destination:street:pronunciation:x-katakana"] = [this]() { + tag_handlers_["junction:ref:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_street_pronunciation_katakana_index( - osmdata_.name_offset_map.index(tag_.second)); + junction_ref_katakana_ = tag_.second; } }; - tag_handlers_["destination:street:to:pronunciation:x-katakana"] = [this]() { + tag_handlers_["junction:name:pronunciation:katakana"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_street_to_pronunciation_katakana_index( - osmdata_.name_offset_map.index(tag_.second)); + junction_name_katakana_ = tag_.second; } }; - tag_handlers_["junction:ref:pronunciation:x-katakana"] = [this]() { + tag_handlers_["destination:pronunciation:jeita"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_junction_ref_pronunciation_katakana_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_jeita_ = tag_.second; } }; - tag_handlers_["destination:pronunciation:x-jeita"] = [this]() { + tag_handlers_["destination:forward:pronunciation:jeita"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_pronunciation_jeita_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_forward_jeita_ = tag_.second; } }; - tag_handlers_["destination:forward:pronunciation:x-jeita"] = [this]() { + tag_handlers_["destination:backward:pronunciation:jeita"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_forward_pronunciation_jeita_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_backward_jeita_ = tag_.second; } }; - tag_handlers_["destination:backward:pronunciation:x-jeita"] = [this]() { + tag_handlers_["destination:ref:pronunciation:jeita"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_backward_pronunciation_jeita_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_ref_jeita_ = tag_.second; } }; - tag_handlers_["destination:ref:pronunciation:x-jeita"] = [this]() { + tag_handlers_["destination:ref:to:pronunciation:jeita"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_ref_pronunciation_jeita_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_ref_to_jeita_ = tag_.second; } }; - tag_handlers_["destination:ref:to:pronunciation:x-jeita"] = [this]() { + tag_handlers_["destination:street:pronunciation:jeita"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_ref_to_pronunciation_jeita_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_street_jeita_ = tag_.second; } }; - tag_handlers_["destination:street:pronunciation:x-jeita"] = [this]() { + tag_handlers_["destination:street:to:pronunciation:jeita"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_street_pronunciation_jeita_index( - osmdata_.name_offset_map.index(tag_.second)); + destination_street_to_jeita_ = tag_.second; } }; - tag_handlers_["destination:street:to:pronunciation:x-jeita"] = [this]() { + tag_handlers_["junction:ref:pronunciation:jeita"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_destination_street_to_pronunciation_jeita_index( - osmdata_.name_offset_map.index(tag_.second)); + junction_ref_jeita_ = tag_.second; } }; - tag_handlers_["junction:ref:pronunciation:x-jeita"] = [this]() { + tag_handlers_["junction:name:pronunciation:jeita"] = [this]() { if (!tag_.second.empty()) { has_pronunciation_tags_ = true; - osm_pronunciation_.set_junction_ref_pronunciation_jeita_index( - osmdata_.name_offset_map.index(tag_.second)); + junction_name_jeita_ = tag_.second; } }; tag_handlers_["turn:lanes"] = [this]() { @@ -1525,16 +1987,37 @@ struct graph_callback : public OSMPBF::Callback { junction != results->end() && (junction->second == "named" || junction->second == "yes"); bool named_junction = false; - // Create a new node and set its attributes + const auto barrier_toll_booth = results->find("barrier"); + bool is_barrier_toll_booth = + (barrier_toll_booth != results->end()) && (barrier_toll_booth->second == "toll_booth"); + + const auto highway_toll_gantry = results->find("highway"); + bool is_highway_toll_gantry = + (highway_toll_gantry != results->end()) && (highway_toll_gantry->second == "toll_gantry"); + + bool is_toll_node = is_barrier_toll_booth || is_highway_toll_gantry; + bool named_toll_node = false; + OSMNode n; + OSMNodeLinguistic linguistics; n.set_id(osmid); n.set_latlng(lng, lat); bool intersection = false; if (is_highway_junction) { n.set_type(NodeType::kMotorWayJunction); } + ref_ = ref_language_ = ref_w_lang_ = name_ = language_ = name_w_lang_ = {}; + name_ipa_ = ref_ipa_ = name_nt_sampa_ = ref_nt_sampa_ = name_katakana_ = ref_katakana_ = + name_jeita_ = ref_jeita_ = {}; for (const auto& tag : *results) { + tag_ = tag; + + bool is_lang_pronunciation = false; + std::size_t found = tag_.first.find(":pronunciation"); + if (found != std::string::npos) + is_lang_pronunciation = true; + // TODO: instead of checking this, we should delete these tag/values completely in lua // and save our CPUs the wasted time of iterating over them again for nothing auto hasTag = !tag.second.empty(); @@ -1577,30 +2060,36 @@ struct graph_callback : public OSMPBF::Callback { n.set_exit_to_index(osmdata_.node_names.index(tag.second)); ++osmdata_.node_exit_to_count; } else if (tag.first == "ref" && is_highway_junction && hasTag) { - // Add the name to the unique node names list and store its index in the OSM node - n.set_ref_index(osmdata_.node_names.index(tag.second)); + // Add the name to the unique node names list and store its index in the OSM node. + + // TODO Need to process ref:right and ref:left and add correctly in graphbuilder. + // ref:left means the ref for the left exit and ref:right means the ref for the + // right exit at a split + ref_ = tag.second; ++osmdata_.node_ref_count; - } else if (tag.first == "name" && (is_highway_junction || maybe_named_junction) && hasTag) { + } else if (tag.first == "name" && + (is_highway_junction || maybe_named_junction || is_toll_node) && hasTag) { // Add the name to the unique node names list and store its index in the OSM node - n.set_name_index(osmdata_.node_names.index(tag.second)); + name_ = tag.second; ++osmdata_.node_name_count; named_junction = maybe_named_junction; + named_toll_node = is_toll_node; } else if (tag.first == "name:pronunciation") { - n.set_name_pronunciation_ipa_index(osmdata_.node_names.index(tag.second)); + name_ipa_ = tag.second; } else if (tag.first == "name:pronunciation:nt-sampa") { - n.set_name_pronunciation_nt_sampa_index(osmdata_.node_names.index(tag.second)); - } else if (tag.first == "name:pronunciation:x-katakana") { - n.set_name_pronunciation_katakana_index(osmdata_.node_names.index(tag.second)); - } else if (tag.first == "name:pronunciation:x-jeita") { - n.set_name_pronunciation_jeita_index(osmdata_.node_names.index(tag.second)); + name_nt_sampa_ = tag.second; + } else if (tag.first == "name:pronunciation:katakana") { + name_katakana_ = tag.second; + } else if (tag.first == "name:pronunciation:jeita") { + name_jeita_ = tag.second; } else if (tag.first == "ref:pronunciation") { - n.set_ref_pronunciation_ipa_index(osmdata_.node_names.index(tag.second)); + ref_ipa_ = tag.second; } else if (tag.first == "ref:pronunciation:nt-sampa") { - n.set_ref_pronunciation_nt_sampa_index(osmdata_.node_names.index(tag.second)); - } else if (tag.first == "ref:pronunciation:x-katakana") { - n.set_ref_pronunciation_katakana_index(osmdata_.node_names.index(tag.second)); - } else if (tag.first == "ref:pronunciation:x-jeita") { - n.set_ref_pronunciation_jeita_index(osmdata_.node_names.index(tag.second)); + ref_nt_sampa_ = tag.second; + } else if (tag.first == "ref:pronunciation:katakana") { + ref_katakana_ = tag.second; + } else if (tag.first == "ref:pronunciation:jeita") { + ref_jeita_ = tag.second; } else if (tag.first == "gate" && tag.second == "true") { osmdata_.edge_count += !intersection; intersection = true; @@ -1644,11 +2133,84 @@ struct graph_callback : public OSMPBF::Callback { n.set_tagged_access(std::stoi(tag.second)); } else if (tag.first == "private") { n.set_private_access(tag.second == "true"); + } else if (!is_lang_pronunciation) { + if (boost::algorithm::starts_with(tag.first, "name:") && + (is_highway_junction || maybe_named_junction || is_toll_node) && hasTag) { + ProcessNameTag(tag_, name_w_lang_, language_); + ++osmdata_.node_name_count; + named_junction = maybe_named_junction; + } else if (boost::algorithm::starts_with(tag_.first, "ref:")) { + ProcessNameTag(tag_, ref_w_lang_, ref_language_); + ++osmdata_.node_ref_count; + } + } else { + std::string t = tag_.first; + PronunciationAlphabet alphabet = PronunciationAlphabet::kIpa; + std::size_t found = t.find(":nt-sampa"); + if (found != std::string::npos) + alphabet = PronunciationAlphabet::kNtSampa; + else { + found = t.find(":katakana"); + if (found != std::string::npos) + alphabet = PronunciationAlphabet::kKatakana; + else { + found = t.find(":jeita"); + if (found != std::string::npos) + alphabet = PronunciationAlphabet::kJeita; + } + } + if (boost::algorithm::starts_with(t, "name:")) { + ProcessPronunciationTag(OSMLinguistic::Type::kNodeName, alphabet, &linguistics); + } else if (boost::algorithm::starts_with(t, "ref:")) { + ProcessPronunciationTag(OSMLinguistic::Type::kNodeRef, alphabet, &linguistics); + } } } - // If we ended up storing a name for a regular junction flag that - n.set_named_intersection(named_junction); + // begin name logic + std::string l = language_; + ProcessName(name_w_lang_, name_, language_); + n.set_name_index(osmdata_.node_names.index(name_)); + linguistics.set_name_lang_index(osmdata_.node_names.index(language_)); + + // begin ref logic + l = ref_language_; + ProcessName(ref_w_lang_, ref_, ref_language_); + n.set_ref_index(osmdata_.node_names.index(ref_)); + linguistics.set_ref_lang_index(osmdata_.node_names.index(ref_language_)); + + ProcessPronunciationName(OSMLinguistic::Type::kNodeName, PronunciationAlphabet::kIpa, name_ipa_, + &linguistics); + + ProcessPronunciationName(OSMLinguistic::Type::kNodeName, PronunciationAlphabet::kKatakana, + name_katakana_, &linguistics); + + ProcessPronunciationName(OSMLinguistic::Type::kNodeName, PronunciationAlphabet::kJeita, + name_jeita_, &linguistics); + + ProcessPronunciationName(OSMLinguistic::Type::kNodeName, PronunciationAlphabet::kNtSampa, + name_nt_sampa_, &linguistics); + + ProcessPronunciationName(OSMLinguistic::Type::kNodeRef, PronunciationAlphabet::kIpa, ref_ipa_, + &linguistics); + + ProcessPronunciationName(OSMLinguistic::Type::kNodeRef, PronunciationAlphabet::kKatakana, + ref_katakana_, &linguistics); + + ProcessPronunciationName(OSMLinguistic::Type::kNodeRef, PronunciationAlphabet::kJeita, ref_jeita_, + &linguistics); + + ProcessPronunciationName(OSMLinguistic::Type::kNodeRef, PronunciationAlphabet::kNtSampa, + ref_nt_sampa_, &linguistics); + + if (!linguistics.isEmpty()) { + n.set_linguistic_info_index(osmdata_.node_linguistic_count); + node_linguistics_->push_back(linguistics); + ++osmdata_.node_linguistic_count; + } + + // Different types of named nodes are tagged as a named intersection + n.set_named_intersection(named_junction || named_toll_node); // If way parsing marked it as the beginning or end of a way (dead ends) we'll keep that too sequence::iterator element = (*way_nodes_)[current_way_node_index_]; @@ -1686,6 +2248,7 @@ struct graph_callback : public OSMPBF::Callback { virtual void way_callback(const uint64_t osmid, const OSMPBF::Tags& tags, const std::vector& nodes) override { + osmid_ = osmid; // unsorted extracts are just plain nasty, so they can bugger off! @@ -1786,21 +2349,97 @@ struct graph_callback : public OSMPBF::Callback { default_speed_ = 0.0f, max_speed_ = 0.0f; average_speed_ = 0.0f, advisory_speed_ = 0.0f; has_default_speed_ = false, has_max_speed_ = false; - has_average_speed_ = false, has_advisory_speed_ = false; - has_surface_ = true; - name_ = {}, service_ = {}, amenity_ = {}; - + has_average_speed_ = false, has_advisory_speed_ = false, has_surface_ = true, + + name_ = language_ = name_w_lang_ = service_ = amenity_ = name_left_ = name_right_ = lang_left_ = + lang_right_ = name_left_w_lang_ = name_right_w_lang_ = {}; + + name_forward_ = name_backward_ = lang_forward_ = lang_backward_ = name_forward_w_lang_ = + name_backward_w_lang_ = {}; + + ref_ipa_ = ref_left_ipa_ = ref_right_ipa_ = ref_katakana_ = ref_left_katakana_ = + ref_right_katakana_ = ref_jeita_ = ref_left_jeita_ = ref_right_jeita_ = ref_nt_sampa_ = + ref_left_nt_sampa_ = ref_right_nt_sampa_ = {}; + + int_ref_ipa_ = int_ref_left_ipa_ = int_ref_right_ipa_ = int_ref_katakana_ = + int_ref_left_katakana_ = int_ref_right_katakana_ = int_ref_jeita_ = int_ref_left_jeita_ = + int_ref_right_jeita_ = int_ref_nt_sampa_ = int_ref_left_nt_sampa_ = + int_ref_right_nt_sampa_ = {}; + + name_ipa_ = name_left_ipa_ = name_right_ipa_ = name_forward_ipa_ = name_backward_ipa_ = + name_katakana_ = name_left_katakana_ = name_right_katakana_ = name_forward_katakana_ = + name_backward_katakana_ = name_jeita_ = name_left_jeita_ = name_right_jeita_ = + name_forward_jeita_ = name_backward_jeita_ = name_nt_sampa_ = name_left_nt_sampa_ = + name_right_nt_sampa_ = name_forward_nt_sampa_ = name_backward_nt_sampa_ = {}; + + alt_name_ipa_ = alt_name_left_ipa_ = alt_name_right_ipa_ = alt_name_katakana_ = + alt_name_left_katakana_ = alt_name_right_katakana_ = alt_name_jeita_ = alt_name_left_jeita_ = + alt_name_right_jeita_ = alt_name_nt_sampa_ = alt_name_left_nt_sampa_ = + alt_name_right_nt_sampa_ = {}; + + official_name_ipa_ = official_name_left_ipa_ = official_name_right_ipa_ = + official_name_katakana_ = official_name_left_katakana_ = official_name_right_katakana_ = + official_name_jeita_ = official_name_left_jeita_ = official_name_right_jeita_ = + official_name_nt_sampa_ = official_name_left_nt_sampa_ = + official_name_right_nt_sampa_ = {}; + + tunnel_name_ipa_ = tunnel_name_left_ipa_ = tunnel_name_right_ipa_ = tunnel_name_katakana_ = + tunnel_name_left_katakana_ = tunnel_name_right_katakana_ = tunnel_name_jeita_ = + tunnel_name_left_jeita_ = tunnel_name_right_jeita_ = tunnel_name_nt_sampa_ = + tunnel_name_left_nt_sampa_ = tunnel_name_right_nt_sampa_ = {}; + + official_name_ = official_language_ = official_name_w_lang_ = official_name_left_ = + official_name_right_ = official_lang_left_ = official_lang_right_ = + official_name_left_w_lang_ = official_name_right_w_lang_ = {}; + + tunnel_name_ = tunnel_language_ = tunnel_name_w_lang_ = tunnel_name_left_ = tunnel_name_right_ = + tunnel_lang_left_ = tunnel_lang_right_ = tunnel_name_left_w_lang_ = + tunnel_name_right_w_lang_ = {}; + + alt_name_ = alt_language_ = alt_name_w_lang_ = alt_name_left_ = alt_name_right_ = alt_lang_left_ = + alt_lang_right_ = alt_name_left_w_lang_ = alt_name_right_w_lang_ = {}; + + destination_ = destination_language_ = destination_w_lang_ = destination_forward_ = + destination_forward_language_ = destination_forward_w_lang_ = destination_backward_ = + destination_backward_language_ = destination_backward_w_lang_ = {}; + + destination_ref_ = destination_ref_language_ = destination_ref_w_lang_ = destination_ref_to_ = + destination_ref_to_language_ = destination_ref_to_w_lang_ = destination_street_ = + destination_street_language_ = destination_street_w_lang_ = destination_street_to_ = + destination_street_to_language_ = destination_street_to_w_lang_ = {}; + + junction_ref_ = junction_ref_language_ = junction_ref_w_lang_ = junction_name_ = + junction_name_language_ = junction_name_w_lang_ = {}; + + destination_ipa_ = destination_forward_ipa_ = destination_backward_ipa_ = destination_katakana_ = + destination_forward_katakana_ = destination_backward_katakana_ = destination_jeita_ = + destination_forward_jeita_ = destination_backward_jeita_ = destination_nt_sampa_ = + destination_forward_nt_sampa_ = destination_backward_nt_sampa_ = {}; + + destination_ref_ipa_ = destination_ref_to_ipa_ = destination_street_ipa_ = + destination_street_to_ipa_ = junction_ref_ipa_ = junction_name_ipa_ = {}; + destination_ref_nt_sampa_ = destination_ref_to_nt_sampa_ = destination_street_nt_sampa_ = + destination_street_to_nt_sampa_ = junction_ref_nt_sampa_ = junction_name_nt_sampa_ = {}; + destination_ref_katakana_ = destination_ref_to_katakana_ = destination_street_katakana_ = + destination_street_to_katakana_ = junction_ref_katakana_ = junction_name_katakana_ = {}; + destination_ref_jeita_ = destination_ref_to_jeita_ = destination_street_jeita_ = + destination_street_to_jeita_ = junction_ref_jeita_ = junction_name_jeita_ = {}; // Process tags way_ = OSMWay{osmid_}; way_.set_node_count(nodes.size()); osm_access_ = OSMAccess{osmid_}; - osm_pronunciation_ = OSMPronunciation{osmid_}; - + pronunciationMap.clear(); + langMap.clear(); has_user_tags_ = false, has_pronunciation_tags_ = false; - ref_ = int_ref_ = direction_ = int_direction_ = {}; - ref_pronunciation_ = int_ref_pronunciation_ = direction_pronunciation_ = - int_direction_pronunciation_ = {}; + ref_ = ref_language_ = ref_w_lang_ = ref_left_ = ref_right_ = ref_lang_left_ = ref_lang_right_ = + ref_left_w_lang_ = ref_right_w_lang_ = {}; + + int_ref_ = int_ref_language_ = int_ref_w_lang_ = int_ref_left_ = int_ref_right_ = + int_ref_lang_left_ = int_ref_lang_right_ = int_ref_left_w_lang_ = int_ref_right_w_lang_ = {}; + + direction_ = int_direction_ = {}; + direction_pronunciation_ = int_direction_pronunciation_ = {}; const auto& surface_exists = results.find("surface"); has_surface_tag_ = (surface_exists != results.end()); @@ -1812,6 +2451,12 @@ struct graph_callback : public OSMPBF::Callback { for (const auto& kv : results) { tag_ = kv; + + bool is_lang_pronunciation = false; + std::size_t found = tag_.first.find(":pronunciation"); + if (found != std::string::npos) + is_lang_pronunciation = true; + const auto it = tag_handlers_.find(tag_.first); if (it != tag_handlers_.end()) { try { @@ -1856,10 +2501,16 @@ struct graph_callback : public OSMPBF::Callback { if (tokens.size() == 2 && tmp.size()) { uint16_t mode = 0; - if (boost::algorithm::starts_with(tag_.first, "motorcar:conditional") || - boost::algorithm::starts_with(tag_.first, "motor_vehicle:conditional")) { + if (boost::algorithm::starts_with(tag_.first, "motor_vehicle:conditional")) { mode = (kAutoAccess | kTruckAccess | kEmergencyAccess | kTaxiAccess | kBusAccess | kHOVAccess | kMopedAccess | kMotorcycleAccess); + } else if (boost::algorithm::starts_with(tag_.first, "motorcar:conditional")) { + if (type == AccessType::kTimedAllowed) { + mode = kAutoAccess | kHOVAccess | kTaxiAccess; + } else { + mode = (kAutoAccess | kTruckAccess | kEmergencyAccess | kTaxiAccess | kBusAccess | + kHOVAccess); + } } else if (boost::algorithm::starts_with(tag_.first, "bicycle:conditional")) { mode = kBicycleAccess; } else if (boost::algorithm::starts_with(tag_.first, "foot:conditional") || @@ -1899,6 +2550,176 @@ struct graph_callback : public OSMPBF::Callback { } } } + } else if (!is_lang_pronunciation) { + if (boost::algorithm::starts_with(tag_.first, "name:left:")) { + ProcessLeftRightNameTag(tag_, name_left_w_lang_, lang_left_); + } else if (boost::algorithm::starts_with(tag_.first, "name:right:")) { + ProcessLeftRightNameTag(tag_, name_right_w_lang_, lang_right_); + } else if (boost::algorithm::starts_with(tag_.first, "name:forward:")) { + ProcessLeftRightNameTag(tag_, name_forward_w_lang_, lang_forward_); + } else if (boost::algorithm::starts_with(tag_.first, "name:backward:")) { + ProcessLeftRightNameTag(tag_, name_backward_w_lang_, lang_backward_); + } else if (boost::algorithm::starts_with(tag_.first, "name:")) { + ProcessNameTag(tag_, name_w_lang_, language_); + } else if (boost::algorithm::starts_with(tag_.first, "official_name:left:")) { + ProcessLeftRightNameTag(tag_, official_name_left_w_lang_, official_lang_left_); + } else if (boost::algorithm::starts_with(tag_.first, "official_name:right:")) { + ProcessLeftRightNameTag(tag_, official_name_right_w_lang_, official_lang_right_); + } else if (boost::algorithm::starts_with(tag_.first, "official_name:")) { + ProcessNameTag(tag_, official_name_w_lang_, official_language_); + } else if (allow_alt_name_ && boost::algorithm::starts_with(tag_.first, "alt_name:left:")) { + ProcessLeftRightNameTag(tag_, alt_name_left_w_lang_, alt_lang_left_); + } else if (allow_alt_name_ && boost::algorithm::starts_with(tag_.first, "alt_name:right:")) { + ProcessLeftRightNameTag(tag_, alt_name_right_w_lang_, alt_lang_right_); + } else if (allow_alt_name_ && boost::algorithm::starts_with(tag_.first, "alt_name:")) { + ProcessNameTag(tag_, alt_name_w_lang_, alt_language_); + } else if (boost::algorithm::starts_with(tag_.first, "ref:left:")) { + ProcessLeftRightNameTag(tag_, ref_left_w_lang_, ref_lang_left_); + } else if (boost::algorithm::starts_with(tag_.first, "ref:right:")) { + ProcessLeftRightNameTag(tag_, ref_right_w_lang_, ref_lang_right_); + } else if (boost::algorithm::starts_with(tag_.first, "ref:")) { + ProcessNameTag(tag_, ref_w_lang_, ref_language_); + } else if (boost::algorithm::starts_with(tag_.first, "int_ref:left:")) { + ProcessLeftRightNameTag(tag_, int_ref_left_w_lang_, int_ref_lang_left_); + } else if (boost::algorithm::starts_with(tag_.first, "int_ref:right:")) { + ProcessLeftRightNameTag(tag_, int_ref_right_w_lang_, int_ref_lang_right_); + } else if (boost::algorithm::starts_with(tag_.first, "int_ref:")) { + ProcessNameTag(tag_, int_ref_w_lang_, int_ref_language_); + } else if (boost::algorithm::starts_with(tag_.first, "tunnel:name:left:")) { + ProcessLeftRightNameTag(tag_, tunnel_name_left_w_lang_, tunnel_lang_left_); + } else if (boost::algorithm::starts_with(tag_.first, "tunnel:name:right:")) { + ProcessLeftRightNameTag(tag_, tunnel_name_right_w_lang_, tunnel_lang_right_); + } else if (boost::algorithm::starts_with(tag_.first, "tunnel:name:")) { + ProcessNameTag(tag_, tunnel_name_w_lang_, tunnel_language_); + } else if (boost::algorithm::starts_with(tag_.first, "destination:backward:")) { + ProcessNameTag(tag_, destination_backward_w_lang_, destination_backward_language_); + } else if (boost::algorithm::starts_with(tag_.first, "destination:forward:")) { + ProcessNameTag(tag_, destination_forward_w_lang_, destination_forward_language_); + } else if (boost::algorithm::starts_with(tag_.first, "destination:ref:to:")) { + ProcessNameTag(tag_, destination_ref_to_w_lang_, destination_ref_to_language_); + } else if (boost::algorithm::starts_with(tag_.first, "destination:ref:")) { + ProcessNameTag(tag_, destination_ref_w_lang_, destination_ref_language_); + } else if (boost::algorithm::starts_with(tag_.first, "destination:street:to:")) { + ProcessNameTag(tag_, destination_street_to_w_lang_, destination_street_to_language_); + } else if (boost::algorithm::starts_with(tag_.first, "destination:street:")) { + ProcessNameTag(tag_, destination_street_w_lang_, destination_street_language_); + } else if (boost::algorithm::starts_with(tag_.first, "destination:")) { + ProcessNameTag(tag_, destination_w_lang_, destination_language_); + } else if (boost::algorithm::starts_with(tag_.first, "junction:ref:")) { + ProcessNameTag(tag_, junction_ref_w_lang_, junction_ref_language_); + } else if (boost::algorithm::starts_with(tag_.first, "junction:name:")) { + ProcessNameTag(tag_, junction_name_w_lang_, junction_name_language_); + } + } else { // is_lang_pronunciation = true + std::string t = tag_.first; + PronunciationAlphabet alphabet = PronunciationAlphabet::kIpa; + std::size_t found = t.find(":nt-sampa"); + if (found != std::string::npos) + alphabet = PronunciationAlphabet::kNtSampa; + else { + found = t.find(":katakana"); + if (found != std::string::npos) + alphabet = PronunciationAlphabet::kKatakana; + else { + found = t.find(":jeita"); + if (found != std::string::npos) + alphabet = PronunciationAlphabet::kJeita; + } + } + + if (boost::algorithm::starts_with(t, "name:left:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kNameLeft, alphabet); + } else if (boost::algorithm::starts_with(t, "name:right:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kNameRight, alphabet); + } else if (boost::algorithm::starts_with(t, "name:forward:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kNameForward, alphabet); + } else if (boost::algorithm::starts_with(t, "name:backward:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kNameBackward, alphabet); + } else if (boost::algorithm::starts_with(t, "name:")) { + ProcessPronunciationTag(OSMLinguistic::Type::kName, alphabet); + } else if (boost::algorithm::starts_with(t, "official_name:left:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kOfficialNameLeft, alphabet); + } else if (boost::algorithm::starts_with(t, "official_name:right:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kOfficialNameRight, alphabet); + } else if (boost::algorithm::starts_with(t, "official_name:")) { + ProcessPronunciationTag(OSMLinguistic::Type::kOfficialName, alphabet); + } else if (allow_alt_name_ && boost::algorithm::starts_with(t, "alt_name:left:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kAltNameLeft, alphabet); + } else if (allow_alt_name_ && boost::algorithm::starts_with(t, "alt_name:right:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kAltNameRight, alphabet); + } else if (allow_alt_name_ && boost::algorithm::starts_with(t, "alt_name:")) { + ProcessPronunciationTag(OSMLinguistic::Type::kAltName, alphabet); + } else if (boost::algorithm::starts_with(t, "ref:left:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kRefLeft, alphabet); + } else if (boost::algorithm::starts_with(t, "ref:right:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kRefRight, alphabet); + } else if (boost::algorithm::starts_with(t, "ref:")) { + ProcessPronunciationTag(OSMLinguistic::Type::kRef, alphabet); + } else if (boost::algorithm::starts_with(t, "int_ref:left:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kIntRefLeft, alphabet); + } else if (boost::algorithm::starts_with(t, "int_ref:right:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kIntRefRight, alphabet); + } else if (boost::algorithm::starts_with(t, "int_ref:")) { + ProcessPronunciationTag(OSMLinguistic::Type::kIntRef, alphabet); + } else if (boost::algorithm::starts_with(t, "tunnel:name:left:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kTunnelNameLeft, alphabet); + } else if (boost::algorithm::starts_with(t, "tunnel:name:right:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kTunnelNameRight, alphabet); + } else if (boost::algorithm::starts_with(t, "tunnel:name:")) { + ProcessPronunciationTag(OSMLinguistic::Type::kTunnelName, alphabet); + } else if (boost::algorithm::starts_with(t, "destination:forward:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kDestinationForward, alphabet); + } else if (boost::algorithm::starts_with(t, "destination:backward:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kDestinationBackward, alphabet); + } else if (boost::algorithm::starts_with(tag_.first, "destination:ref:to:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kDestinationRefTo, alphabet); + } else if (boost::algorithm::starts_with(tag_.first, "destination:ref:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kDestinationRef, alphabet); + } else if (boost::algorithm::starts_with(tag_.first, "destination:street:to:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kDestinationStreetTo, alphabet); + } else if (boost::algorithm::starts_with(tag_.first, "destination:street:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kDestinationStreet, alphabet); + } else if (boost::algorithm::starts_with(t, "destination:")) { + ProcessPronunciationTag(OSMLinguistic::Type::kDestination, alphabet); + } else if (boost::algorithm::starts_with(tag_.first, "junction:ref:")) { + ProcessLeftRightPronunciationTag(OSMLinguistic::Type::kJunctionRef, alphabet); + } else if (boost::algorithm::starts_with(tag_.first, "junction:name:")) { + ProcessPronunciationTag(OSMLinguistic::Type::kJunctionName, alphabet); + } + } + } + + if (!use_direction_on_ways_) { + + uint8_t alpha = static_cast(PronunciationAlphabet::kIpa); + uint8_t right = static_cast(OSMLinguistic::Type::kRefRight); + uint8_t left = static_cast(OSMLinguistic::Type::kRefLeft); + + if (get_pronunciation_index(right, alpha) && get_pronunciation_index(left, alpha)) { + uint8_t type = static_cast(OSMLinguistic::Type::kRef); + ref_ipa_ = osmdata_.name_offset_map.name(get_pronunciation_index(type, alpha)); + pronunciationMap[std::make_pair(type, alpha)] = 0; + } + + alpha = static_cast(PronunciationAlphabet::kKatakana); + if (get_pronunciation_index(right, alpha) && get_pronunciation_index(left, alpha)) { + uint8_t type = static_cast(OSMLinguistic::Type::kRef); + ref_katakana_ = osmdata_.name_offset_map.name(get_pronunciation_index(type, alpha)); + pronunciationMap[std::make_pair(type, alpha)] = 0; + } + + alpha = static_cast(PronunciationAlphabet::kJeita); + if (get_pronunciation_index(right, alpha) && get_pronunciation_index(left, alpha)) { + uint8_t type = static_cast(OSMLinguistic::Type::kRef); + ref_jeita_ = osmdata_.name_offset_map.name(get_pronunciation_index(type, alpha)); + pronunciationMap[std::make_pair(type, alpha)] = 0; + } + + alpha = static_cast(PronunciationAlphabet::kNtSampa); + if (get_pronunciation_index(right, alpha) && get_pronunciation_index(left, alpha)) { + uint8_t type = static_cast(OSMLinguistic::Type::kRef); + ref_nt_sampa_ = osmdata_.name_offset_map.name(get_pronunciation_index(type, alpha)); + pronunciationMap[std::make_pair(type, alpha)] = 0; } } @@ -1919,37 +2740,36 @@ struct graph_callback : public OSMPBF::Callback { if (!int_ref_.empty()) ProcessDirection(true); - if (!ref_pronunciation_.empty()) + if (!ref_ipa_.empty()) ProcessDirectionPronunciation(PronunciationAlphabet::kIpa, false); - if (!int_ref_pronunciation_.empty()) + if (!int_ref_ipa_.empty()) ProcessDirectionPronunciation(PronunciationAlphabet::kIpa, true); - if (!ref_pronunciation_katakana_.empty()) - ProcessDirectionPronunciation(PronunciationAlphabet::kXKatakana, false); + if (!ref_katakana_.empty()) + ProcessDirectionPronunciation(PronunciationAlphabet::kKatakana, false); - if (!int_ref_pronunciation_katakana_.empty()) - ProcessDirectionPronunciation(PronunciationAlphabet::kXKatakana, true); + if (!int_ref_katakana_.empty()) + ProcessDirectionPronunciation(PronunciationAlphabet::kKatakana, true); - if (!ref_pronunciation_nt_sampa_.empty()) + if (!ref_nt_sampa_.empty()) ProcessDirectionPronunciation(PronunciationAlphabet::kNtSampa, false); - if (!int_ref_pronunciation_nt_sampa_.empty()) + if (!int_ref_nt_sampa_.empty()) ProcessDirectionPronunciation(PronunciationAlphabet::kNtSampa, true); - if (!ref_pronunciation_jeita_.empty()) - ProcessDirectionPronunciation(PronunciationAlphabet::kXJeita, false); + if (!ref_jeita_.empty()) + ProcessDirectionPronunciation(PronunciationAlphabet::kJeita, false); - if (!int_ref_pronunciation_jeita_.empty()) - ProcessDirectionPronunciation(PronunciationAlphabet::kXJeita, true); + if (!int_ref_jeita_.empty()) + ProcessDirectionPronunciation(PronunciationAlphabet::kJeita, true); } // add int_refs to the end of the refs for now. makes sure that we don't add dups. - if (way_.int_ref_index()) { - std::string tmp = osmdata_.name_offset_map.name(way_.ref_index()); - + if (!int_ref_.empty()) { + std::string tmp = ref_; std::vector rs = GetTagTokens(tmp); - std::vector is = GetTagTokens(osmdata_.name_offset_map.name(way_.int_ref_index())); + std::vector is = GetTagTokens(int_ref_); bool bFound = false; for (auto& i : is) { @@ -1968,7 +2788,7 @@ struct graph_callback : public OSMPBF::Callback { bFound = false; } if (!tmp.empty()) { - way_.set_ref_index(osmdata_.name_offset_map.index(tmp)); + ref_ = tmp; } // no matter what, clear out the int_ref. way_.set_int_ref_index(0); @@ -2147,40 +2967,692 @@ struct graph_callback : public OSMPBF::Callback { way_.set_speed(default_speed_); } - // ferries / auto trains need to be set to highway cut off in config. - if (way_.ferry() || way_.rail()) { - way_.set_road_class(highway_cutoff_rc_); + // ferries / auto trains need to be set to highway cut off in config. + if (way_.ferry() || way_.rail()) { + way_.set_road_class(highway_cutoff_rc_); + } + + // Delete the name from from name field if it exists in the ref. + if (!name_.empty() && !ref_.empty()) { + std::vector names = GetTagTokens(name_); + std::vector refs = GetTagTokens(ref_); + bool bFound = false; + + std::string tmp; + + for (auto& name : names) { + for (auto& ref : refs) { + if (name == ref) { + bFound = true; + break; + } + } + if (!bFound) { + if (!tmp.empty()) { + tmp += ";"; + } + tmp += name; + } + bFound = false; + } + if (!tmp.empty()) { + name_ = tmp; + } else + name_ = ""; + } + + // edge case. name = name: and we contain a - or / + // see https://www.openstreetmap.org/way/816515178#map=16/42.7888/-1.6391 + if (name_ == name_w_lang_ && !name_.empty() && GetTagTokens(language_).size() == 1) { + bool process = false; + std::vector names; + + if (name_w_lang_.find(" - ") != std::string::npos) { + names = GetTagTokens(name_w_lang_, " - "); + process = true; + } else if (name_w_lang_.find(" / ") != std::string::npos) { + names = GetTagTokens(name_w_lang_, " / "); + process = true; + } + + if (process) { + std::string tmp; + std::string l; + + for (auto& name : names) { + if (!tmp.empty()) { + tmp += ";"; + l += ";"; + } + tmp += name; + l += language_; + } + if (!tmp.empty()) { + name_ = tmp; + name_w_lang_ = tmp; + language_ = l; + } + } + } + + // begin name logic + std::string l = language_; + ProcessName(name_w_lang_, name_, language_); + way_.set_name_index(osmdata_.name_offset_map.index(name_)); + way_.set_name_lang_index(osmdata_.name_offset_map.index(language_)); + + ProcessLRFBName(name_left_w_lang_, name_w_lang_, l, name_left_, lang_left_); + way_.set_name_left_index(osmdata_.name_offset_map.index(name_left_)); + way_.set_name_left_lang_index(osmdata_.name_offset_map.index(lang_left_)); + + ProcessLRFBName(name_right_w_lang_, name_w_lang_, l, name_right_, lang_right_); + way_.set_name_right_index(osmdata_.name_offset_map.index(name_right_)); + way_.set_name_right_lang_index(osmdata_.name_offset_map.index(lang_right_)); + + ProcessLRFBName(name_forward_w_lang_, name_w_lang_, l, name_forward_, lang_forward_); + way_.set_name_forward_index(osmdata_.name_offset_map.index(name_forward_)); + way_.set_name_forward_lang_index(osmdata_.name_offset_map.index(lang_forward_)); + + ProcessLRFBName(name_backward_w_lang_, name_w_lang_, l, name_backward_, lang_backward_); + way_.set_name_backward_index(osmdata_.name_offset_map.index(name_backward_)); + way_.set_name_backward_lang_index(osmdata_.name_offset_map.index(lang_backward_)); + + // begin official name logic + l = official_language_; + ProcessName(official_name_w_lang_, official_name_, official_language_); + way_.set_official_name_index(osmdata_.name_offset_map.index(official_name_)); + way_.set_official_name_lang_index(osmdata_.name_offset_map.index(official_language_)); + + ProcessLRFBName(official_name_left_w_lang_, official_name_w_lang_, l, official_name_left_, + official_lang_left_); + way_.set_official_name_left_index(osmdata_.name_offset_map.index(official_name_left_)); + way_.set_official_name_left_lang_index(osmdata_.name_offset_map.index(official_lang_left_)); + + ProcessLRFBName(official_name_right_w_lang_, official_name_w_lang_, l, official_name_right_, + official_lang_right_); + way_.set_official_name_right_index(osmdata_.name_offset_map.index(official_name_right_)); + way_.set_official_name_right_lang_index(osmdata_.name_offset_map.index(official_lang_right_)); + + // begin alt name logic + l = alt_language_; + ProcessName(alt_name_w_lang_, alt_name_, alt_language_); + way_.set_alt_name_index(osmdata_.name_offset_map.index(alt_name_)); + way_.set_alt_name_lang_index(osmdata_.name_offset_map.index(alt_language_)); + + ProcessLRFBName(alt_name_left_w_lang_, alt_name_w_lang_, l, alt_name_left_, alt_lang_left_); + way_.set_alt_name_left_index(osmdata_.name_offset_map.index(alt_name_left_)); + way_.set_alt_name_left_lang_index(osmdata_.name_offset_map.index(alt_lang_left_)); + + ProcessLRFBName(alt_name_right_w_lang_, alt_name_w_lang_, l, alt_name_right_, alt_lang_right_); + way_.set_alt_name_right_index(osmdata_.name_offset_map.index(alt_name_right_)); + way_.set_alt_name_right_lang_index(osmdata_.name_offset_map.index(alt_lang_right_)); + + // begin ref logic + l = ref_language_; + ProcessName(ref_w_lang_, ref_, ref_language_); + way_.set_ref_index(osmdata_.name_offset_map.index(ref_)); + way_.set_ref_lang_index(osmdata_.name_offset_map.index(ref_language_)); + + ProcessLRFBName(ref_left_w_lang_, ref_w_lang_, l, ref_left_, ref_lang_left_); + way_.set_ref_left_index(osmdata_.name_offset_map.index(ref_left_)); + way_.set_ref_left_lang_index(osmdata_.name_offset_map.index(ref_lang_left_)); + + ProcessLRFBName(ref_right_w_lang_, ref_w_lang_, l, ref_right_, ref_lang_right_); + way_.set_ref_right_index(osmdata_.name_offset_map.index(ref_right_)); + way_.set_ref_right_lang_index(osmdata_.name_offset_map.index(ref_lang_right_)); + + // begin int_ref logic + l = int_ref_language_; + ProcessName(int_ref_w_lang_, int_ref_, int_ref_language_); + way_.set_int_ref_index(osmdata_.name_offset_map.index(int_ref_)); + way_.set_int_ref_lang_index(osmdata_.name_offset_map.index(int_ref_language_)); + + ProcessLRFBName(int_ref_left_w_lang_, int_ref_w_lang_, l, int_ref_left_, int_ref_lang_left_); + way_.set_int_ref_left_index(osmdata_.name_offset_map.index(int_ref_left_)); + way_.set_int_ref_left_lang_index(osmdata_.name_offset_map.index(int_ref_lang_left_)); + + ProcessLRFBName(int_ref_right_w_lang_, int_ref_w_lang_, l, int_ref_right_, int_ref_lang_right_); + way_.set_int_ref_right_index(osmdata_.name_offset_map.index(int_ref_right_)); + way_.set_int_ref_right_lang_index(osmdata_.name_offset_map.index(int_ref_lang_right_)); + + // begin tunnel name logic + l = tunnel_language_; + ProcessName(tunnel_name_w_lang_, tunnel_name_, tunnel_language_); + way_.set_tunnel_name_index(osmdata_.name_offset_map.index(tunnel_name_)); + way_.set_tunnel_name_lang_index(osmdata_.name_offset_map.index(tunnel_language_)); + + ProcessLRFBName(tunnel_name_left_w_lang_, tunnel_name_w_lang_, l, tunnel_name_left_, + tunnel_lang_left_); + way_.set_tunnel_name_left_index(osmdata_.name_offset_map.index(tunnel_name_left_)); + way_.set_tunnel_name_left_lang_index(osmdata_.name_offset_map.index(tunnel_lang_left_)); + + ProcessLRFBName(tunnel_name_right_w_lang_, tunnel_name_w_lang_, l, tunnel_name_right_, + tunnel_lang_right_); + way_.set_tunnel_name_right_index(osmdata_.name_offset_map.index(tunnel_name_right_)); + way_.set_tunnel_name_right_lang_index(osmdata_.name_offset_map.index(tunnel_lang_right_)); + + uint8_t t = static_cast(OSMLinguistic::Type::kName); + uint8_t alpha = static_cast(PronunciationAlphabet::kIpa); + std::string ipa_name = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string ipa_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kKatakana); + std::string katakana_name = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string katakana_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kJeita); + std::string jeita_name = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string jeita_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kNtSampa); + std::string nt_sampa_name = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string nt_sampa_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + ProcessPronunciationName(OSMLinguistic::Type::kName, PronunciationAlphabet::kIpa, name_ipa_); + + ProcessPronunciationName(OSMLinguistic::Type::kName, PronunciationAlphabet::kKatakana, + name_katakana_); + + ProcessPronunciationName(OSMLinguistic::Type::kName, PronunciationAlphabet::kJeita, name_jeita_); + + ProcessPronunciationName(OSMLinguistic::Type::kName, PronunciationAlphabet::kNtSampa, + name_nt_sampa_); + + ProcessPronunciationLRFBName(ipa_name, ipa_lang, name_left_ipa_, OSMLinguistic::Type::kNameLeft, + PronunciationAlphabet::kIpa); + + ProcessPronunciationLRFBName(katakana_name, katakana_lang, name_left_katakana_, + OSMLinguistic::Type::kNameLeft, PronunciationAlphabet::kKatakana); + + ProcessPronunciationLRFBName(jeita_name, jeita_lang, name_left_jeita_, + OSMLinguistic::Type::kNameLeft, PronunciationAlphabet::kJeita); + + ProcessPronunciationLRFBName(nt_sampa_name, nt_sampa_lang, name_left_nt_sampa_, + OSMLinguistic::Type::kNameLeft, PronunciationAlphabet::kNtSampa); + + ProcessPronunciationLRFBName(ipa_name, ipa_lang, name_right_ipa_, OSMLinguistic::Type::kNameRight, + PronunciationAlphabet::kIpa); + + ProcessPronunciationLRFBName(katakana_name, katakana_lang, name_right_katakana_, + OSMLinguistic::Type::kNameRight, PronunciationAlphabet::kKatakana); + + ProcessPronunciationLRFBName(jeita_name, jeita_lang, name_right_jeita_, + OSMLinguistic::Type::kNameRight, PronunciationAlphabet::kJeita); + + ProcessPronunciationLRFBName(nt_sampa_name, nt_sampa_lang, name_right_nt_sampa_, + OSMLinguistic::Type::kNameRight, PronunciationAlphabet::kNtSampa); + + ProcessPronunciationLRFBName(ipa_name, ipa_lang, name_backward_ipa_, + OSMLinguistic::Type::kNameBackward, PronunciationAlphabet::kIpa); + + ProcessPronunciationLRFBName(katakana_name, katakana_lang, name_backward_katakana_, + OSMLinguistic::Type::kNameBackward, + PronunciationAlphabet::kKatakana); + + ProcessPronunciationLRFBName(jeita_name, jeita_lang, name_backward_jeita_, + OSMLinguistic::Type::kNameBackward, PronunciationAlphabet::kJeita); + + ProcessPronunciationLRFBName(nt_sampa_name, nt_sampa_lang, name_backward_nt_sampa_, + OSMLinguistic::Type::kNameBackward, PronunciationAlphabet::kNtSampa); + + ProcessPronunciationLRFBName(ipa_name, ipa_lang, name_forward_ipa_, + OSMLinguistic::Type::kNameForward, PronunciationAlphabet::kIpa); + + ProcessPronunciationLRFBName(katakana_name, katakana_lang, name_forward_katakana_, + OSMLinguistic::Type::kNameForward, PronunciationAlphabet::kKatakana); + + ProcessPronunciationLRFBName(jeita_name, jeita_lang, name_forward_jeita_, + OSMLinguistic::Type::kNameForward, PronunciationAlphabet::kJeita); + + ProcessPronunciationLRFBName(nt_sampa_name, nt_sampa_lang, name_forward_nt_sampa_, + OSMLinguistic::Type::kNameForward, PronunciationAlphabet::kNtSampa); + + t = static_cast(OSMLinguistic::Type::kAltName); + alpha = static_cast(PronunciationAlphabet::kIpa); + std::string ipa_alt_name = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string ipa_alt_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kKatakana); + std::string katakana_alt_name = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string katakana_alt_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kJeita); + std::string jeita_alt_name = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string jeita_alt_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kNtSampa); + std::string nt_sampa_alt_name = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string nt_sampa_alt_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + ProcessPronunciationName(OSMLinguistic::Type::kAltName, PronunciationAlphabet::kIpa, + alt_name_ipa_); + + ProcessPronunciationName(OSMLinguistic::Type::kAltName, PronunciationAlphabet::kKatakana, + alt_name_katakana_); + + ProcessPronunciationName(OSMLinguistic::Type::kAltName, PronunciationAlphabet::kJeita, + alt_name_jeita_); + + ProcessPronunciationName(OSMLinguistic::Type::kAltName, PronunciationAlphabet::kNtSampa, + alt_name_nt_sampa_); + + ProcessPronunciationLRFBName(ipa_alt_name, ipa_alt_lang, alt_name_left_ipa_, + OSMLinguistic::Type::kAltNameLeft, PronunciationAlphabet::kIpa); + + ProcessPronunciationLRFBName(katakana_alt_name, katakana_alt_lang, alt_name_left_katakana_, + OSMLinguistic::Type::kAltNameLeft, PronunciationAlphabet::kKatakana); + + ProcessPronunciationLRFBName(jeita_alt_name, jeita_alt_lang, alt_name_left_jeita_, + OSMLinguistic::Type::kAltNameLeft, PronunciationAlphabet::kJeita); + + ProcessPronunciationLRFBName(nt_sampa_alt_name, nt_sampa_alt_lang, alt_name_left_nt_sampa_, + OSMLinguistic::Type::kAltNameLeft, PronunciationAlphabet::kNtSampa); + + ProcessPronunciationLRFBName(ipa_alt_name, ipa_alt_lang, alt_name_right_ipa_, + OSMLinguistic::Type::kAltNameRight, PronunciationAlphabet::kIpa); + + ProcessPronunciationLRFBName(katakana_alt_name, katakana_alt_lang, alt_name_right_katakana_, + OSMLinguistic::Type::kAltNameRight, + PronunciationAlphabet::kKatakana); + + ProcessPronunciationLRFBName(jeita_alt_name, jeita_alt_lang, alt_name_right_jeita_, + OSMLinguistic::Type::kAltNameRight, PronunciationAlphabet::kJeita); + + ProcessPronunciationLRFBName(nt_sampa_alt_name, nt_sampa_alt_lang, alt_name_right_nt_sampa_, + OSMLinguistic::Type::kAltNameRight, PronunciationAlphabet::kNtSampa); + + t = static_cast(OSMLinguistic::Type::kOfficialName); + alpha = static_cast(PronunciationAlphabet::kIpa); + std::string ipa_official_name = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string ipa_official_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kKatakana); + std::string katakana_official_name = + osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string katakana_official_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kJeita); + std::string jeita_official_name = + osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string jeita_official_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kNtSampa); + std::string nt_sampa_official_name = + osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string nt_sampa_official_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + ProcessPronunciationName(OSMLinguistic::Type::kOfficialName, PronunciationAlphabet::kIpa, + official_name_ipa_); + + ProcessPronunciationName(OSMLinguistic::Type::kOfficialName, PronunciationAlphabet::kKatakana, + official_name_katakana_); + + ProcessPronunciationName(OSMLinguistic::Type::kOfficialName, PronunciationAlphabet::kJeita, + official_name_jeita_); + + ProcessPronunciationName(OSMLinguistic::Type::kOfficialName, PronunciationAlphabet::kNtSampa, + official_name_nt_sampa_); + + ProcessPronunciationLRFBName(ipa_official_name, ipa_official_lang, official_name_left_ipa_, + OSMLinguistic::Type::kOfficialNameLeft, PronunciationAlphabet::kIpa); + + ProcessPronunciationLRFBName(katakana_official_name, katakana_official_lang, + official_name_left_katakana_, OSMLinguistic::Type::kOfficialNameLeft, + PronunciationAlphabet::kKatakana); + + ProcessPronunciationLRFBName(jeita_official_name, jeita_official_lang, official_name_left_jeita_, + OSMLinguistic::Type::kOfficialNameLeft, + PronunciationAlphabet::kJeita); + + ProcessPronunciationLRFBName(nt_sampa_official_name, nt_sampa_official_lang, + official_name_left_nt_sampa_, OSMLinguistic::Type::kOfficialNameLeft, + PronunciationAlphabet::kNtSampa); + + ProcessPronunciationLRFBName(ipa_official_name, ipa_official_lang, official_name_right_ipa_, + OSMLinguistic::Type::kOfficialNameRight, + PronunciationAlphabet::kIpa); + + ProcessPronunciationLRFBName(katakana_official_name, katakana_official_lang, + official_name_right_katakana_, + OSMLinguistic::Type::kOfficialNameRight, + PronunciationAlphabet::kKatakana); + + ProcessPronunciationLRFBName(jeita_official_name, jeita_official_lang, official_name_right_jeita_, + OSMLinguistic::Type::kOfficialNameRight, + PronunciationAlphabet::kJeita); + + ProcessPronunciationLRFBName(nt_sampa_official_name, nt_sampa_official_lang, + official_name_right_nt_sampa_, + OSMLinguistic::Type::kOfficialNameRight, + PronunciationAlphabet::kNtSampa); + + t = static_cast(OSMLinguistic::Type::kTunnelName); + alpha = static_cast(PronunciationAlphabet::kIpa); + std::string ipa_tunnel_name = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string ipa_tunnel_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kKatakana); + std::string katakana_tunnel_name = + osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string katakana_tunnel_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kJeita); + std::string jeita_tunnel_name = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string jeita_tunnel_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kNtSampa); + std::string nt_sampa_tunnel_name = + osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string nt_sampa_tunnel_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + ProcessPronunciationName(OSMLinguistic::Type::kTunnelName, PronunciationAlphabet::kIpa, + tunnel_name_ipa_); + + ProcessPronunciationName(OSMLinguistic::Type::kTunnelName, PronunciationAlphabet::kKatakana, + tunnel_name_katakana_); + + ProcessPronunciationName(OSMLinguistic::Type::kTunnelName, PronunciationAlphabet::kJeita, + tunnel_name_jeita_); + + ProcessPronunciationName(OSMLinguistic::Type::kTunnelName, PronunciationAlphabet::kNtSampa, + tunnel_name_nt_sampa_); + + ProcessPronunciationLRFBName(ipa_tunnel_name, ipa_tunnel_lang, tunnel_name_left_ipa_, + OSMLinguistic::Type::kTunnelNameLeft, PronunciationAlphabet::kIpa); + + ProcessPronunciationLRFBName(katakana_tunnel_name, katakana_tunnel_lang, + tunnel_name_left_katakana_, OSMLinguistic::Type::kTunnelNameLeft, + PronunciationAlphabet::kKatakana); + + ProcessPronunciationLRFBName(jeita_tunnel_name, jeita_tunnel_lang, tunnel_name_left_jeita_, + OSMLinguistic::Type::kTunnelNameLeft, PronunciationAlphabet::kJeita); + + ProcessPronunciationLRFBName(nt_sampa_tunnel_name, nt_sampa_tunnel_lang, + tunnel_name_left_nt_sampa_, OSMLinguistic::Type::kTunnelNameLeft, + PronunciationAlphabet::kNtSampa); + + ProcessPronunciationLRFBName(ipa_tunnel_name, ipa_tunnel_lang, tunnel_name_right_ipa_, + OSMLinguistic::Type::kTunnelNameRight, PronunciationAlphabet::kIpa); + + ProcessPronunciationLRFBName(katakana_tunnel_name, katakana_tunnel_lang, + tunnel_name_right_katakana_, OSMLinguistic::Type::kTunnelNameRight, + PronunciationAlphabet::kKatakana); + + ProcessPronunciationLRFBName(jeita_tunnel_name, jeita_tunnel_lang, tunnel_name_right_jeita_, + OSMLinguistic::Type::kTunnelNameRight, + PronunciationAlphabet::kJeita); + + ProcessPronunciationLRFBName(nt_sampa_tunnel_name, nt_sampa_tunnel_lang, + tunnel_name_right_nt_sampa_, OSMLinguistic::Type::kTunnelNameRight, + PronunciationAlphabet::kNtSampa); + + t = static_cast(OSMLinguistic::Type::kRef); + alpha = static_cast(PronunciationAlphabet::kIpa); + std::string ipa_ref_name = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string ipa_ref_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kKatakana); + std::string katakana_ref_name = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string katakana_ref_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kJeita); + std::string jeita_ref_name = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string jeita_ref_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kNtSampa); + std::string nt_sampa_ref_name = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string nt_sampa_ref_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + if (!use_direction_on_ways_) { + ProcessPronunciationName(OSMLinguistic::Type::kRef, PronunciationAlphabet::kIpa, ref_ipa_); + + ProcessPronunciationName(OSMLinguistic::Type::kRef, PronunciationAlphabet::kKatakana, + ref_katakana_); + + ProcessPronunciationName(OSMLinguistic::Type::kRef, PronunciationAlphabet::kJeita, ref_jeita_); + + ProcessPronunciationName(OSMLinguistic::Type::kRef, PronunciationAlphabet::kNtSampa, + ref_nt_sampa_); } - // Delete the name from from name field if it exists in the ref. - if (!name_.empty() && way_.ref_index()) { - std::vector names = GetTagTokens(name_); - std::vector refs = GetTagTokens(osmdata_.name_offset_map.name(way_.ref_index())); - bool bFound = false; + ProcessPronunciationLRFBName(ipa_ref_name, ipa_ref_lang, ref_left_ipa_, + OSMLinguistic::Type::kRefLeft, PronunciationAlphabet::kIpa); - std::string tmp; + ProcessPronunciationLRFBName(katakana_ref_name, katakana_ref_lang, ref_left_katakana_, + OSMLinguistic::Type::kRefLeft, PronunciationAlphabet::kKatakana); - for (auto& name : names) { - for (auto& ref : refs) { - if (name == ref) { - bFound = true; - break; - } - } - if (!bFound) { - if (!tmp.empty()) { - tmp += ";"; - } - tmp += name; - } - bFound = false; - } - if (!tmp.empty()) { - way_.set_name_index(osmdata_.name_offset_map.index(tmp)); - } - } else { - way_.set_name_index(osmdata_.name_offset_map.index(name_)); - } + ProcessPronunciationLRFBName(jeita_ref_name, jeita_ref_lang, ref_left_jeita_, + OSMLinguistic::Type::kRefLeft, PronunciationAlphabet::kJeita); + + ProcessPronunciationLRFBName(nt_sampa_ref_name, nt_sampa_ref_lang, ref_left_nt_sampa_, + OSMLinguistic::Type::kRefLeft, PronunciationAlphabet::kNtSampa); + + ProcessPronunciationLRFBName(ipa_ref_name, ipa_ref_lang, ref_right_ipa_, + OSMLinguistic::Type::kRefRight, PronunciationAlphabet::kIpa); + + ProcessPronunciationLRFBName(katakana_ref_name, katakana_ref_lang, ref_right_katakana_, + OSMLinguistic::Type::kRefRight, PronunciationAlphabet::kKatakana); + + ProcessPronunciationLRFBName(jeita_ref_name, jeita_ref_lang, ref_right_jeita_, + OSMLinguistic::Type::kRefRight, PronunciationAlphabet::kJeita); + + ProcessPronunciationLRFBName(nt_sampa_ref_name, nt_sampa_ref_lang, ref_right_nt_sampa_, + OSMLinguistic::Type::kRefRight, PronunciationAlphabet::kNtSampa); + + // begin destination logic + ProcessName(destination_w_lang_, destination_, destination_language_); + way_.set_destination_index(osmdata_.name_offset_map.index(destination_)); + way_.set_destination_lang_index(osmdata_.name_offset_map.index(destination_language_)); + + ProcessName(destination_forward_w_lang_, destination_forward_, destination_forward_language_); + way_.set_destination_forward_index(osmdata_.name_offset_map.index(destination_forward_)); + way_.set_destination_forward_lang_index( + osmdata_.name_offset_map.index(destination_forward_language_)); + + ProcessName(destination_backward_w_lang_, destination_backward_, destination_backward_language_); + way_.set_destination_backward_index(osmdata_.name_offset_map.index(destination_backward_)); + way_.set_destination_backward_lang_index( + osmdata_.name_offset_map.index(destination_backward_language_)); + + ProcessName(junction_ref_w_lang_, junction_ref_, junction_ref_language_); + way_.set_junction_ref_index(osmdata_.name_offset_map.index(junction_ref_)); + way_.set_junction_ref_lang_index(osmdata_.name_offset_map.index(junction_ref_language_)); + + ProcessName(junction_name_w_lang_, junction_name_, junction_name_language_); + way_.set_junction_name_index(osmdata_.name_offset_map.index(junction_name_)); + way_.set_junction_name_lang_index(osmdata_.name_offset_map.index(junction_name_language_)); + + ProcessName(destination_ref_w_lang_, destination_ref_, destination_ref_language_); + way_.set_destination_ref_index(osmdata_.name_offset_map.index(destination_ref_)); + way_.set_destination_ref_lang_index(osmdata_.name_offset_map.index(destination_ref_language_)); + + ProcessName(destination_ref_to_w_lang_, destination_ref_to_, destination_ref_to_language_); + way_.set_destination_ref_to_index(osmdata_.name_offset_map.index(destination_ref_to_)); + way_.set_destination_ref_to_lang_index( + osmdata_.name_offset_map.index(destination_ref_to_language_)); + + ProcessName(destination_street_to_w_lang_, destination_street_to_, + destination_street_to_language_); + way_.set_destination_street_to_index(osmdata_.name_offset_map.index(destination_street_to_)); + way_.set_destination_street_to_lang_index( + osmdata_.name_offset_map.index(destination_street_to_language_)); + + ProcessName(destination_street_w_lang_, destination_street_, destination_street_language_); + way_.set_destination_street_index(osmdata_.name_offset_map.index(destination_street_)); + way_.set_destination_street_lang_index( + osmdata_.name_offset_map.index(destination_street_language_)); + + t = static_cast(OSMLinguistic::Type::kDestination); + alpha = static_cast(PronunciationAlphabet::kIpa); + std::string ipa_destination = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string ipa_destination_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kKatakana); + std::string katakana_destination = + osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string katakana_destination_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kJeita); + std::string jeita_destination = osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string jeita_destination_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + alpha = static_cast(PronunciationAlphabet::kNtSampa); + std::string nt_sampa_destination = + osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)); + std::string nt_sampa_destination_lang = osmdata_.name_offset_map.name(get_lang_index(t, alpha)); + + ProcessPronunciationName(OSMLinguistic::Type::kDestination, PronunciationAlphabet::kIpa, + destination_ipa_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestination, PronunciationAlphabet::kKatakana, + destination_katakana_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestination, PronunciationAlphabet::kJeita, + destination_jeita_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestination, PronunciationAlphabet::kNtSampa, + destination_nt_sampa_); + + if (destination_forward_ipa_.empty()) + ProcessPronunciationName(OSMLinguistic::Type::kDestinationForward, PronunciationAlphabet::kIpa, + destination_forward_ipa_); + else + ProcessPronunciationLRFBName(ipa_destination, ipa_destination_lang, destination_forward_ipa_, + OSMLinguistic::Type::kDestinationForward, + PronunciationAlphabet::kIpa); + + if (destination_forward_katakana_.empty()) + ProcessPronunciationName(OSMLinguistic::Type::kDestinationForward, + PronunciationAlphabet::kKatakana, destination_forward_katakana_); + else + ProcessPronunciationLRFBName(katakana_destination, katakana_lang, destination_forward_katakana_, + OSMLinguistic::Type::kDestinationForward, + PronunciationAlphabet::kKatakana); + + if (destination_forward_jeita_.empty()) + ProcessPronunciationName(OSMLinguistic::Type::kDestinationForward, + PronunciationAlphabet::kJeita, destination_forward_jeita_); + else + ProcessPronunciationLRFBName(jeita_destination, jeita_destination_lang, + destination_forward_jeita_, + OSMLinguistic::Type::kDestinationForward, + PronunciationAlphabet::kJeita); + + if (destination_forward_nt_sampa_.empty()) + ProcessPronunciationName(OSMLinguistic::Type::kDestinationForward, + PronunciationAlphabet::kNtSampa, destination_forward_nt_sampa_); + else + ProcessPronunciationLRFBName(nt_sampa_destination, nt_sampa_destination_lang, + destination_forward_nt_sampa_, + OSMLinguistic::Type::kDestinationForward, + PronunciationAlphabet::kNtSampa); + + if (destination_backward_ipa_.empty()) + ProcessPronunciationName(OSMLinguistic::Type::kDestinationBackward, PronunciationAlphabet::kIpa, + destination_backward_ipa_); + else + ProcessPronunciationLRFBName(ipa_destination, ipa_destination_lang, destination_backward_ipa_, + OSMLinguistic::Type::kDestinationBackward, + PronunciationAlphabet::kIpa); + + if (destination_backward_katakana_.empty()) + ProcessPronunciationName(OSMLinguistic::Type::kDestinationBackward, + PronunciationAlphabet::kKatakana, destination_backward_katakana_); + else + ProcessPronunciationLRFBName(katakana_destination, katakana_destination_lang, + destination_backward_katakana_, + OSMLinguistic::Type::kDestinationBackward, + PronunciationAlphabet::kKatakana); + + if (destination_backward_jeita_.empty()) + ProcessPronunciationName(OSMLinguistic::Type::kDestinationBackward, + PronunciationAlphabet::kJeita, destination_backward_jeita_); + else + ProcessPronunciationLRFBName(jeita_destination, jeita_destination_lang, + destination_backward_jeita_, + OSMLinguistic::Type::kDestinationBackward, + PronunciationAlphabet::kJeita); + + if (destination_backward_nt_sampa_.empty()) + ProcessPronunciationName(OSMLinguistic::Type::kDestinationBackward, + PronunciationAlphabet::kNtSampa, destination_backward_nt_sampa_); + else + ProcessPronunciationLRFBName(nt_sampa_destination, nt_sampa_destination_lang, + destination_backward_nt_sampa_, + OSMLinguistic::Type::kDestinationBackward, + PronunciationAlphabet::kNtSampa); + + ProcessPronunciationName(OSMLinguistic::Type::kDestinationRef, PronunciationAlphabet::kIpa, + destination_ref_ipa_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestinationRef, PronunciationAlphabet::kKatakana, + destination_ref_katakana_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestinationRef, PronunciationAlphabet::kJeita, + destination_ref_jeita_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestinationRef, PronunciationAlphabet::kNtSampa, + destination_ref_nt_sampa_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestinationRefTo, PronunciationAlphabet::kIpa, + destination_ref_to_ipa_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestinationRefTo, PronunciationAlphabet::kKatakana, + destination_ref_to_katakana_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestinationRefTo, PronunciationAlphabet::kJeita, + destination_ref_to_jeita_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestinationRefTo, PronunciationAlphabet::kNtSampa, + destination_ref_to_nt_sampa_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestinationStreet, PronunciationAlphabet::kIpa, + destination_street_ipa_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestinationStreet, + PronunciationAlphabet::kKatakana, destination_street_katakana_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestinationStreet, PronunciationAlphabet::kJeita, + destination_street_jeita_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestinationStreet, PronunciationAlphabet::kNtSampa, + destination_street_nt_sampa_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestinationStreetTo, PronunciationAlphabet::kIpa, + destination_street_to_ipa_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestinationStreetTo, + PronunciationAlphabet::kKatakana, destination_street_to_katakana_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestinationStreetTo, PronunciationAlphabet::kJeita, + destination_street_to_jeita_); + + ProcessPronunciationName(OSMLinguistic::Type::kDestinationStreetTo, + PronunciationAlphabet::kNtSampa, destination_street_to_nt_sampa_); + + ProcessPronunciationName(OSMLinguistic::Type::kJunctionRef, PronunciationAlphabet::kIpa, + junction_ref_ipa_); + + ProcessPronunciationName(OSMLinguistic::Type::kJunctionRef, PronunciationAlphabet::kKatakana, + junction_ref_katakana_); + + ProcessPronunciationName(OSMLinguistic::Type::kJunctionRef, PronunciationAlphabet::kJeita, + junction_ref_jeita_); + + ProcessPronunciationName(OSMLinguistic::Type::kJunctionRef, PronunciationAlphabet::kNtSampa, + junction_ref_nt_sampa_); + + ProcessPronunciationName(OSMLinguistic::Type::kJunctionName, PronunciationAlphabet::kIpa, + junction_name_ipa_); + + ProcessPronunciationName(OSMLinguistic::Type::kJunctionName, PronunciationAlphabet::kKatakana, + junction_name_katakana_); + + ProcessPronunciationName(OSMLinguistic::Type::kJunctionName, PronunciationAlphabet::kJeita, + junction_name_jeita_); + + ProcessPronunciationName(OSMLinguistic::Type::kJunctionName, PronunciationAlphabet::kNtSampa, + junction_name_nt_sampa_); // Infer cul-de-sac if a road edge is a loop and is low classification. if (!way_.roundabout() && loop_nodes_.size() != nodes.size() && way_.use() == Use::kRoad && @@ -2196,7 +3668,24 @@ struct graph_callback : public OSMPBF::Callback { if (has_pronunciation_tags_) { way_.set_has_pronunciation_tags(true); - pronunciation_->push_back(osm_pronunciation_); + for (auto const& p : pronunciationMap) { + if (p.second != 0) { + OSMLinguistic ling; + ling.key_.type_ = p.first.first; + ling.key_.alpha_ = p.first.second; + ling.name_offset_ = p.second; + osmdata_.pronunciations.insert(LinguisticMultiMap::value_type(osmid_, ling)); + } + } + for (auto const& l : langMap) { + if (l.second != 0) { + OSMLinguistic ling; + ling.key_.type_ = l.first.first; + ling.key_.alpha_ = l.first.second; + ling.name_offset_ = l.second; + osmdata_.langs.insert(LinguisticMultiMap::value_type(osmid_, ling)); + } + } } // Add the way to the list @@ -2655,18 +4144,453 @@ struct graph_callback : public OSMPBF::Callback { void reset(sequence* ways, sequence* way_nodes, sequence* access, - sequence* pronunciation, sequence* complex_restrictions_from, sequence* complex_restrictions_to, - sequence* bss_nodes) { + sequence* bss_nodes, + sequence* node_linguistics) { // reset the pointers (either null them out or set them to something valid) ways_.reset(ways); way_nodes_.reset(way_nodes); access_.reset(access); - pronunciation_.reset(pronunciation); complex_restrictions_from_.reset(complex_restrictions_from); complex_restrictions_to_.reset(complex_restrictions_to); bss_nodes_.reset(bss_nodes); + node_linguistics_.reset(node_linguistics); + + if (node_linguistics != nullptr) { + // push empty struct at index 0 + OSMNodeLinguistic ling; + node_linguistics_->push_back(ling); + ++osmdata_.node_linguistic_count; + } + } + + void ProcessPronunciationTag(const OSMLinguistic::Type& type, + const PronunciationAlphabet& alphabet, + OSMNodeLinguistic* linguistics = nullptr) { + + uint32_t name = 0, lang = 0; + uint8_t t = static_cast(type); + uint8_t alpha = static_cast(alphabet); + + if (linguistics) { + if (type == OSMLinguistic::Type::kNodeName) { + switch (alphabet) { + case PronunciationAlphabet::kIpa: + name = linguistics->name_pronunciation_ipa_index(); + lang = linguistics->name_pronunciation_ipa_lang_index(); + break; + case PronunciationAlphabet::kKatakana: + name = linguistics->name_pronunciation_katakana_index(); + lang = linguistics->name_pronunciation_katakana_lang_index(); + break; + case PronunciationAlphabet::kJeita: + name = linguistics->name_pronunciation_jeita_index(); + lang = linguistics->name_pronunciation_jeita_lang_index(); + break; + case PronunciationAlphabet::kNtSampa: + name = linguistics->name_pronunciation_nt_sampa_index(); + lang = linguistics->name_pronunciation_nt_sampa_lang_index(); + break; + case PronunciationAlphabet::kNone: + break; + } + } else if (type == OSMLinguistic::Type::kNodeRef) { + switch (alphabet) { + case PronunciationAlphabet::kIpa: + name = linguistics->ref_pronunciation_ipa_index(); + lang = linguistics->ref_pronunciation_ipa_lang_index(); + break; + case PronunciationAlphabet::kKatakana: + name = linguistics->ref_pronunciation_katakana_index(); + lang = linguistics->ref_pronunciation_katakana_lang_index(); + break; + case PronunciationAlphabet::kJeita: + name = linguistics->ref_pronunciation_jeita_index(); + lang = linguistics->ref_pronunciation_jeita_lang_index(); + break; + case PronunciationAlphabet::kNtSampa: + name = linguistics->ref_pronunciation_nt_sampa_index(); + lang = linguistics->ref_pronunciation_nt_sampa_lang_index(); + break; + case PronunciationAlphabet::kNone: + break; + } + } + } else { + name = get_pronunciation_index(t, alpha); + lang = get_lang_index(t, alpha); + } + + std::string name_w_lang, language; + + if (name != 0) + name_w_lang = + !linguistics ? osmdata_.name_offset_map.name(name) : osmdata_.node_names.name(name); + + if (lang != 0) + language = !linguistics ? osmdata_.name_offset_map.name(lang) : osmdata_.node_names.name(lang); + + ProcessNameTag(tag_, name_w_lang, language, true); + + SavePronunciationData(t, alpha, name_w_lang, language, linguistics); + } + + void ProcessLeftRightPronunciationTag(const OSMLinguistic::Type& type, + const PronunciationAlphabet& alphabet) { + uint32_t name = 0, lang = 0; + std::string name_w_lang, language; + uint8_t t = static_cast(type); + uint8_t alpha = static_cast(alphabet); + + name = get_pronunciation_index(t, alpha); + lang = get_lang_index(t, alpha); + + if (name != 0) + name_w_lang = osmdata_.name_offset_map.name(name); + + if (lang != 0) + language = osmdata_.name_offset_map.name(lang); + + ProcessLeftRightNameTag(tag_, name_w_lang, language, true); + + SavePronunciationData(t, alpha, name_w_lang, language); + } + + void SavePronunciationData(const uint8_t type, + const uint8_t alphabet, + const std::string& pronunciation, + const std::string& language, + OSMNodeLinguistic* linguistics = nullptr) { + if (!pronunciation.empty()) { + has_pronunciation_tags_ = true; + + if (linguistics) { + OSMLinguistic::Type t = static_cast(type); + PronunciationAlphabet alpha = static_cast(alphabet); + if (t == OSMLinguistic::Type::kNodeName) { + switch (alpha) { + case PronunciationAlphabet::kIpa: + linguistics->set_name_pronunciation_ipa_index(osmdata_.node_names.index(pronunciation)); + linguistics->set_name_pronunciation_ipa_lang_index(osmdata_.node_names.index(language)); + break; + case PronunciationAlphabet::kKatakana: + linguistics->set_name_pronunciation_katakana_index( + osmdata_.node_names.index(pronunciation)); + linguistics->set_name_pronunciation_katakana_lang_index( + osmdata_.node_names.index(language)); + break; + case PronunciationAlphabet::kJeita: + linguistics->set_name_pronunciation_jeita_index( + osmdata_.node_names.index(pronunciation)); + linguistics->set_name_pronunciation_jeita_lang_index( + osmdata_.node_names.index(language)); + break; + case PronunciationAlphabet::kNtSampa: + linguistics->set_name_pronunciation_nt_sampa_index( + osmdata_.node_names.index(pronunciation)); + linguistics->set_name_pronunciation_nt_sampa_lang_index( + osmdata_.node_names.index(language)); + break; + case PronunciationAlphabet::kNone: + break; + } + } else if (t == OSMLinguistic::Type::kNodeRef) { + switch (alpha) { + case PronunciationAlphabet::kIpa: + linguistics->set_ref_pronunciation_ipa_index(osmdata_.node_names.index(pronunciation)); + linguistics->set_ref_pronunciation_ipa_lang_index(osmdata_.node_names.index(language)); + break; + case PronunciationAlphabet::kKatakana: + linguistics->set_ref_pronunciation_katakana_index( + osmdata_.node_names.index(pronunciation)); + linguistics->set_ref_pronunciation_katakana_lang_index( + osmdata_.node_names.index(language)); + break; + case PronunciationAlphabet::kJeita: + linguistics->set_ref_pronunciation_jeita_index( + osmdata_.node_names.index(pronunciation)); + linguistics->set_ref_pronunciation_jeita_lang_index( + osmdata_.node_names.index(language)); + break; + case PronunciationAlphabet::kNtSampa: + linguistics->set_ref_pronunciation_nt_sampa_index( + osmdata_.node_names.index(pronunciation)); + linguistics->set_ref_pronunciation_nt_sampa_lang_index( + osmdata_.node_names.index(language)); + break; + case PronunciationAlphabet::kNone: + break; + } + } + } else { + + pronunciationMap[std::make_pair(type, alphabet)] = + osmdata_.name_offset_map.index(pronunciation); + langMap[std::make_pair(type, alphabet)] = osmdata_.name_offset_map.index(language); + } + } + } + + void ProcessNameTag(const std::pair& tag, + std::string& name_w_lang, + std::string& language, + bool is_lang_pronunciation = false) { + // If we start to use admin tags for HERE, then put use_admin_db_ check back + // if (!use_admin_db_) + // return; + + std::string t = tag.first; + std::size_t found = t.find(":pronunciation"); + if (found != std::string::npos && !is_lang_pronunciation) + return; + else { + if (found != std::string::npos) { + // remove :pronunciation or :pronunciation: + t = tag.first.substr(0, found); + has_pronunciation_tags_ = true; + } + } + + if (boost::algorithm::starts_with(t, "tunnel:name:")) + t = t.substr(7); + else if (boost::algorithm::starts_with(t, "junction:name:") || + boost::algorithm::starts_with(t, "junction:ref:")) + t = t.substr(9); + else { + found = t.find(":lang:"); + if (found != std::string::npos) { + t = t.substr(found + 1); + } + } + + // if we have column at the and of token delete it + // TODO It looks like road bug. We shouldn't have any column ant the end + while (t.back() == ':') { + t.pop_back(); + } + + std::vector tokens = GetTagTokens(t, ':'); + if (tokens.size() == 2) { + + std::string lang = tokens.at(1); + if (stringLanguage(lang) != Language::kNone && + !tag.second.empty()) // name:en, name:ar, name:fr, etc + { + t = tag.second; + uint32_t count = std::count(t.begin(), t.end(), ';'); + std::string l = lang; + for (uint32_t i = 0; i < count; i++) { + l += ";" + lang; + } + if (name_w_lang.empty()) { + name_w_lang = tag.second; + language = l; + } else { + name_w_lang += ";" + tag.second; + language += ";" + l; + } + } + } + } + + void ProcessLeftRightNameTag(const std::pair& tag, + std::string& name_left_right_w_lang, + std::string& lang_left_right, + bool is_lang_pronunciation = false) { + if (!use_admin_db_) + return; + + std::string t = tag.first; + std::size_t found = t.find(":pronunciation"); + if (found != std::string::npos && !is_lang_pronunciation) + return; + else { + if (found != std::string::npos) { + // remove :pronunciation or :pronunciation: + t = tag.first.substr(0, found); + has_pronunciation_tags_ = true; + } + + if (boost::algorithm::starts_with(t, "tunnel:name:")) + t = t.substr(7); + } + + std::vector tokens; + std::string lang; + + found = t.find(":lang:"); + // destination:forward:lang:nl + if (found != std::string::npos) { + tokens = GetTagTokens(t, ':'); + if (tokens.size() == 4) + lang = tokens.at(3); + } else { + tokens = GetTagTokens(t, ':'); + if (tokens.size() == 3) + lang = tokens.at(2); + } + + found = t.find(":to:"); + if (found != std::string::npos && tokens.size() == 5) { + lang = tokens.at(4); + } + + if (!lang.empty()) { + + if (stringLanguage(lang) != Language::kNone && !tag_.second.empty()) // name:left:en + { + if (name_left_right_w_lang.empty()) { + name_left_right_w_lang = tag_.second; + lang_left_right = lang; + } else { + name_left_right_w_lang += ";" + tag_.second; + lang_left_right += ";" + lang; + } + } + } + } + + void ProcessPronunciationName(const OSMLinguistic::Type& type, + const PronunciationAlphabet& alphabet, + std::string& name_w_pronunciation, + OSMNodeLinguistic* linguistics = nullptr) { + uint32_t name_index = 0, lang_index = 0; + std::string language, name_pronunciation_w_lang; + uint8_t t = static_cast(type); + uint8_t alpha = static_cast(alphabet); + + if (linguistics) { + if (type == OSMLinguistic::Type::kNodeName) { + switch (alphabet) { + case PronunciationAlphabet::kIpa: + name_index = linguistics->name_pronunciation_ipa_index(); + lang_index = linguistics->name_pronunciation_ipa_lang_index(); + break; + case PronunciationAlphabet::kKatakana: + name_index = linguistics->name_pronunciation_katakana_index(); + lang_index = linguistics->name_pronunciation_katakana_lang_index(); + break; + case PronunciationAlphabet::kJeita: + name_index = linguistics->name_pronunciation_jeita_index(); + lang_index = linguistics->name_pronunciation_jeita_lang_index(); + break; + case PronunciationAlphabet::kNtSampa: + name_index = linguistics->name_pronunciation_nt_sampa_index(); + lang_index = linguistics->name_pronunciation_nt_sampa_lang_index(); + break; + case PronunciationAlphabet::kNone: + break; + } + } else if (type == OSMLinguistic::Type::kNodeRef) { + switch (alphabet) { + case PronunciationAlphabet::kIpa: + name_index = linguistics->ref_pronunciation_ipa_index(); + lang_index = linguistics->ref_pronunciation_ipa_lang_index(); + break; + case PronunciationAlphabet::kKatakana: + name_index = linguistics->ref_pronunciation_katakana_index(); + lang_index = linguistics->ref_pronunciation_katakana_lang_index(); + break; + case PronunciationAlphabet::kJeita: + name_index = linguistics->ref_pronunciation_jeita_index(); + lang_index = linguistics->ref_pronunciation_jeita_lang_index(); + break; + case PronunciationAlphabet::kNtSampa: + name_index = linguistics->ref_pronunciation_nt_sampa_index(); + lang_index = linguistics->ref_pronunciation_nt_sampa_lang_index(); + break; + case PronunciationAlphabet::kNone: + break; + } + } + } else { + name_index = get_pronunciation_index(t, alpha); + lang_index = get_lang_index(t, alpha); + } + + if (name_index != 0) + name_pronunciation_w_lang = !linguistics ? osmdata_.name_offset_map.name(name_index) + : osmdata_.node_names.name(name_index); + + if (lang_index != 0) + language = !linguistics ? osmdata_.name_offset_map.name(lang_index) + : osmdata_.node_names.name(lang_index); + + ProcessName(name_pronunciation_w_lang, name_w_pronunciation, language); + + SavePronunciationData(t, alpha, name_w_pronunciation, language, linguistics); + } + + void ProcessPronunciationLRFBName(const std::string& pronunciation_name, + const std::string& pronunciation_lang, + const std::string& name_lr_fb, + const OSMLinguistic::Type& type, + const PronunciationAlphabet& alphabet) { + + uint32_t name_lr_fb_w_lang_index = 0, lang_lr_fb_index = 0; + uint8_t t = static_cast(type); + uint8_t alpha = static_cast(alphabet); + + name_lr_fb_w_lang_index = get_pronunciation_index(t, alpha); + lang_lr_fb_index = get_lang_index(t, alpha); + + const std::string name_lr_fb_w_lang = osmdata_.name_offset_map.name(name_lr_fb_w_lang_index); + std::string name = name_lr_fb; + std::string lang = osmdata_.name_offset_map.name(lang_lr_fb_index); + + ProcessLRFBName(name_lr_fb_w_lang, pronunciation_name, pronunciation_lang, name, lang); + SavePronunciationData(t, alpha, name, lang); + } + + void ProcessName(const std::string& name_w_lang, std::string& name, std::string& language) { + if (!use_admin_db_) + return; + + if (!name.empty()) { + if (name_w_lang.empty()) + return; + else { + uint32_t count = std::count(name.begin(), name.end(), ';'); + language.insert(0, count + 1, ';'); + name += ";" + name_w_lang; + } + } else + name = name_w_lang; + } + + void ProcessLRFBName(const std::string& name_lr_fb_w_lang, + const std::string& name_w_lang, + const std::string& language, + std::string& name_lr_fb, + std::string& lang_lr_fb) { + if (!use_admin_db_) + return; + + if (!name_lr_fb.empty()) { + + if (name_lr_fb_w_lang.empty()) + + if (!name_w_lang.empty()) { // other side of street name may not change + name_lr_fb += ";" + name_w_lang; + lang_lr_fb += ";" + language; + } else + return; + else { + std::string lang = lang_lr_fb; + uint32_t count = std::count(name_lr_fb.begin(), name_lr_fb.end(), ';'); + for (uint32_t i = 0; i <= count; i++) { + lang_lr_fb = ";" + lang; + } + + if (!name_w_lang.empty()) { // other side of street name may not change + name_lr_fb += ";" + name_lr_fb_w_lang + ";" + name_w_lang; + lang_lr_fb += ";" + language; + } else { + name_lr_fb += ";" + name_lr_fb_w_lang; + } + } + } } void ProcessDirection(bool int_ref) { @@ -2682,9 +4606,9 @@ struct graph_callback : public OSMPBF::Callback { if (direction.empty()) { if (int_ref) - way_.set_int_ref_index(osmdata_.name_offset_map.index(ref)); + int_ref_ = ref; else - way_.set_ref_index(osmdata_.name_offset_map.index(ref)); + ref_ = ref; } else { std::vector refs = GetTagTokens(ref); std::vector directions = GetTagTokens(direction); @@ -2701,14 +4625,14 @@ struct graph_callback : public OSMPBF::Callback { tmp_ref += refs.at(i); } if (int_ref) - way_.set_int_ref_index(osmdata_.name_offset_map.index(tmp_ref)); + int_ref_ = tmp_ref; else - way_.set_ref_index(osmdata_.name_offset_map.index(tmp_ref)); + ref_ = tmp_ref; } else { if (int_ref) - way_.set_int_ref_index(osmdata_.name_offset_map.index(ref)); + int_ref_ = ref; else - way_.set_ref_index(osmdata_.name_offset_map.index(ref)); + ref_ = ref; } } } @@ -2719,19 +4643,19 @@ struct graph_callback : public OSMPBF::Callback { if (int_ref) { switch (type) { case PronunciationAlphabet::kIpa: - ref_pronunciation = int_ref_pronunciation_; + ref_pronunciation = int_ref_ipa_; direction_pronunciation = int_direction_pronunciation_; break; - case PronunciationAlphabet::kXKatakana: - ref_pronunciation = int_ref_pronunciation_katakana_; + case PronunciationAlphabet::kKatakana: + ref_pronunciation = int_ref_katakana_; direction_pronunciation = int_direction_pronunciation_katakana_; break; case PronunciationAlphabet::kNtSampa: - ref_pronunciation = int_ref_pronunciation_nt_sampa_; + ref_pronunciation = int_ref_nt_sampa_; direction_pronunciation = int_direction_pronunciation_nt_sampa_; break; - case PronunciationAlphabet::kXJeita: - ref_pronunciation = int_ref_pronunciation_jeita_; + case PronunciationAlphabet::kJeita: + ref_pronunciation = int_ref_jeita_; direction_pronunciation = int_direction_pronunciation_jeita_; break; case PronunciationAlphabet::kNone: @@ -2740,19 +4664,19 @@ struct graph_callback : public OSMPBF::Callback { } else { switch (type) { case PronunciationAlphabet::kIpa: - ref_pronunciation = ref_pronunciation_; + ref_pronunciation = ref_ipa_; direction_pronunciation = direction_pronunciation_; break; - case PronunciationAlphabet::kXKatakana: - ref_pronunciation = ref_pronunciation_katakana_; + case PronunciationAlphabet::kKatakana: + ref_pronunciation = ref_katakana_; direction_pronunciation = direction_pronunciation_katakana_; break; case PronunciationAlphabet::kNtSampa: - ref_pronunciation = ref_pronunciation_nt_sampa_; + ref_pronunciation = ref_nt_sampa_; direction_pronunciation = direction_pronunciation_nt_sampa_; break; - case PronunciationAlphabet::kXJeita: - ref_pronunciation = ref_pronunciation_jeita_; + case PronunciationAlphabet::kJeita: + ref_pronunciation = ref_jeita_; direction_pronunciation = direction_pronunciation_jeita_; break; case PronunciationAlphabet::kNone: @@ -2784,91 +4708,35 @@ struct graph_callback : public OSMPBF::Callback { } void UpdateRefPronunciation(const std::string& ref_pronunciation, - const PronunciationAlphabet type, + const PronunciationAlphabet alphabet, bool int_ref) { + uint8_t alpha = static_cast(alphabet); if (int_ref) { - switch (type) { - case PronunciationAlphabet::kIpa: - osm_pronunciation_.set_int_ref_pronunciation_ipa_index( - osmdata_.name_offset_map.index(ref_pronunciation)); - break; - case PronunciationAlphabet::kXKatakana: - osm_pronunciation_.set_int_ref_pronunciation_katakana_index( - osmdata_.name_offset_map.index(ref_pronunciation)); - break; - case PronunciationAlphabet::kNtSampa: - osm_pronunciation_.set_int_ref_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(ref_pronunciation)); - break; - case PronunciationAlphabet::kXJeita: - osm_pronunciation_.set_int_ref_pronunciation_jeita_index( - osmdata_.name_offset_map.index(ref_pronunciation)); - break; - case PronunciationAlphabet::kNone: - break; - } + uint8_t t = static_cast(OSMLinguistic::Type::kIntRef); + pronunciationMap[std::make_pair(t, alpha)] = osmdata_.name_offset_map.index(ref_pronunciation); + } else { - switch (type) { - case PronunciationAlphabet::kIpa: - osm_pronunciation_.set_ref_pronunciation_ipa_index( - osmdata_.name_offset_map.index(ref_pronunciation)); - break; - case PronunciationAlphabet::kXKatakana: - osm_pronunciation_.set_ref_pronunciation_katakana_index( - osmdata_.name_offset_map.index(ref_pronunciation)); - break; - case PronunciationAlphabet::kNtSampa: - osm_pronunciation_.set_ref_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(ref_pronunciation)); - break; - case PronunciationAlphabet::kXJeita: - osm_pronunciation_.set_ref_pronunciation_jeita_index( - osmdata_.name_offset_map.index(ref_pronunciation)); - break; - case PronunciationAlphabet::kNone: - break; - } + uint8_t t = static_cast(OSMLinguistic::Type::kRef); + pronunciationMap[std::make_pair(t, alpha)] = osmdata_.name_offset_map.index(ref_pronunciation); } } void MergeRefPronunciations() { - for (uint8_t type = static_cast(PronunciationAlphabet::kIpa); - type != static_cast(PronunciationAlphabet::kNtSampa) + 1; ++type) { + for (uint8_t alphabet = static_cast(PronunciationAlphabet::kIpa); + alphabet != static_cast(PronunciationAlphabet::kNone); ++alphabet) { // add int_ref pronunciations to the end of the pronunciation refs for now. makes sure that we // don't add dups. uint32_t index = 0; std::string tmp; - switch (static_cast(type)) { - case PronunciationAlphabet::kIpa: - index = osm_pronunciation_.int_ref_pronunciation_ipa_index(); - tmp = - (index ? osmdata_.name_offset_map.name(osm_pronunciation_.ref_pronunciation_ipa_index()) - : ""); - break; - case PronunciationAlphabet::kXKatakana: - index = osm_pronunciation_.int_ref_pronunciation_katakana_index(); - tmp = (index ? osmdata_.name_offset_map.name( - osm_pronunciation_.ref_pronunciation_katakana_index()) - : ""); - break; - case PronunciationAlphabet::kNtSampa: - index = osm_pronunciation_.int_ref_pronunciation_nt_sampa_index(); - tmp = (index ? osmdata_.name_offset_map.name( - osm_pronunciation_.ref_pronunciation_nt_sampa_index()) - : ""); - break; - case PronunciationAlphabet::kXJeita: - index = osm_pronunciation_.int_ref_pronunciation_jeita_index(); - tmp = - (index - ? osmdata_.name_offset_map.name(osm_pronunciation_.ref_pronunciation_jeita_index()) - : ""); - break; - case PronunciationAlphabet::kNone: - break; - } + + uint8_t t = static_cast(OSMLinguistic::Type::kIntRef); + uint8_t alpha = static_cast(alphabet); + + index = get_pronunciation_index(t, alpha); + t = static_cast(OSMLinguistic::Type::kRef); + tmp = (index ? osmdata_.name_offset_map.name(get_pronunciation_index(t, alpha)) : ""); if (index) { std::vector rs = GetTagTokens(tmp); @@ -2891,41 +4759,13 @@ struct graph_callback : public OSMPBF::Callback { bFound = false; } - switch (static_cast(type)) { - case PronunciationAlphabet::kIpa: - if (!tmp.empty()) { - osm_pronunciation_.set_ref_pronunciation_ipa_index(osmdata_.name_offset_map.index(tmp)); - } - // no matter what, clear out the int_ref. - osm_pronunciation_.set_int_ref_pronunciation_ipa_index(0); - break; - case PronunciationAlphabet::kXKatakana: - if (!tmp.empty()) { - osm_pronunciation_.set_ref_pronunciation_katakana_index( - osmdata_.name_offset_map.index(tmp)); - } - // no matter what, clear out the int_ref. - osm_pronunciation_.set_int_ref_pronunciation_katakana_index(0); - break; - case PronunciationAlphabet::kNtSampa: - if (!tmp.empty()) { - osm_pronunciation_.set_ref_pronunciation_nt_sampa_index( - osmdata_.name_offset_map.index(tmp)); - } - // no matter what, clear out the int_ref. - osm_pronunciation_.set_int_ref_pronunciation_nt_sampa_index(0); - break; - case PronunciationAlphabet::kXJeita: - if (!tmp.empty()) { - osm_pronunciation_.set_ref_pronunciation_jeita_index( - osmdata_.name_offset_map.index(tmp)); - } - // no matter what, clear out the int_ref. - osm_pronunciation_.set_int_ref_pronunciation_jeita_index(0); - break; - case PronunciationAlphabet::kNone: - break; + if (!tmp.empty()) { + + pronunciationMap[std::make_pair(t, alpha)] = osmdata_.name_offset_map.index(tmp); } + // no matter what, clear out the int_ref. + t = static_cast(OSMLinguistic::Type::kIntRef); + pronunciationMap[std::make_pair(t, alpha)] = 0; } } } @@ -2937,6 +4777,12 @@ struct graph_callback : public OSMPBF::Callback { OSMWay way_; std::pair tag_; uint64_t osmid_; + + const uint8_t ipa = static_cast(PronunciationAlphabet::kIpa); + const uint8_t nt_sampa = static_cast(PronunciationAlphabet::kNtSampa); + const uint8_t katakana = static_cast(PronunciationAlphabet::kKatakana); + const uint8_t jeita = static_cast(PronunciationAlphabet::kJeita); + float default_speed_ = 0.0f, max_speed_ = 0.0f; float average_speed_ = 0.0f, advisory_speed_ = 0.0f; bool has_default_speed_ = false, has_max_speed_ = false; @@ -2944,17 +4790,100 @@ struct graph_callback : public OSMPBF::Callback { bool has_surface_ = true; bool has_surface_tag_ = true; OSMAccess osm_access_; - OSMPronunciation osm_pronunciation_; + std::map, uint32_t> pronunciationMap; + std::map, uint32_t> langMap; bool has_user_tags_ = false, has_pronunciation_tags_ = false; - std::string ref_, int_ref_, direction_, int_direction_; - std::string ref_pronunciation_, int_ref_pronunciation_, ref_pronunciation_nt_sampa_, - int_ref_pronunciation_nt_sampa_, ref_pronunciation_katakana_, int_ref_pronunciation_katakana_, - ref_pronunciation_jeita_, int_ref_pronunciation_jeita_; + + std::string ref_, ref_language_, ref_w_lang_, ref_left_, ref_right_, ref_lang_left_, + ref_lang_right_, ref_left_w_lang_, ref_right_w_lang_, payment_, payment_forward_, + payment_backward_; + + std::string int_ref_, int_ref_language_, int_ref_w_lang_, int_ref_left_, int_ref_right_, + int_ref_lang_left_, int_ref_lang_right_, int_ref_left_w_lang_, int_ref_right_w_lang_; + + std::string direction_, int_direction_; + std::string direction_pronunciation_, int_direction_pronunciation_, direction_pronunciation_nt_sampa_, int_direction_pronunciation_nt_sampa_, direction_pronunciation_katakana_, int_direction_pronunciation_katakana_, direction_pronunciation_jeita_, int_direction_pronunciation_jeita_; - std::string name_, service_, amenity_; + std::string name_, language_, name_w_lang_, service_, amenity_; + + std::string ref_ipa_, ref_left_ipa_, ref_right_ipa_; + std::string ref_katakana_, ref_left_katakana_, ref_right_katakana_; + std::string ref_jeita_, ref_left_jeita_, ref_right_jeita_; + std::string ref_nt_sampa_, ref_left_nt_sampa_, ref_right_nt_sampa_; + + std::string int_ref_ipa_, int_ref_left_ipa_, int_ref_right_ipa_; + std::string int_ref_katakana_, int_ref_left_katakana_, int_ref_right_katakana_; + std::string int_ref_jeita_, int_ref_left_jeita_, int_ref_right_jeita_; + std::string int_ref_nt_sampa_, int_ref_left_nt_sampa_, int_ref_right_nt_sampa_; + + std::string name_ipa_, name_left_ipa_, name_right_ipa_, name_forward_ipa_, name_backward_ipa_; + std::string name_katakana_, name_left_katakana_, name_right_katakana_, name_forward_katakana_, + name_backward_katakana_; + std::string name_jeita_, name_left_jeita_, name_right_jeita_, name_forward_jeita_, + name_backward_jeita_; + std::string name_nt_sampa_, name_left_nt_sampa_, name_right_nt_sampa_, name_forward_nt_sampa_, + name_backward_nt_sampa_; + + std::string alt_name_ipa_, alt_name_left_ipa_, alt_name_right_ipa_; + std::string alt_name_katakana_, alt_name_left_katakana_, alt_name_right_katakana_; + std::string alt_name_jeita_, alt_name_left_jeita_, alt_name_right_jeita_; + std::string alt_name_nt_sampa_, alt_name_left_nt_sampa_, alt_name_right_nt_sampa_; + + std::string official_name_ipa_, official_name_left_ipa_, official_name_right_ipa_; + std::string official_name_katakana_, official_name_left_katakana_, official_name_right_katakana_; + std::string official_name_jeita_, official_name_left_jeita_, official_name_right_jeita_; + std::string official_name_nt_sampa_, official_name_left_nt_sampa_, official_name_right_nt_sampa_; + + std::string tunnel_name_ipa_, tunnel_name_left_ipa_, tunnel_name_right_ipa_; + std::string tunnel_name_katakana_, tunnel_name_left_katakana_, tunnel_name_right_katakana_; + std::string tunnel_name_jeita_, tunnel_name_left_jeita_, tunnel_name_right_jeita_; + std::string tunnel_name_nt_sampa_, tunnel_name_left_nt_sampa_, tunnel_name_right_nt_sampa_; + + std::string destination_ipa_, destination_forward_ipa_, destination_backward_ipa_; + std::string destination_katakana_, destination_forward_katakana_, destination_backward_katakana_; + std::string destination_jeita_, destination_forward_jeita_, destination_backward_jeita_; + std::string destination_nt_sampa_, destination_forward_nt_sampa_, destination_backward_nt_sampa_; + + std::string destination_ref_ipa_, destination_ref_to_ipa_, destination_street_ipa_, + destination_street_to_ipa_, junction_ref_ipa_, junction_name_ipa_; + std::string destination_ref_nt_sampa_, destination_ref_to_nt_sampa_, destination_street_nt_sampa_, + destination_street_to_nt_sampa_, junction_ref_nt_sampa_, junction_name_nt_sampa_; + std::string destination_ref_katakana_, destination_ref_to_katakana_, destination_street_katakana_, + destination_street_to_katakana_, junction_ref_katakana_, junction_name_katakana_; + std::string destination_ref_jeita_, destination_ref_to_jeita_, destination_street_jeita_, + destination_street_to_jeita_, junction_ref_jeita_, junction_name_jeita_; + + std::string name_left_, name_right_, lang_left_, lang_right_; + std::string name_left_w_lang_, name_right_w_lang_; + + std::string name_forward_, name_backward_, lang_forward_, lang_backward_; + std::string name_forward_w_lang_, name_backward_w_lang_; + + std::string official_name_, official_language_, official_name_w_lang_, official_name_left_, + official_name_right_, official_lang_left_, official_lang_right_, official_name_left_w_lang_, + official_name_right_w_lang_; + + std::string tunnel_name_, tunnel_language_, tunnel_name_w_lang_, tunnel_name_left_, + tunnel_name_right_, tunnel_lang_left_, tunnel_lang_right_, tunnel_name_left_w_lang_, + tunnel_name_right_w_lang_; + + std::string alt_name_, alt_language_, alt_name_w_lang_, alt_name_left_, alt_name_right_, + alt_lang_left_, alt_lang_right_, alt_name_left_w_lang_, alt_name_right_w_lang_; + + std::string destination_, destination_language_, destination_w_lang_, destination_forward_, + destination_forward_language_, destination_forward_w_lang_, destination_backward_, + destination_backward_language_, destination_backward_w_lang_; + + std::string destination_ref_, destination_ref_language_, destination_ref_w_lang_, + destination_ref_to_, destination_ref_to_language_, destination_ref_to_w_lang_, + destination_street_, destination_street_language_, destination_street_w_lang_, + destination_street_to_, destination_street_to_language_, destination_street_to_w_lang_; + + std::string junction_name_, junction_name_language_, junction_name_w_lang_, junction_ref_, + junction_ref_language_, junction_ref_w_lang_; // Configuration option to include highway=platform for pedestrians bool include_platforms_; @@ -3013,8 +4942,7 @@ struct graph_callback : public OSMPBF::Callback { // user entered access std::unique_ptr> access_; - // way pronunciations - std::unique_ptr> pronunciation_; + // from complex restrictions std::unique_ptr> complex_restrictions_from_; // used to find out if a wayid is the to edge for a complex restriction @@ -3023,6 +4951,9 @@ struct graph_callback : public OSMPBF::Callback { // bss nodes std::unique_ptr> bss_nodes_; + // node linguistics + std::unique_ptr> node_linguistics_; + // used to set "culdesac" labels to loop roads correctly culdesac_processor culdesac_processor_; @@ -3030,6 +4961,22 @@ struct graph_callback : public OSMPBF::Callback { Tags empty_node_results_; Tags empty_way_results_; Tags empty_relation_results_; + + uint32_t get_pronunciation_index(const uint8_t type, const uint8_t alpha) { + auto itr = pronunciationMap.find(std::make_pair(type, alpha)); + if (itr != pronunciationMap.end()) { + return itr->second; + } + return 0; + } + + uint32_t get_lang_index(const uint8_t type, const uint8_t alpha) { + auto itr = langMap.find(std::make_pair(type, alpha)); + if (itr != langMap.end()) { + return itr->second; + } + return 0; + } }; } // namespace @@ -3041,8 +4988,7 @@ OSMData PBFGraphParser::ParseWays(const boost::property_tree::ptree& pt, const std::vector& input_files, const std::string& ways_file, const std::string& way_nodes_file, - const std::string& access_file, - const std::string& pronunciation_file) { + const std::string& access_file) { // TODO: option 1: each one threads makes an osmdata and we splice them together at the end // option 2: synchronize around adding things to a single osmdata. will have to test to see // which is the least expensive (memory and speed). leaning towards option 2 @@ -3068,8 +5014,7 @@ OSMData PBFGraphParser::ParseWays(const boost::property_tree::ptree& pt, callback.reset(new sequence(ways_file, true), new sequence(way_nodes_file, true), - new sequence(access_file, true), - new sequence(pronunciation_file, true), nullptr, nullptr, nullptr); + new sequence(access_file, true), nullptr, nullptr, nullptr, nullptr); // Parse the ways and find all node Ids needed (those that are part of a // way's node list. Iterate through each pbf input file. LOG_INFO("Parsing ways..."); @@ -3096,14 +5041,6 @@ OSMData PBFGraphParser::ParseWays(const boost::property_tree::ptree& pt, access.sort([](const OSMAccess& a, const OSMAccess& b) { return a.way_id() < b.way_id(); }); } - // we need to sort the pronunciation indexes so that we can easily find them. - LOG_INFO("Sorting pronunciation indexes by way id..."); - { - sequence pronunciation(pronunciation_file, false); - pronunciation.sort( - [](const OSMPronunciation& a, const OSMPronunciation& b) { return a.way_id() < b.way_id(); }); - } - LOG_INFO("Finished"); // Return OSM data @@ -3142,9 +5079,9 @@ void PBFGraphParser::ParseRelations(const boost::property_tree::ptree& pt, } } - callback.reset(nullptr, nullptr, nullptr, nullptr, + callback.reset(nullptr, nullptr, nullptr, new sequence(complex_restriction_from_file, true), - new sequence(complex_restriction_to_file, true), nullptr); + new sequence(complex_restriction_to_file, true), nullptr, nullptr); // Parse relations. LOG_INFO("Parsing relations..."); @@ -3156,7 +5093,8 @@ void PBFGraphParser::ParseRelations(const boost::property_tree::ptree& pt, OSMPBF::Interest::CHANGESETS), callback); } - LOG_INFO("Finished with " + std::to_string(osmdata.restrictions.size()) + " simple restrictions"); + LOG_INFO("Finished with " + std::to_string(osmdata.restrictions.size()) + + " simple turn restrictions"); LOG_INFO("Finished with " + std::to_string(osmdata.lane_connectivity_map.size()) + " lane connections"); @@ -3184,6 +5122,7 @@ void PBFGraphParser::ParseNodes(const boost::property_tree::ptree& pt, const std::vector& input_files, const std::string& way_nodes_file, const std::string& bss_nodes_file, + const std::string& linguistic_node_file, OSMData& osmdata) { // TODO: option 1: each one threads makes an osmdata and we splice them together at the end // option 2: synchronize around adding things to a single osmdata. will have to test to see @@ -3219,8 +5158,8 @@ void PBFGraphParser::ParseNodes(const boost::property_tree::ptree& pt, callback.current_way_node_index_ = callback.last_node_ = callback.last_way_ = callback.last_relation_ = 0; // we send a null way_nodes file so that only the bike share stations are parsed - callback.reset(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - new sequence(bss_nodes_file, create)); + callback.reset(nullptr, nullptr, nullptr, nullptr, nullptr, + new sequence(bss_nodes_file, create), nullptr); OSMPBF::Parser::parse(file_handle, static_cast(OSMPBF::Interest::NODES), callback); create = false; @@ -3250,7 +5189,7 @@ void PBFGraphParser::ParseNodes(const boost::property_tree::ptree& pt, // each time we parse nodes we have to run through the way nodes file from the beginning because // because osm node ids are only sorted at the single pbf file level callback.reset(nullptr, new sequence(way_nodes_file, false), nullptr, nullptr, - nullptr, nullptr, nullptr); + nullptr, nullptr, new sequence(linguistic_node_file, true)); callback.current_way_node_index_ = callback.last_node_ = callback.last_way_ = callback.last_relation_ = 0; OSMPBF::Parser::parse(file_handle, @@ -3290,6 +5229,7 @@ void PBFGraphParser::ParseNodes(const boost::property_tree::ptree& pt, LOG_INFO("Number of nodes with refs (exits) = " + std::to_string(osmdata.node_ref_count)); LOG_INFO("Number of nodes with exit_to = " + std::to_string(osmdata.node_exit_to_count)); LOG_INFO("Number of nodes with names = " + std::to_string(osmdata.node_name_count)); + LOG_INFO("Number of nodes with linguistics = " + std::to_string(osmdata.node_linguistic_count)); LOG_INFO("Number of way refs = " + std::to_string(osmdata.way_ref.size())); LOG_INFO("Number of reverse way refs = " + std::to_string(osmdata.way_ref_rev.size())); LOG_INFO("Unique Node Strings (names, refs, etc.) = " + std::to_string(osmdata.node_names.Size())); diff --git a/src/mjolnir/restrictionbuilder.cc b/src/mjolnir/restrictionbuilder.cc index 6c8c050a81..fcc46e70ab 100644 --- a/src/mjolnir/restrictionbuilder.cc +++ b/src/mjolnir/restrictionbuilder.cc @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include @@ -270,8 +270,8 @@ ComplexRestrictionBuilder CreateComplexRestriction(const OSMRestriction& restric }; struct Result { - uint32_t forward_restrictions_count; - uint32_t reverse_restrictions_count; + uint32_t forward_restrictions_count = 0; + uint32_t reverse_restrictions_count = 0; std::vector restrictions; std::unordered_set part_of_restriction; }; @@ -752,7 +752,7 @@ void RestrictionBuilder::Build(const boost::property_tree::ptree& pt, std::vector> promises(threads.size()); // Start the threads - LOG_INFO("Adding Restrictions at level " + std::to_string(tl->level)); + LOG_INFO("Adding complex turn restrictions at level " + std::to_string(tl->level)); for (size_t i = 0; i < threads.size(); ++i) { threads[i].reset(new std::thread(build, std::cref(complex_from_restrictions_file), std::cref(complex_to_restrictions_file), diff --git a/src/mjolnir/servicedays.cc b/src/mjolnir/servicedays.cc index 1be04da880..694cef2e32 100644 --- a/src/mjolnir/servicedays.cc +++ b/src/mjolnir/servicedays.cc @@ -150,7 +150,7 @@ uint64_t remove_service_day(const uint64_t& days, if (start_date <= removed_date && removed_date <= enddate) { uint32_t length = static_cast((removed_date - start_date).count()); - return ~((~days) | (static_cast(1) << length)); + return days & ~(static_cast(1) << length); } return days; } diff --git a/src/mjolnir/shortcutbuilder.cc b/src/mjolnir/shortcutbuilder.cc index ee19f1a5d4..9dccb4b9d0 100644 --- a/src/mjolnir/shortcutbuilder.cc +++ b/src/mjolnir/shortcutbuilder.cc @@ -3,11 +3,6 @@ #include #include -#include -#include -#include -#include -#include #include #include #include @@ -29,6 +24,50 @@ using namespace valhalla::mjolnir; namespace { +struct ShortcutAccessRestriction { + std::unordered_map all_restrictions; + // important to set the edge's attribute + uint64_t modes = 0; + + ShortcutAccessRestriction(const std::vector&& restrictions) { + for (const auto& res : restrictions) { + modes |= res.modes(); + all_restrictions.emplace(res.type(), std::move(res)); + } + }; + + // updates non-conditional restrictions if their value is lower than the current value + // TODO(nils): we could also contract over conditional restrictions with a bit more work: + // kTimeDenied is fine to just append all restrictions of the base edges, but kTimeAllowed + // will be harder, there we'll have to merge overlapping time periods + void update_nonconditional(const std::vector&& other_restrictions) { + for (const auto& new_ar : other_restrictions) { + // update the modes for the edge attribute regardless + if (new_ar.type() == AccessType::kTimedAllowed || new_ar.type() == AccessType::kTimedDenied || + new_ar.type() == AccessType::kDestinationAllowed) { + continue; + } + modes |= new_ar.modes(); + auto ar_inserted = all_restrictions.emplace(new_ar.type(), new_ar); + if (!ar_inserted.second && new_ar.value() < ar_inserted.first->second.value()) { + ar_inserted.first->second = std::move(new_ar); + } + } + } +}; + +// only keeps access restrictions which can fail contraction +void remove_nonconditional_restrictions(std::vector& access_restrictions) { + access_restrictions.erase(std::remove_if(std::begin(access_restrictions), + std::end(access_restrictions), + [](const AccessRestriction& elem) { + return elem.type() != AccessType::kDestinationAllowed && + elem.type() != AccessType::kTimedAllowed && + elem.type() != AccessType::kTimedDenied; + }), + std::end(access_restrictions)); +} + // Simple structure to hold the 2 pair of directed edges at a node. // First edge in the pair is incoming and second is outgoing struct EdgePairs { @@ -71,31 +110,27 @@ bool EdgesMatch(const graph_tile_ptr& tile, const DirectedEdge* edge1, const Dir // bridge and tunnel here if (edge1->classification() != edge2->classification() || edge1->link() != edge2->link() || edge1->use() != edge2->use() || edge1->toll() != edge2->toll() || - edge1->destonly() != edge2->destonly() || edge1->unpaved() != edge2->unpaved() || - edge1->surface() != edge2->surface() || edge1->roundabout() != edge2->roundabout()) { + edge1->destonly() != edge2->destonly() || edge1->destonly_hgv() != edge2->destonly_hgv() || + edge1->unpaved() != edge2->unpaved() || edge1->surface() != edge2->surface() || + edge1->roundabout() != edge2->roundabout()) { return false; } - // Names must match - // TODO - this allows matches in any order. Do we need to maintain order? - // TODO - should allow near matches? - std::vector edge1names = tile->GetNames(edge1); - std::vector edge2names = tile->GetNames(edge2); - if (edge1names.size() != edge2names.size()) { - return false; - } - for (const auto& name1 : edge1names) { - bool found = false; - for (const auto& name2 : edge2names) { - if (name1 == name2) { - found = true; - break; - } - } - if (!found) { + // if there's conditional access restrictions, they must match; others we can safely contract over + if (edge1->access_restriction() || edge2->access_restriction()) { + auto res1 = tile->GetAccessRestrictions(edge1 - tile->directededge(0), kVehicularAccess); + remove_nonconditional_restrictions(res1); + auto res2 = tile->GetAccessRestrictions(edge2 - tile->directededge(0), kVehicularAccess); + remove_nonconditional_restrictions(res2); + if (res1.size() != res2.size()) return false; + for (size_t i = 0; i < res1.size(); ++i) { + if (res1[i].type() != res2[i].type() || res1[i].modes() != res2[i].modes() || + res1[i].value() != res2[i].value()) + return false; } } + return true; } @@ -132,26 +167,6 @@ GraphId GetOpposingEdge(const GraphId& node, return GraphId(0, 0, 0); } -/** - * Is there an opposing edge with matching edgeinfo offset. The end node of the directed edge - * must be in the same tile as the directed edge. - * @param tile Graph tile of the edge - * @param directededge Directed edge to match. - */ -bool OpposingEdgeInfoMatches(const graph_tile_ptr& tile, const DirectedEdge* edge) { - // Get the nodeinfo at the end of the edge. Iterate through the directed edges and return - // true if a matching edgeinfo offset if found. - const NodeInfo* nodeinfo = tile->node(edge->endnode().id()); - const DirectedEdge* directededge = tile->directededge(nodeinfo->edge_index()); - for (uint32_t i = 0; i < nodeinfo->edge_count(); i++, directededge++) { - // Return true if the edge info matches (same name, shape, etc.) - if (directededge->edgeinfo_offset() == edge->edgeinfo_offset()) { - return true; - } - } - return false; -} - // Get the ISO country code at the end node std::string EndNodeIso(const DirectedEdge* edge, GraphReader& reader) { graph_tile_ptr tile = reader.GetGraphTile(edge->endnode()); @@ -212,7 +227,7 @@ bool CanContract(GraphReader& reader, return false; // Exactly one pair of edges match. Check if any other remaining edges - // are driveable outbound from the node. If so this cannot be contracted. + // are drivable outbound from the node. If so this cannot be contracted. // NOTE-this seems to cause issues on PA Tpke / Breezewood /* for (uint32_t i = 0; i < n; i++) { if (i != match.first && i != match.second) { @@ -247,11 +262,6 @@ bool CanContract(GraphReader& reader, return false; } - // Can not have different speeds in the same direction - if ((oppdiredge1->speed() != edge2->speed()) || (oppdiredge2->speed() != edge1->speed())) { - return false; - } - // ISO country codes at the end nodes must equal this node std::string iso = tile->admininfo(nodeinfo->admin_index()).country_iso(); std::string e1_iso = EndNodeIso(edge1, reader); @@ -264,14 +274,14 @@ bool CanContract(GraphReader& reader, // and there are other edges at the node (forward intersecting edge or a // 'T' intersection if (nodeinfo->local_edge_count() > 2) { - // Find number of driveable edges - uint32_t driveable = 0; + // Find number of drivable edges + uint32_t drivable = 0; for (uint32_t i = 0; i < nodeinfo->local_edge_count(); i++) { if (nodeinfo->local_driveability(i) != Traversability::kNone) { - driveable++; + drivable++; } } - if (driveable > 2) { + if (drivable > 2) { uint32_t heading1 = (nodeinfo->heading(edge1->localedgeidx()) + 180) % 360; uint32_t turn_degree = GetTurnDegree(heading1, nodeinfo->heading(edge2->localedgeidx())); if (turn_degree > 60 && turn_degree < 300) { @@ -287,16 +297,17 @@ bool CanContract(GraphReader& reader, } // Connect 2 edges shape and update the next end node in the new level -uint32_t ConnectEdges(GraphReader& reader, - const GraphId& startnode, - const GraphId& edgeid, - std::list& shape, - GraphId& endnode, - uint32_t& opp_local_idx, - uint32_t& restrictions, - float& average_density, - float& total_duration, - float& total_truck_duration) { +void ConnectEdges(GraphReader& reader, + const GraphId& startnode, + const GraphId& edgeid, + std::list& shape, + GraphId& endnode, + uint32_t& opp_local_idx, + uint32_t& restrictions, + float& average_density, + float& total_duration, + float& total_truck_duration, + ShortcutAccessRestriction& access_restrictions) { // Get the tile and directed edge. auto tile = reader.GetGraphTile(startnode); const DirectedEdge* directededge = tile->directededge(edgeid); @@ -311,7 +322,6 @@ uint32_t ConnectEdges(GraphReader& reader, total_duration += edge_duration; // Add edge and turn duration for truck - // Currently use the same as for cars. TODO: implement for trucks total_truck_duration += turn_duration; auto const truck_speed = tile->GetSpeed(directededge, kNoFlowMask, kInvalidSecondsOfWeek, true); assert(truck_speed != 0); @@ -338,9 +348,11 @@ uint32_t ConnectEdges(GraphReader& reader, // Add to the weighted average average_density += directededge->length() * directededge->density(); - // Update the end node and return the length + // Preserve the most restrictive access restrictions + access_restrictions.update_nonconditional(tile->GetAccessRestrictions(edgeid.id(), kAllAccess)); + + // Update the end node endnode = directededge->endnode(); - return directededge->length(); } // Check if the edge is entering a contracted node @@ -352,19 +364,18 @@ bool IsEnteringEdgeOfContractedNode(GraphReader& reader, const GraphId& nodeid, } // Add shortcut edges (if they should exist) from the specified node -// TODO - need to add access restrictions? -uint32_t AddShortcutEdges(GraphReader& reader, - const graph_tile_ptr& tile, - GraphTileBuilder& tilebuilder, - const GraphId& start_node, - const uint32_t edge_index, - const uint32_t edge_count, - std::unordered_map& shortcuts) { +std::pair AddShortcutEdges(GraphReader& reader, + const graph_tile_ptr& tile, + GraphTileBuilder& tilebuilder, + const GraphId& start_node, + const uint32_t edge_index, + const uint32_t edge_count, + std::unordered_map& shortcuts) { // Shortcut edges have to start at a node that is not contracted - return if // this node can be contracted. EdgePairs edgepairs; if (CanContract(reader, tile, start_node, edgepairs)) { - return 0; + return {uint32_t(0), uint32_t(0)}; } // Check if this is the last edge in a shortcut (if the endnode cannot be contracted). @@ -375,6 +386,7 @@ uint32_t AddShortcutEdges(GraphReader& reader, // Iterate through directed edges of the base node uint32_t shortcut = 0; uint32_t shortcut_count = 0; + uint32_t total_edge_count = 0; GraphId edge_id(start_node.tileid(), start_node.level(), edge_index); for (uint32_t i = 0; i < edge_count; i++, ++edge_id) { // Skip transit connection edges. @@ -392,19 +404,21 @@ uint32_t AddShortcutEdges(GraphReader& reader, // edge of the contracted node. GraphId end_node = directededge->endnode(); if (IsEnteringEdgeOfContractedNode(reader, end_node, edge_id)) { + total_edge_count++; // Form a shortcut edge. DirectedEdge newedge = *directededge; - uint32_t length = newedge.length(); // For computing weighted density and total turn duration along the shortcut - float average_density = length * newedge.density(); + uint32_t edge_length = newedge.length(); + float average_density = edge_length * newedge.density(); uint32_t const speed = tile->GetSpeed(directededge, kNoFlowMask); assert(speed != 0); - float total_duration = length / (speed * kKPHtoMetersPerSec); + float total_duration = edge_length / (speed * kKPHtoMetersPerSec); uint32_t const truck_speed = - tile->GetSpeed(directededge, kNoFlowMask, kInvalidSecondsOfWeek, true); + std::min(tile->GetSpeed(directededge, kNoFlowMask, kInvalidSecondsOfWeek, true), + directededge->truck_speed() ? directededge->truck_speed() : kMaxAssumedTruckSpeed); assert(truck_speed != 0); - float total_truck_duration = directededge->length() / (truck_speed * kKPHtoMetersPerSec); + float total_truck_duration = edge_length / (truck_speed * kKPHtoMetersPerSec); // Get the shape for this edge. If this initial directed edge is not // forward - reverse the shape so the edge info stored is forward for @@ -416,22 +430,10 @@ uint32_t AddShortcutEdges(GraphReader& reader, std::reverse(shape.begin(), shape.end()); } - // Get names - they apply over all edges of the shortcut - auto names = edgeinfo.GetNames(); - auto tagged_values = edgeinfo.GetTaggedValues(); - auto pronunciations = edgeinfo.GetTaggedValues(true); - - auto types = edgeinfo.GetTypes(); - - // Add any access restriction records. TODO - make sure we don't contract - // across edges with different restrictions. - if (newedge.access_restriction()) { - auto restrictions = tile->GetAccessRestrictions(edge_id.id(), kAllAccess); - for (const auto& res : restrictions) { - tilebuilder.AddAccessRestriction(AccessRestriction(tilebuilder.directededges().size(), - res.type(), res.modes(), res.value())); - } - } + // store all access_restrictions of the base edge: non-conditional ones will be updated while + // contracting, conditional ones are breaking contraction and are safe to simply copy + ShortcutAccessRestriction access_restrictions{ + tile->GetAccessRestrictions(edge_id.id(), kAllAccess)}; // Connect edges to the shortcut while the end node is marked as // contracted (contains edge pairs in the shortcut info). @@ -454,7 +456,7 @@ uint32_t AddShortcutEdges(GraphReader& reader, next_edge_id = edgepairs.edge2.second; } else { // Break out of loop. This case can happen when a shortcut edge - // enters another shortcut edge (but is not driveable in reverse + // enters another shortcut edge (but is not drivable in reverse // direction from the node). const DirectedEdge* de = tile->directededge(next_edge_id); LOG_ERROR("Edge not found in edge pairs. WayID = " + @@ -466,27 +468,27 @@ uint32_t AddShortcutEdges(GraphReader& reader, // end node in the new level). Keep track of the last restriction // on the connected shortcut - need to set that so turn restrictions // off of shortcuts work properly - length += ConnectEdges(reader, end_node, next_edge_id, shape, end_node, opp_local_idx, rst, - average_density, total_duration, total_truck_duration); + ConnectEdges(reader, end_node, next_edge_id, shape, end_node, opp_local_idx, rst, + average_density, total_duration, total_truck_duration, access_restrictions); + total_edge_count++; } - // Do we need to force adding edgeinfo (opposing edge could have diff names)? - // If end node is in the same tile and opposing edge does not have matching - // edge_info_offset). - bool diff_names = directededge->endnode().tileid() == edge_id.tileid() && - !OpposingEdgeInfoMatches(tile, directededge); + // Get the length from the shape. This prevents roundoff issues when forming + // elevation. + uint32_t length = valhalla::midgard::length(shape); // Add the edge info. Use length and number of shape points to match an // edge in case multiple shortcut edges exist between the 2 nodes. // Test whether this shape is forward or reverse (in case an existing // edge exists). Shortcuts use way Id = 0.Set mean elevation to 0 as a placeholder, - // set it later if adding elevation to this dataset. + // set it later if adding elevation to this dataset. No need for names etc, shortcuts + // aren't used in guidance bool forward = true; uint32_t idx = ((length & 0xfffff) | ((shape.size() & 0xfff) << 20)); uint32_t edge_info_offset = tilebuilder.AddEdgeInfo(idx, start_node, end_node, 0, 0, edgeinfo.bike_network(), - edgeinfo.speed_limit(), shape, names, tagged_values, pronunciations, - types, forward, diff_names); + edgeinfo.speed_limit(), shape, {}, {}, {}, 0, forward, false); + ; newedge.set_edgeinfo_offset(edge_info_offset); @@ -500,6 +502,18 @@ uint32_t AddShortcutEdges(GraphReader& reader, newedge.set_opp_local_idx(opp_local_idx); newedge.set_restrictions(rst); + // add new access restrictions if any and set the mask on the edge + if (access_restrictions.all_restrictions.size()) { + newedge.set_access_restriction(access_restrictions.modes); + for (const auto& res : access_restrictions.all_restrictions) { + tilebuilder.AddAccessRestriction(AccessRestriction(tilebuilder.directededges().size(), + res.second.type(), res.second.modes(), + res.second.value())); + } + } + + // set new access mask + // Update the length, curvature, and end node newedge.set_length(length); newedge.set_curvature(compute_curvature(shape)); @@ -568,16 +582,17 @@ uint32_t AddShortcutEdges(GraphReader& reader, LOG_WARN("Exceeding max shortcut edges from a node at LL = " + std::to_string(ll.lat()) + "," + std::to_string(ll.lng())); } - return shortcut_count; + return {shortcut_count, total_edge_count}; } // Form shortcuts for tiles in this level. -uint32_t FormShortcuts(GraphReader& reader, const TileLevel& level) { +std::pair FormShortcuts(GraphReader& reader, const TileLevel& level) { // Iterate through the tiles at this level (TODO - can we mark the tiles // the tiles that shortcuts end within?) reader.Clear(); bool added = false; uint32_t shortcut_count = 0; + uint32_t total_edge_count = 0; uint32_t ntiles = level.tiles.TileCount(); uint32_t tile_level = (uint32_t)level.level; graph_tile_ptr tile; @@ -619,8 +634,10 @@ uint32_t FormShortcuts(GraphReader& reader, const TileLevel& level) { // Add shortcut edges first. std::unordered_map shortcuts; - shortcut_count += AddShortcutEdges(reader, tile, tilebuilder, node_id, old_edge_index, - old_edge_count, shortcuts); + auto stats = AddShortcutEdges(reader, tile, tilebuilder, node_id, old_edge_index, + old_edge_count, shortcuts); + shortcut_count += stats.first; + total_edge_count += stats.second; // Copy the rest of the directed edges from this node GraphId edgeid(tileid, tile_level, old_edge_index); @@ -668,6 +685,9 @@ uint32_t FormShortcuts(GraphReader& reader, const TileLevel& level) { tilebuilder.AddLaneConnectivity(laneconnectivity); } + // Names can be different in the forward and backward direction + bool diff_names = tilebuilder.OpposingEdgeInfoDiffers(tile, directededge); + // Get edge info, shape, and names from the old tile and add // to the new. Use prior edgeinfo offset as the key to make sure // edges that have the same end nodes are differentiated (this @@ -678,8 +698,9 @@ uint32_t FormShortcuts(GraphReader& reader, const TileLevel& level) { edgeinfo.wayid(), edgeinfo.mean_elevation(), edgeinfo.bike_network(), edgeinfo.speed_limit(), edgeinfo.encoded_shape(), edgeinfo.GetNames(), - edgeinfo.GetTaggedValues(), edgeinfo.GetTaggedValues(true), - edgeinfo.GetTypes(), added); + edgeinfo.GetTaggedValues(), edgeinfo.GetLinguisticTaggedValues(), + edgeinfo.GetTypes(), added, diff_names); + newedge.set_edgeinfo_offset(edge_info_offset); // Set the superseded mask - this is the shortcut mask that supersedes this edge @@ -721,7 +742,7 @@ uint32_t FormShortcuts(GraphReader& reader, const TileLevel& level) { reader.Trim(); } } - return shortcut_count; + return {shortcut_count, total_edge_count}; } } // namespace @@ -746,8 +767,11 @@ void ShortcutBuilder::Build(const boost::property_tree::ptree& pt) { for (; tile_level != TileHierarchy::levels().rend(); ++tile_level) { // Create shortcuts on this level LOG_INFO("Creating shortcuts on level " + std::to_string(tile_level->level)); - uint32_t count = FormShortcuts(reader, *tile_level); - LOG_INFO("Finished with " + std::to_string(count) + " shortcuts"); + [[maybe_unused]] auto stats = FormShortcuts(reader, *tile_level); + [[maybe_unused]] uint32_t avg = stats.first ? (stats.second / stats.first) : 0; + LOG_INFO("Finished with " + std::to_string(stats.first) + " shortcuts superseding " + + std::to_string(stats.second) + " edges, average ~" + std::to_string(avg) + + " edges per shortcut"); } } diff --git a/src/mjolnir/speed_assigner.h b/src/mjolnir/speed_assigner.h index eeff0ed7a6..a07c2875fc 100644 --- a/src/mjolnir/speed_assigner.h +++ b/src/mjolnir/speed_assigner.h @@ -213,7 +213,7 @@ class SpeedAssigner { if (cs.HasMember("iso3166-2")) { code.push_back('.'); if (code.size() == (code += cs["iso3166-2"].GetString()).size()) - throw std::runtime_error("Cannot have emtpy state code"); + throw std::runtime_error("Cannot have empty state code"); } } if (tables.count(code)) diff --git a/src/mjolnir/statistics.cc b/src/mjolnir/statistics.cc index debb274c13..faa38c0f72 100644 --- a/src/mjolnir/statistics.cc +++ b/src/mjolnir/statistics.cc @@ -4,13 +4,8 @@ #include "mjolnir/graphvalidator.h" #include "statistics.h" -#include #include #include -#include -#include -#include -#include #include #include #include diff --git a/src/mjolnir/statistics.h b/src/mjolnir/statistics.h index 952ad1837b..fde30e0a50 100644 --- a/src/mjolnir/statistics.h +++ b/src/mjolnir/statistics.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_STATISTICS_H_ #include -#include #include #include #include diff --git a/src/mjolnir/statistics_database.cc b/src/mjolnir/statistics_database.cc index 8f8806f3be..0ba0d00793 100644 --- a/src/mjolnir/statistics_database.cc +++ b/src/mjolnir/statistics_database.cc @@ -1,8 +1,6 @@ #include "statistics.h" #include -#include - #include "filesystem.h" #include "midgard/logging.h" #include "mjolnir/util.h" diff --git a/src/mjolnir/timeparsing.cc b/src/mjolnir/timeparsing.cc index 09432741f8..790e84271c 100644 --- a/src/mjolnir/timeparsing.cc +++ b/src/mjolnir/timeparsing.cc @@ -1,10 +1,8 @@ -#include #include #include #include #include -#include #include #include "baldr/graphconstants.h" @@ -15,8 +13,78 @@ using namespace valhalla::baldr; using namespace valhalla::mjolnir; -namespace valhalla { -namespace mjolnir { +namespace { +// Dec Su[-1]-Mar 3 => Dec#Su#[5]-Mar#3 +const std::pair kBeginWeekdayOfTheMonth = + {std::regex( + "(?:(January|February|March|April|May|June|July|" + "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" + "Sep|Sept|Oct|Nov|Dec)) (?:(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|" + "Sunday|Mon|Mo|Tues|Tue|Tu|Weds|Wed|We|Thurs|Thur|Th|Fri|Fr|Sat|Sa|Sun|Su)(\\[-?[0-9]\\])-" + "(?:(January|February|March|April|May|June|July|August|September|October|November" + "|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)) (\\d{1,2}))", + std::regex_constants::icase), + "$1#$2#$3-$4#$5"}; + +// Mar 3-Dec Su[-1] => Mar#3-Dec#Su#[5] +const std::pair kEndWeedkayOfTheMonth = + {std::regex( + "(?:(January|February|March|April|May|June|July|August|" + "September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|" + "Nov|Dec)) (\\d{1,2})-(?:(January|February|March|April|May|June|July|" + "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" + "Sep|Sept|Oct|Nov|Dec)) (?:(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|" + "Sunday|Mon|Mo|Tues|Tue|Tu|Weds|Wed|We|Thurs|Thur|Th|Fri|Fr|Sat|Sa|Sun|Su)(\\[-?[0-9]\\])" + ")", + std::regex_constants::icase), + "$1#$2-$3#$4#$5"}; + +// Dec Su[-1] => Dec#Su#[5] +const std::pair kWeekdayOfTheMonth = + {std::regex("(?:(January|February|March|April|May|June|July|" + "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" + "Sep|Sept|Oct|Nov|Dec)) (?:(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|" + "Sunday|Mon|Mo|Tues|Tue|Tu|Weds|Wed|We|Thurs|Thur|Th|Fri|Fr|Sat|Sa|Sun|Su)(\\[-?[0-9]" + "\\]))", + std::regex_constants::icase), + "$1#$2#$3"}; + +// Mon[-1] => Mon#[5], Tue[2] => Tue#[2] +const std::pair kWeekdayOfEveryMonth = + {std::regex("(?:(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|" + "Sunday|Mon|Mo|Tues|Tue|Tu|Weds|Wed|We|Thurs|Thur|Th|Fri|Fr|Sat|Sa|Sun|" + "Su)(\\[-?[0-9]\\]))", + std::regex_constants::icase), + "$1#$2"}; + +// Feb 16-Oct 15 09:00-18:30 => Feb#16-Oct#15 09:00-18:30 +const std::pair kMonthDay = + {std::regex("(?:(January|February|March|April|May|June|July|" + "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" + "Sep|Sept|Oct|Nov|Dec)) (\\d{1,2})", + std::regex_constants::icase), + "$1#$2"}; + +// Feb 2-14 => Feb#2-Feb#14 +const std::pair kRangeWithinMonth = + {std::regex("(?:(January|February|March|April|May|June|July|" + "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" + "Sep|Sept|Oct|Nov|Dec)) (\\d{1,2})-(\\d{1,2})", + std::regex_constants::icase), + "$1#$2-$1#$3"}; + +// Nov - Mar => Nov-Mar +const std::pair kMonthRange = + {std::regex("(?:(January|February|March|April|May|June|July|" + "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" + "Sep|Sept|Oct|Nov|Dec)) - (?:(January|February|March|April|May|June|July|" + "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" + "Sep|Sept|Oct|Nov|Dec))", + std::regex_constants::icase), + "$1-$2"}; + +// fifth is the equivalent of last week in month (-1) +const std::pair kLastWeekday = {std::regex("\\[-1\\]"), "[5]"}; std::vector GetTokens(const std::string& tag_value, char delim) { std::vector tokens; @@ -32,12 +100,17 @@ bool RegexFound(const std::string& source, const std::regex& regex) { return std::distance(begin, end); } -std::string -FormatCondition(const std::string& source, const std::regex& regex, const std::string& pattern) { - return std::regex_replace(source, regex, pattern); +std::string FormatCondition(const std::string& source, + const std::pair& regex_pattern) { + return std::regex_replace(source, regex_pattern.first, regex_pattern.second); } -// get the dow mask from user inputed string. try to handle most inputs +} // namespace + +namespace valhalla { +namespace mjolnir { + +// get the dow mask from the provided string. try to handle most inputs uint8_t get_dow_mask(const std::string& dow) { std::string str = dow; @@ -68,7 +141,7 @@ uint8_t get_dow_mask(const std::string& dow) { return kDOWNone; } -// get the dow from user inputed string. try to handle most inputs +// get the dow from the provided string. try to handle most inputs DOW get_dow(const std::string& dow) { std::string str = dow; @@ -164,84 +237,44 @@ std::vector get_time_range(const std::string& str) { return time_domains; } - // Dec Su[-1]-Mar 3 - std::regex regex = std::regex( - "(?:(January|February|March|April|May|June|July|" - "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" - "Sep|Sept|Oct|Nov|Dec)) (?:(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|" - "Sunday|Mon|Mo|Tues|Tue|Tu|Weds|Wed|We|Thurs|Thur|Th|Fri|Fr|Sat|Sa|Sun|Su)(\\[-?[0-9]\\])-" - "(?:(January|February|March|April|May|June|July|August|September|October|November" - "|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)) (\\d{1,2}))", - std::regex_constants::icase); - - if (RegexFound(condition, regex)) { - condition = FormatCondition(condition, regex, "$1#$2#$3-$4#$5"); - // fifth is the equivalent of last week in month (-1) - condition = FormatCondition(condition, std::regex("\\[-1\\]"), "[5]"); + // Dec Su[-1]-Mar 3 => Dec#Su#[5]-Mar#3 + if (RegexFound(condition, kBeginWeekdayOfTheMonth.first)) { + condition = FormatCondition(condition, kBeginWeekdayOfTheMonth); + condition = FormatCondition(condition, kLastWeekday); } else { - // Mar 3-Dec Su[-1] - std::regex regex = std::regex( - "(?:(January|February|March|April|May|June|July|August|" - "September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|" - "Nov|Dec)) (\\d{1,2})-(?:(January|February|March|April|May|June|July|" - "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" - "Sep|Sept|Oct|Nov|Dec)) (?:(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|" - "Sunday|Mon|Mo|Tues|Tue|Tu|Weds|Wed|We|Thurs|Thur|Th|Fri|Fr|Sat|Sa|Sun|Su)(\\[-?[0-9]\\])" - ")", - std::regex_constants::icase); - - if (RegexFound(condition, regex)) { - condition = FormatCondition(condition, regex, "$1#$2-$3#$4#$5"); - // fifth is the equivalent of last week in month (-1) - condition = FormatCondition(condition, std::regex("\\[-1\\]"), "[5]"); + // Mar 3-Dec Su[-1] => Mar#3-Dec#Su#[5] + if (RegexFound(condition, kEndWeedkayOfTheMonth.first)) { + condition = FormatCondition(condition, kEndWeedkayOfTheMonth); + condition = FormatCondition(condition, kLastWeekday); } else { - // Dec Su[-1] - regex = std::regex( - "(?:(January|February|March|April|May|June|July|" - "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" - "Sep|Sept|Oct|Nov|Dec)) (?:(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|" - "Sunday|Mon|Mo|Tues|Tue|Tu|Weds|Wed|We|Thurs|Thur|Th|Fri|Fr|Sat|Sa|Sun|Su)(\\[-?[0-9]" - "\\]))", - std::regex_constants::icase); - - if (RegexFound(condition, regex)) { - condition = FormatCondition(condition, regex, "$1#$2#$3"); - // fifth is the equivalent of last week in month (-1) - condition = FormatCondition(condition, std::regex("\\[-1\\]"), "[5]"); + // Dec Su[-1] => Dec#Su#[5] + if (RegexFound(condition, kWeekdayOfTheMonth.first)) { + condition = FormatCondition(condition, kWeekdayOfTheMonth); + condition = FormatCondition(condition, kLastWeekday); } else { - regex = std::regex("(?:(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|" - "Sunday|Mon|Mo|Tues|Tue|Tu|Weds|Wed|We|Thurs|Thur|Th|Fri|Fr|Sat|Sa|Sun|" - "Su)(\\[-?[0-9]\\]))", - std::regex_constants::icase); - - if (RegexFound(condition, regex)) { - condition = FormatCondition(condition, regex, "$1#$2"); - // fifth is the equivalent of last week in month (-1) - condition = FormatCondition(condition, std::regex("\\[-1\\]"), "[5]"); + // Mon[-1] => Mon#[5], Tue[2] => Tue#[2] + if (RegexFound(condition, kWeekdayOfEveryMonth.first)) { + condition = FormatCondition(condition, kWeekdayOfEveryMonth); + condition = FormatCondition(condition, kLastWeekday); } else { - // Feb 16-Oct 15 09:00-18:30 - regex = std:: - regex("(?:(January|February|March|April|May|June|July|" - "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" - "Sep|Sept|Oct|Nov|Dec)) (\\d{1,2})", - std::regex_constants::icase); - - if (RegexFound(condition, regex)) { - condition = FormatCondition(condition, regex, "$1#$2"); + // Feb 16-Oct 15 09:00-18:30 => Feb#16-Oct#15 09:00-18:30 + if (RegexFound(condition, kMonthDay.first)) { + condition = FormatCondition(condition, kMonthDay); } else { - // Feb 2-14 - regex = std:: - regex("(?:(January|February|March|April|May|June|July|" - "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" - "Sep|Sept|Oct|Nov|Dec)) (\\d{1,2})-(\\d{1,2})", - std::regex_constants::icase); - - if (RegexFound(condition, regex)) { - condition = FormatCondition(condition, regex, "$1#$2-$1#$3"); + + // Feb 2-14 => Feb#2-Feb#14 + if (RegexFound(condition, kRangeWithinMonth.first)) { + condition = FormatCondition(condition, kRangeWithinMonth); + } else { + + // Nov - Mar => Nov-Mar + if (RegexFound(condition, kMonthRange.first)) { + condition = FormatCondition(condition, kMonthRange); + } } } } diff --git a/src/mjolnir/transitbuilder.cc b/src/mjolnir/transitbuilder.cc index 8836645233..09c73a96f1 100644 --- a/src/mjolnir/transitbuilder.cc +++ b/src/mjolnir/transitbuilder.cc @@ -3,12 +3,10 @@ #include #include -#include #include #include #include #include -#include #include #include @@ -44,7 +42,7 @@ struct OSMConnectionEdge { std::vector names; std::vector shape; std::vector tagged_values; - std::vector pronunciations; + std::vector linguistics; }; // Struct to hold stats information during each threads work @@ -167,8 +165,7 @@ void ConnectToGraph(GraphTileBuilder& tilebuilder_local, bool added = false; uint32_t edge_info_offset = tilebuilder_local.AddEdgeInfo(0, conn.osm_node, endnode, conn.wayid, 0, 0, 0, conn.shape, - conn.names, conn.tagged_values, conn.pronunciations, 0, - added); + conn.names, conn.tagged_values, conn.linguistics, 0, added); directededge.set_edgeinfo_offset(edge_info_offset); directededge.set_forward(true); tilebuilder_local.directededges().emplace_back(std::move(directededge)); @@ -264,8 +261,8 @@ void ConnectToGraph(GraphTileBuilder& tilebuilder_local, std::reverse(r_shape.begin(), r_shape.end()); uint32_t edge_info_offset = tilebuilder_transit.AddEdgeInfo(0, conn.stop_node, conn.osm_node, conn.wayid, 0, 0, 0, - r_shape, conn.names, conn.tagged_values, - conn.pronunciations, 0, added); + r_shape, conn.names, conn.tagged_values, conn.linguistics, + 0, added); LOG_DEBUG("Add conn from stop to OSM: ei offset = " + std::to_string(edge_info_offset)); directededge.set_edgeinfo_offset(edge_info_offset); directededge.set_forward(true); @@ -489,7 +486,7 @@ void build(const boost::property_tree::ptree& pt, // TODO - how to handle connections that reach nodes outside the tile? Need to break up the calls // to MakeConnections and ConnectToGraph below. First we have threads find all the connections - // even accross tile boundaries, we keep those in ram and then we run another pass where we add + // even across tile boundaries, we keep those in ram and then we run another pass where we add // those to the tiles when we rewrite them in ConnectToGraph. So two separate threaded steps // rather than cramming both of them into the single build function @@ -652,7 +649,7 @@ void TransitBuilder::Build(const boost::property_tree::ptree& pt) { } auto t2 = std::chrono::high_resolution_clock::now(); - uint32_t secs = std::chrono::duration_cast(t2 - t1).count(); + [[maybe_unused]] uint32_t secs = std::chrono::duration_cast(t2 - t1).count(); LOG_INFO("Finished - TransitBuilder took " + std::to_string(secs) + " secs"); } diff --git a/src/mjolnir/util.cc b/src/mjolnir/util.cc index f91d01419e..2960778d47 100644 --- a/src/mjolnir/util.cc +++ b/src/mjolnir/util.cc @@ -22,6 +22,7 @@ #include #include #include +#include using namespace valhalla::midgard; @@ -51,6 +52,7 @@ const std::string tile_manifest_file = "tile_manifest.json"; const std::string access_file = "access.bin"; const std::string pronunciation_file = "pronunciation.bin"; const std::string bss_nodes_file = "bss_nodes.bin"; +const std::string linguistic_node_file = "linguistics_node.bin"; const std::string cr_from_file = "complex_from_restrictions.bin"; const std::string cr_to_file = "complex_to_restrictions.bin"; const std::string new_to_old_file = "new_nodes_to_old_nodes.bin"; @@ -68,9 +70,19 @@ namespace mjolnir { */ std::vector GetTagTokens(const std::string& tag_value, char delim) { std::vector tokens; - boost::algorithm::split(tokens, tag_value, - std::bind(std::equal_to(), delim, std::placeholders::_1), - boost::algorithm::token_compress_off); + boost::algorithm::split( + tokens, tag_value, + [delim](auto&& PH1) { return std::equal_to()(delim, std::forward(PH1)); }, + boost::algorithm::token_compress_off); + + return tokens; +} + +std::vector GetTagTokens(const std::string& tag_value, const std::string& delim_str) { + std::regex regex_str(delim_str); + std::vector tokens(std::sregex_token_iterator(tag_value.begin(), tag_value.end(), + regex_str, -1), + std::sregex_token_iterator()); return tokens; } @@ -161,6 +173,41 @@ bool shapes_match(const std::vector& shape1, const std::vector } } +/** + * Get the index of the opposing edge at the end node. This is on the local hierarchy, + * before adding transition and shortcut edges. Make sure that even if the end nodes + * and lengths match that the correct edge is selected (match shape) since some loops + * can have the same length and end node. + */ +uint32_t GetOpposingEdgeIndex(const graph_tile_ptr& endnodetile, + const baldr::GraphId& startnode, + const graph_tile_ptr& tile, + const baldr::DirectedEdge& edge) { + // Get the nodeinfo at the end of the edge + const baldr::NodeInfo* nodeinfo = endnodetile->node(edge.endnode().id()); + + // Iterate through the directed edges and return when the end node matches the specified + // node, the length matches, and the shape matches (or edgeinfo offset matches) + const baldr::DirectedEdge* directededge = endnodetile->directededge(nodeinfo->edge_index()); + for (uint32_t i = 0; i < nodeinfo->edge_count(); i++, directededge++) { + if (directededge->endnode() == startnode && directededge->length() == edge.length()) { + // If in the same tile and the edgeinfo offset matches then the shape and names will match + if (endnodetile == tile && directededge->edgeinfo_offset() == edge.edgeinfo_offset()) { + return i; + } else { + // Need to compare shape if not in the same tile or different EdgeInfo (could be different + // names in opposing directions) + if (shapes_match(tile->edgeinfo(&edge).shape(), + endnodetile->edgeinfo(directededge).shape())) { + return i; + } + } + } + } + LOG_ERROR("Could not find opposing edge index"); + return baldr::kMaxEdgesPerNode; +} + std::shared_ptr make_spatialite_cache(sqlite3* handle) { if (!handle) { return nullptr; @@ -169,7 +216,7 @@ std::shared_ptr make_spatialite_cache(sqlite3* handle) { spatialite_singleton_t::get_instance(); void* conn = spatialite_alloc_connection(); spatialite_init_ex(handle, conn, 0); - return std::shared_ptr(conn, [](void* c) { spatialite_cleanup_ex(c); }); + return {conn, [](void* c) { spatialite_cleanup_ex(c); }}; } bool build_tile_set(const boost::property_tree::ptree& original_config, @@ -183,6 +230,11 @@ bool build_tile_set(const boost::property_tree::ptree& original_config, } }; + if (input_files.size() > 1) { + LOG_WARN( + "Tile building using more than one osm.pbf extract is discouraged. Consider merging the extracts into one file. See this issue for more info: https://github.com/valhalla/valhalla/issues/3925 "); + } + // Take out tile_extract and tile_url from property tree as tiles must only use the tile_dir auto config = original_config; config.get_child("mjolnir").erase("tile_extract"); @@ -226,8 +278,8 @@ bool build_tile_set(const boost::property_tree::ptree& original_config, std::string edges_bin = tile_dir + edges_file; std::string tile_manifest = tile_dir + tile_manifest_file; std::string access_bin = tile_dir + access_file; - std::string pronunciation_bin = tile_dir + pronunciation_file; std::string bss_nodes_bin = tile_dir + bss_nodes_file; + std::string linguistic_node_bin = tile_dir + linguistic_node_file; std::string cr_from_bin = tile_dir + cr_from_file; std::string cr_to_bin = tile_dir + cr_to_file; std::string new_to_old_bin = tile_dir + new_to_old_file; @@ -240,7 +292,7 @@ bool build_tile_set(const boost::property_tree::ptree& original_config, if (start_stage <= BuildStage::kParseWays && BuildStage::kParseWays <= end_stage) { // Read the OSM protocol buffer file. Callbacks for ways are defined within the PBFParser class osm_data = PBFGraphParser::ParseWays(config.get_child("mjolnir"), input_files, ways_bin, - way_nodes_bin, access_bin, pronunciation_bin); + way_nodes_bin, access_bin); // Free all protobuf memory - cannot use the protobuffer lib after this! if (release_osmpbf_memory && BuildStage::kParseWays == end_stage) { @@ -277,7 +329,7 @@ bool build_tile_set(const boost::property_tree::ptree& original_config, // Read the OSM protocol buffer file. Callbacks for nodes // are defined within the PBFParser class PBFGraphParser::ParseNodes(config.get_child("mjolnir"), input_files, way_nodes_bin, bss_nodes_bin, - osm_data); + linguistic_node_bin, osm_data); // Free all protobuf memory - cannot use the protobuffer lib after this! if (release_osmpbf_memory) { @@ -321,7 +373,7 @@ bool build_tile_set(const boost::property_tree::ptree& original_config, // Build the graph using the OSMNodes and OSMWays from the parser GraphBuilder::Build(config, osm_data, ways_bin, way_nodes_bin, nodes_bin, edges_bin, cr_from_bin, - cr_to_bin, pronunciation_bin, tiles); + cr_to_bin, linguistic_node_bin, tiles); } // Enhance the local level of the graph. This adds information to the local @@ -401,10 +453,10 @@ bool build_tile_set(const boost::property_tree::ptree& original_config, remove_temp_file(nodes_bin); remove_temp_file(edges_bin); remove_temp_file(access_bin); - remove_temp_file(pronunciation_bin); remove_temp_file(bss_nodes_bin); remove_temp_file(cr_from_bin); remove_temp_file(cr_to_bin); + remove_temp_file(linguistic_node_bin); remove_temp_file(new_to_old_bin); remove_temp_file(old_to_new_bin); remove_temp_file(tile_manifest); diff --git a/src/mjolnir/valhalla_add_elevation.cc b/src/mjolnir/valhalla_add_elevation.cc index 242520f576..ba8083229b 100644 --- a/src/mjolnir/valhalla_add_elevation.cc +++ b/src/mjolnir/valhalla_add_elevation.cc @@ -6,11 +6,13 @@ #include #include +#include "baldr/graphid.h" #include "baldr/graphreader.h" #include "baldr/graphtile.h" #include "baldr/rapidjson_utils.h" #include "mjolnir/elevationbuilder.h" -#include "mjolnir/valhalla_add_elevation_utils.h" + +#include "argparse_utils.h" namespace opt = cxxopts; @@ -18,7 +20,36 @@ using namespace valhalla::baldr; using namespace valhalla::midgard; using namespace valhalla::mjolnir; -enum Input { CONFIG, TILES }; +namespace { +std::deque get_tile_ids(const boost::property_tree::ptree& pt, + const std::unordered_set& tiles) { + if (tiles.empty()) + return {}; + + auto tile_dir = pt.get_optional("mjolnir.tile_dir"); + if (!tile_dir || !filesystem::exists(*tile_dir)) { + LOG_WARN("Tile storage directory does not exist"); + return {}; + } + + std::unordered_set tiles_set{tiles.begin(), tiles.end()}; + + std::deque tilequeue; + GraphReader reader(pt.get_child("mjolnir")); + std::for_each(std::begin(tiles), std::end(tiles), [&](const auto& tile) { + auto tile_id = GraphTile::GetTileId(*tile_dir + tile); + GraphId local_tile_id(tile_id.tileid(), tile_id.level(), tile_id.id()); + if (!reader.DoesTileExist(local_tile_id)) { + LOG_WARN("Provided tile doesn't belong to the tile directory from config file"); + return; + } + + tilequeue.push_back(tile_id); + }); + + return tilequeue; +} +} // namespace /* * This tool downloads elevations from remote storage for each provided tile. @@ -30,95 +61,62 @@ enum Input { CONFIG, TILES }; * - tiles - stays for the path to a tile file. * */ -std::pair> parse_arguments(int argc, char** argv) { - opt::Options options( - "help", - " Usage: valhalla_add_elevation [options]\n" - "valhalla_add_elevation is a tool for loading elevations for a provided tile. " - "The service checks if required elevations stored locally if they are not " - "it tries to establish connection to the remote storage(based on the information from configuration file)" - "and loads required elevations.\n"); - - options.add_options()("h,help", - "Print usage")("c,config", "Path to the configuration file.", - opt::value< - std::string>())("t,tiles", "Tiles to add elevations to", - opt::value>()); - - options.allow_unrecognised_options(); - std::string config_file; +int main(int argc, char** argv) { + const auto program = filesystem::path(__FILE__).stem().string(); + // args std::vector tiles; - try { - auto result = options.parse(argc, argv); - - if (result.count("help")) { - std::cout << options.help() << "\n"; - return {}; - } + boost::property_tree::ptree config; - if (!result.count("config")) { - std::cerr << "No configuration file provided" - << "\n\n"; - } else { - config_file = result["config"].as(); - } + try { + // clang-format off + opt::Options options( + program, + std::string(program) + " " + VALHALLA_VERSION + "\n\n" + "a tool for loading elevations for a provided tile. " + "The service checks if required elevations stored locally if they are not " + "it tries to establish connection to the remote storage (based on the information from configuration file)" + "and loads required elevations.\n"); + + options.add_options() + ("h,help", "Print usage") + ("v,version", "Print the version of this software.") + ("c,config", "Path to the configuration file.", opt::value()) + ("i,inline-config", "Inline JSON config", cxxopts::value()) + ("t,tiles", "Tiles to add elevations to", opt::value>(tiles)) + ("j,concurrency", "Number of threads to use. Defaults to all threads.", opt::value()); + // clang-format on + + const auto result = options.parse(argc, argv); + if (!parse_common_args(program, options, result, config, "mjolnir.logging", true)) + return EXIT_SUCCESS; if (!result.count("tiles")) { - std::cerr << "No tile file provided" - << "\n\n"; + std::cerr << "Tile file is required\n\n" << options.help() << "\n\n"; + return EXIT_FAILURE; } else { - tiles = result["tiles"].as>(); + for (const auto& tile : result["concurrency"].as>()) { + if (filesystem::exists(tile) && filesystem::is_regular_file(tile)) + return true; + } + std::cerr << "All tile files are invalid\n\n" << options.help() << "\n\n"; + return EXIT_FAILURE; } - } catch (std::exception& e) { - std::cerr << "Unable to parse command line options. Error: " << e.what() << "\n"; - return {}; - } - - return {config_file, tiles}; -} - -std::unordered_set get_valid_tile_paths(std::vector&& tiles) { - std::unordered_set st; - for (const auto& tile : tiles) { - if (filesystem::exists(tile) && filesystem::is_regular_file(tile)) - st.insert(tile); - } - - return st; -} - -int main(int argc, char** argv) { - auto params = parse_arguments(argc, argv); - if (std::get(params).empty() && std::get(params).empty()) { - return EXIT_SUCCESS; - } - - if (std::get(params).empty() || std::get(params).empty()) { - std::cerr << "Invalid input: " << (std::get(params).empty() ? "config" : "tile") - << " was not provided\n\n"; + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; return EXIT_FAILURE; - } - - if (!filesystem::exists(std::get(params)) || - !filesystem::is_regular_file(std::get(params))) { - std::cerr << "Fail to parse configuration file\n\n"; - return EXIT_FAILURE; - } - - auto tiles = get_valid_tile_paths(std::move(std::get(params))); - if (tiles.empty()) { - std::cerr << "All tile files are invalid\n\n"; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; return EXIT_FAILURE; } - boost::property_tree::ptree pt; - rapidjson::read_json(std::get(params), pt); - auto tile_ids = valhalla::mjolnir::get_tile_ids(pt, tiles); + // pass the deduplicated tiles + auto tile_ids = get_tile_ids(config, std::unordered_set(tiles.begin(), tiles.end())); if (tile_ids.empty()) { std::cerr << "Failed to load tiles\n\n"; return EXIT_FAILURE; } - ElevationBuilder::Build(pt, tile_ids); + ElevationBuilder::Build(config, tile_ids); return EXIT_SUCCESS; } \ No newline at end of file diff --git a/src/mjolnir/valhalla_add_landmarks.cc b/src/mjolnir/valhalla_add_landmarks.cc new file mode 100644 index 0000000000..4cec1bbed4 --- /dev/null +++ b/src/mjolnir/valhalla_add_landmarks.cc @@ -0,0 +1,49 @@ +#include "argparse_utils.h" +#include "baldr/rapidjson_utils.h" +#include "filesystem.h" +#include "midgard/logging.h" +#include "mjolnir/landmarks.h" +#include + +int main(int argc, char** argv) { + const auto program = filesystem::path(__FILE__).stem().string(); + // args + boost::property_tree::ptree config; + + try { + // clang-format off + cxxopts::Options options( + program, + program + " " + VALHALLA_VERSION + "\n\n" + "valhalla_add_landmarks is a program that adds landmarks to existing graph tiles via a SQLite" + " database containing landmark POIs.\n" + "\n\n"); + + options.add_options() + ("h,help", "Print this help message.") + ("v,version", "Print the version of this software.") + ("c,config", "Path to the json configuration file.", cxxopts::value()) + ("j,concurrency", "Number of threads to use when processing the data.", cxxopts::value()) + ("i,inline-config", "Inline JSON config", cxxopts::value()); + // clang-format on + + options.parse_positional({"input_files"}); + options.positional_help("OSM PBF file(s)"); + auto result = options.parse(argc, argv); + if (!parse_common_args(program, options, result, config, "mjolnir.logging", true)) + return EXIT_SUCCESS; + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; + return EXIT_FAILURE; + } + + if (!valhalla::mjolnir::AddLandmarks(config.get_child("mjolnir"))) { + return EXIT_FAILURE; + }; + + return EXIT_SUCCESS; +} diff --git a/src/mjolnir/valhalla_add_predicted_traffic.cc b/src/mjolnir/valhalla_add_predicted_traffic.cc index 390622e642..2eb13290e5 100644 --- a/src/mjolnir/valhalla_add_predicted_traffic.cc +++ b/src/mjolnir/valhalla_add_predicted_traffic.cc @@ -1,11 +1,9 @@ #include #include #include -#include #include -#include #include -#include +#include #include #include @@ -18,14 +16,13 @@ #include "baldr/graphreader.h" #include "baldr/predictedspeeds.h" -#include "baldr/rapidjson_utils.h" #include "filesystem.h" #include "midgard/logging.h" #include "midgard/util.h" #include "mjolnir/graphtilebuilder.h" #include "mjolnir/util.h" -#include "config.h" +#include "argparse_utils.h" namespace vm = valhalla::midgard; namespace vb = valhalla::baldr; @@ -33,12 +30,6 @@ namespace vj = valhalla::mjolnir; namespace bpt = boost::property_tree; -// args -boost::property_tree::ptree config; -filesystem::path traffic_tile_dir; -unsigned int num_threads; -bool summary = false; - namespace { // Struct to hold stats information during each threads work @@ -242,81 +233,50 @@ void update_tiles( } // anonymous namespace -bool ParseArguments(int argc, char* argv[]) { +int main(int argc, char** argv) { + const auto program = filesystem::path(__FILE__).stem().string(); + // args + filesystem::path traffic_tile_dir; + bool summary = false; + boost::property_tree::ptree config; + try { // clang-format off cxxopts::Options options( - "valhalla_add_predicted_traffic", - "valhalla_add_predicted_traffic " VALHALLA_VERSION "\n\n" + program, + program + " " + VALHALLA_VERSION + "\n\n" "adds predicted traffic to valhalla tiles.\n"); options.add_options() ("h,help", "Print this help message.") ("v,version", "Print the version of this software.") - ("j,concurrency", "Number of threads to use.", cxxopts::value(num_threads)->default_value(std::to_string(std::thread::hardware_concurrency()))) + ("j,concurrency", "Number of threads to use.", cxxopts::value()) ("c,config", "Path to the json configuration file.", cxxopts::value()) ("i,inline-config", "Inline json config.", cxxopts::value()) ("s,summary", "Output summary information about traffic coverage for the tile set", cxxopts::value(summary)) - ("t,traffic_tile_dir", "positional argument", cxxopts::value()); + ("t,traffic-tile-dir", "positional argument", cxxopts::value()); // clang-format on options.parse_positional({"traffic-tile-dir"}); options.positional_help("Traffic tile dir"); auto result = options.parse(argc, argv); + if (!parse_common_args(program, options, result, config, "mjolnir.logging", true)) + return EXIT_SUCCESS; - if (result.count("help")) { - std::cout << options.help() << "\n"; - exit(0); - } - - if (result.count("version")) { - std::cout << "valhalla_add_predicted_traffic " << VALHALLA_VERSION << "\n"; - exit(0); - } - - if (!result.count("traffic_tile_dir")) { + if (!result.count("traffic-tile-dir")) { std::cout << "You must provide a tile directory to read the csv tiles from.\n"; return false; } - traffic_tile_dir = filesystem::path(result["traffic_tile_dir"].as()); - - // Read the config file - if (result.count("inline-config")) { - std::stringstream ss; - ss << result["inline-config"].as(); - rapidjson::read_json(ss, config); - } else if (result.count("config") && - filesystem::is_regular_file(result["config"].as())) { - rapidjson::read_json(result["config"].as(), config); - } else { - std::cerr << "Configuration is required\n\n" << options.help() << "\n\n"; - return false; - } - - return true; - } catch (cxxopts::OptionException& e) { + traffic_tile_dir = filesystem::path(result["traffic-tile-dir"].as()); + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { std::cerr << "Unable to parse command line options because: " << e.what() << "\n" << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; - return false; - } - - return true; -} - -int main(int argc, char** argv) { - if (!ParseArguments(argc, argv)) { return EXIT_FAILURE; } - // configure logging - auto logging_subtree = config.get_child_optional("mjolnir.logging"); - if (logging_subtree) { - auto logging_config = - valhalla::midgard::ToMap>(logging_subtree.get()); - valhalla::midgard::logging::Configure(logging_config); - } - // queue up all the work we'll be doing std::unordered_map> files_per_tile; for (filesystem::recursive_directory_iterator i(traffic_tile_dir), end; i != end; ++i) { @@ -337,10 +297,7 @@ int main(int argc, char** argv) { std::random_device rd; std::shuffle(traffic_tiles.begin(), traffic_tiles.end(), std::mt19937(rd())); - LOG_INFO("Adding predicted traffic with " + std::to_string(num_threads) + " threads"); - std::vector> threads(num_threads); - - std::cout << traffic_tile_dir << std::endl; + std::vector> threads(config.get("mjolnir.concurrency")); LOG_INFO("Parsing speeds from " + std::to_string(traffic_tiles.size()) + " tiles."); size_t floor = traffic_tiles.size() / threads.size(); @@ -391,6 +348,9 @@ int main(int argc, char** argv) { if (!summary) return EXIT_SUCCESS; + // don't use the .tar for stats + config.get_child("mjolnir").erase("tile_extract"); + GraphReader reader(config.get_child("mjolnir")); // Iterate through the tiles int shortcuts_with_speed = 0; @@ -450,9 +410,9 @@ int main(int argc, char** argv) { } } LOG_INFO("Stats - excluding shortcut edges"); - LOG_INFO("non driveable with speed = " + std::to_string(non_dr_with_speed)); + LOG_INFO("non drivable with speed = " + std::to_string(non_dr_with_speed)); LOG_INFO("Shortcuts with speed = " + std::to_string(shortcuts_with_speed)); - uint32_t totaldriveable = 0, totalpt = 0, totalff = 0, totaldriveablelink = 0; + uint32_t totaldrivable = 0, totalpt = 0, totalff = 0, totaldrivablelink = 0; for (uint32_t i = 0; i < 8; i++) { float pct1 = 100.0f * (float)pred_road_class_edges[i] / dr_road_class_edges[i]; float pct2 = 100.0f * (float)ff_road_class_edges[i] / dr_road_class_edges[i]; @@ -460,19 +420,18 @@ int main(int argc, char** argv) { std::stringstream ss_pct1, ss_pct2; ss_pct1 << std::setprecision(1) << std::fixed << pct1; ss_pct2 << std::setprecision(1) << std::fixed << pct2; - LOG_INFO("RC " + std::to_string(i) + ": driveable edges " + + LOG_INFO("RC " + std::to_string(i) + ": drivable edges " + std::to_string(dr_road_class_edges[i]) + " predtraffic " + std::to_string(pred_road_class_edges[i]) + " pct " + ss_pct1.str() + " ff " + std::to_string(ff_road_class_edges[i]) + " pct " + ss_pct2.str()); - totaldriveable += dr_road_class_edges[i]; - totaldriveablelink += dr_class_edges_links[i]; + totaldrivable += dr_road_class_edges[i]; + totaldrivablelink += dr_class_edges_links[i]; totalpt += pred_road_class_edges[i]; totalff += ff_road_class_edges[i]; } - LOG_INFO("total driveable = " + std::to_string(totaldriveable) + - " total driveable ramps/links = " + std::to_string(totaldriveablelink)); - LOG_INFO("total driveable non ramps/links = " + - std::to_string(totaldriveable - totaldriveablelink)); + LOG_INFO("total drivable = " + std::to_string(totaldrivable) + + " total drivable ramps/links = " + std::to_string(totaldrivablelink)); + LOG_INFO("total drivable non ramps/links = " + std::to_string(totaldrivable - totaldrivablelink)); LOG_INFO("total pred " + std::to_string(totalpt)); LOG_INFO("total ff " + std::to_string(totalff)); diff --git a/src/mjolnir/valhalla_assign_speeds.cc b/src/mjolnir/valhalla_assign_speeds.cc index fa1c3941d8..15971137c4 100644 --- a/src/mjolnir/valhalla_assign_speeds.cc +++ b/src/mjolnir/valhalla_assign_speeds.cc @@ -1,3 +1,4 @@ +#include "argparse_utils.h" #include "baldr/graphreader.h" #include "baldr/rapidjson_utils.h" #include "config.h" @@ -11,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -76,71 +78,40 @@ void assign(const boost::property_tree::ptree& config, } int main(int argc, char** argv) { + const auto program = filesystem::path(__FILE__).stem().string(); // args - filesystem::path config_file_path; bpt::ptree config; try { // clang-format off cxxopts::Options options( - "valhalla_assign_speeds", - "valhalla_assign_speeds " VALHALLA_VERSION "\n\n" + program, + program + " " + VALHALLA_VERSION + "\n\n" "Modifies default speeds based on provided configuration.\n"); options.add_options() ("h,help", "Print this help message.") ("v,version", "Print the version of this software.") ("c,config", "Path to the json configuration file.", cxxopts::value()) - ("i,inline-config", "Inline JSON config", cxxopts::value()); + ("i,inline-config", "Inline JSON config", cxxopts::value()) + ("j,concurrency", "Number of threads to use. Defaults to all threads.", cxxopts::value()); // clang-format on auto result = options.parse(argc, argv); - - if (result.count("version")) { - std::cout << "pbfadminbuilder " << VALHALLA_VERSION << "\n"; + if (!parse_common_args(program, options, result, config, "mjolnir.logging", true)) return EXIT_SUCCESS; - } - - if (result.count("help")) { - std::cout << options.help() << "\n"; - return EXIT_SUCCESS; - } - - // Read the config file - if (result.count("inline-config")) { - std::stringstream ss; - ss << result["inline-config"].as(); - rapidjson::read_json(ss, config); - } else if (result.count("config") && - filesystem::is_regular_file( - config_file_path = filesystem::path(result["config"].as()))) { - rapidjson::read_json(config_file_path.string(), config); - } else { - std::cerr << "Configuration is required\n" << options.help() << std::endl; - return EXIT_FAILURE; - } - - if (!result.count("config")) { - std::cout << "You must provide a config for loading and modifying tiles.\n"; - return EXIT_FAILURE; - } - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { std::cerr << "Unable to parse command line options because: " << e.what() << "\n" << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; + return EXIT_FAILURE; } - // configure logging - rapidjson::read_json(config_file_path.string(), config); config.get_child("mjolnir").erase("tile_extract"); config.get_child("mjolnir").erase("tile_url"); config.get_child("mjolnir").erase("traffic_extract"); - auto logging_subtree = config.get_child_optional("mjolnir.logging"); - if (logging_subtree) { - auto logging_config = - valhalla::midgard::ToMap>(logging_subtree.get()); - valhalla::midgard::logging::Configure(logging_config); - } // queue some tiles up to modify std::deque tilequeue; @@ -152,10 +123,7 @@ int main(int argc, char** argv) { std::shuffle(tilequeue.begin(), tilequeue.end(), std::mt19937(3)); // spawn threads to modify the tiles - auto concurrency = - std::max(static_cast(1), - config.get("mjolnir.concurrency", std::thread::hardware_concurrency())); - std::vector> threads(concurrency); + std::vector> threads(config.get("mjolnir.concurrency")); std::list>> results; std::mutex lock; for (auto& thread : threads) { diff --git a/src/mjolnir/valhalla_benchmark_admins.cc b/src/mjolnir/valhalla_benchmark_admins.cc index 76ac3e08c9..ea9b13cf87 100644 --- a/src/mjolnir/valhalla_benchmark_admins.cc +++ b/src/mjolnir/valhalla_benchmark_admins.cc @@ -1,14 +1,6 @@ #include #include -#include -#include -#include -#include -#include -#include -#include #include -#include #include #include @@ -26,7 +18,6 @@ #include "baldr/graphtile.h" #include "baldr/rapidjson_utils.h" #include "baldr/tilehierarchy.h" -#include "config.h" #include "filesystem.h" #include "midgard/aabb2.h" #include "midgard/constants.h" @@ -36,6 +27,8 @@ #include "midgard/util.h" #include "mjolnir/util.h" +#include "argparse_utils.h" + using namespace valhalla::midgard; using namespace valhalla::baldr; @@ -195,67 +188,45 @@ void Benchmark(const boost::property_tree::ptree& pt) { sqlite3_close(db_handle); } -bool ParseArguments(int argc, char* argv[]) { +int main(int argc, char** argv) { + const auto program = filesystem::path(__FILE__).stem().string(); + // args std::vector input_files; + boost::property_tree::ptree config; try { // clang-format off - cxxopts::Options options("valhalla_benchmark_admins", - "valhalla_benchmark_admins " VALHALLA_VERSION "\n\n" + cxxopts::Options options( + program, + program + " " + VALHALLA_VERSION + "\n\n" "valhalla_benchmark_admins is a program to time the admin queries\n"); options.add_options() ("h,help", "Print this help message.") ("v,version", "Print the version of this software.") - ("c,config", "Path to the json configuration file.", cxxopts::value()); + ("c,config", "Path to the json configuration file.", cxxopts::value()) + ("i,inline-config", "Inline JSON config", cxxopts::value()); // clang-format on auto result = options.parse(argc, argv); + if (!parse_common_args(program, options, result, config, "mjolnir.logging")) + return EXIT_SUCCESS; if (result.count("version")) { std::cout << "valhalla_benchmark_admins " << VALHALLA_VERSION << "\n"; return EXIT_SUCCESS; } - - if (result.count("help")) { - std::cout << options.help() << "\n"; - return EXIT_SUCCESS; - } - - if (result.count("config") && - filesystem::is_regular_file(config_file_path = - filesystem::path(result["config"].as()))) { - return true; - } else { - std::cerr << "Configuration file is required\n\n" << options.help() << "\n\n"; - } - } catch (const cxxopts::OptionException& e) { - std::cout << "Unable to parse command line options because: " << e.what() << std::endl; - } - - return false; -} - -int main(int argc, char** argv) { - if (!ParseArguments(argc, argv)) { + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; return EXIT_FAILURE; - } - - // Ccheck what type of input we are getting - boost::property_tree::ptree pt; - rapidjson::read_json(config_file_path.string(), pt); - - // Configure logging - auto logging_subtree = pt.get_child_optional("mjolnir.logging"); - if (logging_subtree) { - auto logging_config = - valhalla::midgard::ToMap>(logging_subtree.get()); - valhalla::midgard::logging::Configure(logging_config); } auto t1 = std::chrono::high_resolution_clock::now(); - Benchmark(pt.get_child("mjolnir")); + Benchmark(config.get_child("mjolnir")); auto t2 = std::chrono::high_resolution_clock::now(); uint32_t msecs = std::chrono::duration_cast(t2 - t1).count(); float secs = msecs * 0.001f; diff --git a/src/mjolnir/valhalla_build_admins.cc b/src/mjolnir/valhalla_build_admins.cc index 30ae635e80..4376339370 100644 --- a/src/mjolnir/valhalla_build_admins.cc +++ b/src/mjolnir/valhalla_build_admins.cc @@ -3,21 +3,23 @@ #include #include "baldr/rapidjson_utils.h" -#include "config.h" #include "midgard/logging.h" #include "midgard/util.h" #include "mjolnir/adminbuilder.h" -filesystem::path config_file_path; -std::vector input_files; -boost::property_tree::ptree pt; +#include "argparse_utils.h" + +int main(int argc, char** argv) { + const auto program = filesystem::path(__FILE__).stem().string(); + // args + std::vector input_files; + boost::property_tree::ptree config; -bool ParseArguments(int argc, char* argv[]) { try { // clang-format off cxxopts::Options options( - "valhalla_build_admins", - "valhalla_build_admins " VALHALLA_VERSION "\n\n" + program, + program + " " + VALHALLA_VERSION + "\n\n" "valhalla_build_admins is a program that creates a administrative SQLite database from \n" "one or multiple osm.pbf files. The admin db is used during graph building to enrich \n" "nodes and edges." @@ -34,64 +36,23 @@ bool ParseArguments(int argc, char* argv[]) { options.parse_positional({"input_files"}); options.positional_help("OSM PBF file(s)"); auto result = options.parse(argc, argv); - - if (result.count("version")) { - std::cout << "pbfadminbuilder " << VALHALLA_VERSION << "\n"; - exit(0); - } - - if (result.count("help")) { - std::cout << options.help() << "\n"; - exit(0); - } + if (!parse_common_args(program, options, result, config, "mjolnir.logging")) + return EXIT_SUCCESS; // input files are positional if (!result.count("input_files")) { - std::cerr << "Input file is required\n" << std::endl; - return false; - } - - if (result.count("inline-config")) { - std::stringstream ss; - ss << result["inline-config"].as(); - rapidjson::read_json(ss, pt); - return true; - } else if (result.count("config") && - filesystem::is_regular_file( - config_file_path = filesystem::path(result["config"].as()))) { - rapidjson::read_json(config_file_path.string(), pt); - return true; - } else { - std::cerr << "Configuration is required\n" << options.help() << std::endl; - return false; + throw cxxopts::exceptions::exception("Input file is required\n\n" + options.help()); } - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { std::cerr << "Unable to parse command line options because: " << e.what() << "\n" << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; - } - - return false; -} - -int main(int argc, char** argv) { - - if (!ParseArguments(argc, argv)) { return EXIT_FAILURE; } - // check what type of input we are getting - rapidjson::read_json(config_file_path.string(), pt); - - // configure logging - auto logging_subtree = pt.get_child_optional("mjolnir.logging"); - if (logging_subtree) { - auto logging_config = - valhalla::midgard::ToMap>(logging_subtree.get()); - valhalla::midgard::logging::Configure(logging_config); - } - - if (!valhalla::mjolnir::BuildAdminFromPBF(pt.get_child("mjolnir"), input_files)) { + if (!valhalla::mjolnir::BuildAdminFromPBF(config.get_child("mjolnir"), input_files)) { return EXIT_FAILURE; }; diff --git a/src/mjolnir/valhalla_build_connectivity.cc b/src/mjolnir/valhalla_build_connectivity.cc index 63b1366af3..7089e42b94 100644 --- a/src/mjolnir/valhalla_build_connectivity.cc +++ b/src/mjolnir/valhalla_build_connectivity.cc @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -12,62 +11,20 @@ #include "baldr/connectivity_map.h" #include "baldr/rapidjson_utils.h" #include "baldr/tilehierarchy.h" -#include "config.h" #include "filesystem.h" +#include "argparse_utils.h" + using namespace valhalla::baldr; using namespace valhalla::midgard; -filesystem::path config_file_path; - struct PPMObject { std::string magic_num; int32_t width, height, maxColVal; char* m_image; }; -bool ParseArguments(int argc, char* argv[]) { - try { - // clang-format off - cxxopts::Options options( - "valhalla_build_connectivity", - "valhalla_build_connectivity " VALHALLA_VERSION "\n\n" - "valhalla_build_connectivity is a program that creates a PPM image file representing\n" - "the connectivity between tiles.\n\n"); - - options.add_options() - ("h,help", "Print this help message.") - ("v,version", "Print the version of this software.") - ("c,config", "Path to the json configuration file.", cxxopts::value()); - // clang-format on - - auto result = options.parse(argc, argv); - - if (result.count("version")) { - std::cout << "valhalla_build_connectivity " << VALHALLA_VERSION << "\n"; - return EXIT_SUCCESS; - } - - if (result.count("help")) { - std::cout << options.help() << "\n"; - return EXIT_SUCCESS; - } - - if (result.count("config") && - filesystem::is_regular_file(config_file_path = - filesystem::path(result["config"].as()))) { - return true; - } else { - std::cerr << "Configuration file is required\n\n" << options.help() << "\n\n"; - } - } catch (const cxxopts::OptionException& e) { - std::cout << "Unable to parse command line options because: " << e.what() << std::endl; - } - - return false; -} - struct RGB { uint8_t red; uint8_t green; @@ -86,17 +43,39 @@ struct RGB { // Main application to create a ppm image file of connectivity. int main(int argc, char** argv) { - // Parse command line arguments - if (!ParseArguments(argc, argv)) { + const auto program = filesystem::path(__FILE__).stem().string(); + // args + boost::property_tree::ptree config; + + try { + // clang-format off + cxxopts::Options options( + program, + program + " " + VALHALLA_VERSION + "\n\n" + "valhalla_build_connectivity is a program that creates a PPM image file representing\n" + "the connectivity between tiles.\n\n"); + + options.add_options() + ("h,help", "Print this help message.") + ("v,version", "Print the version of this software.") + ("c,config", "Path to the json configuration file.", cxxopts::value()) + ("i,inline-config", "Inline JSON config", cxxopts::value()); + // clang-format on + + auto result = options.parse(argc, argv); + if (!parse_common_args(program, options, result, config, "mjolnir.logging")) + return EXIT_SUCCESS; + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; return EXIT_FAILURE; } - // Get the config to see which coverage we are using - boost::property_tree::ptree pt; - rapidjson::read_json(config_file_path.string(), pt); - // Get something we can use to fetch tiles - valhalla::baldr::connectivity_map_t connectivity_map(pt.get_child("mjolnir")); + valhalla::baldr::connectivity_map_t connectivity_map(config.get_child("mjolnir")); uint32_t transit_level = TileHierarchy::levels().back().level + 1; for (uint32_t level = 0; level <= transit_level; level++) { diff --git a/src/mjolnir/valhalla_build_landmarks.cc b/src/mjolnir/valhalla_build_landmarks.cc new file mode 100644 index 0000000000..5665146189 --- /dev/null +++ b/src/mjolnir/valhalla_build_landmarks.cc @@ -0,0 +1,55 @@ +#include "argparse_utils.h" +#include "baldr/rapidjson_utils.h" +#include "filesystem.h" +#include "midgard/logging.h" +#include "mjolnir/landmarks.h" +#include + +int main(int argc, char** argv) { + const auto program = filesystem::path(__FILE__).stem().string(); + // args + std::vector input_files; + boost::property_tree::ptree config; + + try { + // clang-format off + cxxopts::Options options( + program, + program + " " + VALHALLA_VERSION + "\n\n" + "valhalla_build_landmarks is a program that builds a SQLite database to store POI as landmarks from \n" + "one or multiple osm.pbf files. The landmark db is used during graph building to facilitate navigation." + "\n\n"); + + options.add_options() + ("h,help", "Print this help message.") + ("v,version", "Print the version of this software.") + ("c,config", "Path to the json configuration file.", cxxopts::value()) + ("i,inline-config", "Inline JSON config", cxxopts::value()) + ("input_files", "positional arguments", cxxopts::value>(input_files)); + // clang-format on + + options.parse_positional({"input_files"}); + options.positional_help("OSM PBF file(s)"); + auto result = options.parse(argc, argv); + if (!parse_common_args(program, options, result, config, "mjolnir.logging")) + return EXIT_SUCCESS; + + // input files are positional + if (!result.count("input_files")) { + throw cxxopts::exceptions::exception("Input file is required\n\n" + options.help()); + } + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; + return EXIT_FAILURE; + } + + if (!valhalla::mjolnir::BuildLandmarkFromPBF(config.get_child("mjolnir"), input_files)) { + return EXIT_FAILURE; + }; + + return EXIT_SUCCESS; +} diff --git a/src/mjolnir/valhalla_build_statistics.cc b/src/mjolnir/valhalla_build_statistics.cc index 7f34526572..da32850049 100644 --- a/src/mjolnir/valhalla_build_statistics.cc +++ b/src/mjolnir/valhalla_build_statistics.cc @@ -3,23 +3,18 @@ #include "statistics.h" #include "baldr/rapidjson_utils.h" -#include #include #include #include #include #include #include -#include -#include -#include +#include #include #include #include #include -#include "config.h" - #include "baldr/graphconstants.h" #include "baldr/graphid.h" #include "baldr/graphreader.h" @@ -31,12 +26,12 @@ #include "midgard/logging.h" #include "midgard/pointll.h" +#include "argparse_utils.h" + using namespace valhalla::midgard; using namespace valhalla::baldr; using namespace valhalla::mjolnir; -filesystem::path config_file_path; - namespace { struct HGVRestrictionTypes { @@ -571,64 +566,39 @@ void BuildStatistics(const boost::property_tree::ptree& pt) { stats.roulette_data.GenerateTasks(pt); } -bool ParseArguments(int argc, char* argv[]) { +int main(int argc, char** argv) { + const auto program = filesystem::path(__FILE__).stem().string(); + // args + boost::property_tree::ptree config; + try { // clang-format off - cxxopts::Options options("valhalla_build_statistics", - "valhalla_build_statistics " VALHALLA_VERSION "\n\n" - "valhalla_build_statistics is a program that builds a statistics database.\n\n"); + cxxopts::Options options( + program, + program + " " + VALHALLA_VERSION + "\n\n" + "valhalla_build_statistics is a program that builds a statistics database.\n\n"); options.add_options() ("h,help", "Print this help message") ("v,version", "Print the version of this software.") - ("c,config", "Path to the json configuration file.", cxxopts::value()); + ("c,config", "Path to the json configuration file.", cxxopts::value()) + ("i,inline-config", "Inline JSON config", cxxopts::value()) + ("j,concurrency", "Number of threads to use. Defaults to all threads.", cxxopts::value()); // clang-format on auto result = options.parse(argc, argv); - - if (result.count("help")) { - std::cout << options.help() << "\n"; - exit(0); - } - - if (result.count("version")) { - std::cout << "valhalla_build_statistics " << VALHALLA_VERSION << "\n"; - exit(0); - } - - if (result.count("config") && - filesystem::is_regular_file(config_file_path = - filesystem::path(result["config"].as()))) { - return true; - } else { - std::cerr << "Configuration file is required\n\n" << options.help() << "\n\n"; - } - } catch (const cxxopts::OptionException& e) { - std::cout << "Unable to parse command line options because: " << e.what() << std::endl; - } - - return false; -} - -int main(int argc, char** argv) { - if (!ParseArguments(argc, argv)) { + if (!parse_common_args(program, options, result, config, "mjolnir.logging", true)) + return EXIT_SUCCESS; + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; return EXIT_FAILURE; } - // check the type of input - boost::property_tree::ptree pt; - rapidjson::read_json(config_file_path.string(), pt); - - // configure logging - auto logging_subtree = pt.get_child_optional("mjolnir.logging"); - if (logging_subtree) { - auto loggin_config = - valhalla::midgard::ToMap>(logging_subtree.get()); - valhalla::midgard::logging::Configure(loggin_config); - } - - BuildStatistics(pt); + BuildStatistics(config); return EXIT_SUCCESS; } diff --git a/src/mjolnir/valhalla_build_tiles.cc b/src/mjolnir/valhalla_build_tiles.cc index 6811191911..3269cdf05c 100644 --- a/src/mjolnir/valhalla_build_tiles.cc +++ b/src/mjolnir/valhalla_build_tiles.cc @@ -6,12 +6,13 @@ #include #include "baldr/rapidjson_utils.h" -#include "config.h" #include "filesystem.h" #include "midgard/logging.h" #include "midgard/util.h" #include "mjolnir/util.h" +#include "argparse_utils.h" + using namespace valhalla::mjolnir; // List the build stages @@ -24,21 +25,22 @@ void list_stages() { } int main(int argc, char** argv) { + const auto program = filesystem::path(__FILE__).stem().string(); // args - filesystem::path config_file_path; std::vector input_files; BuildStage start_stage = BuildStage::kInitialize; BuildStage end_stage = BuildStage::kCleanup; - boost::property_tree::ptree pt; + boost::property_tree::ptree config; try { // ref: // https://github.com/jarro2783/cxxopts/blob/302302b30839505703d37fb82f536c53cf9172fa/src/example.cpp cxxopts::Options options( - "valhalla_build_tiles", - "valhalla_build_tiles " VALHALLA_VERSION - "\n\nvalhalla_build_tiles is a program that creates the route graph\nfrom one or multiple osm.pbf extract(s)\n"); + program, + program + " " + VALHALLA_VERSION + + "\n\n" + "a program that creates the route graph\nfrom one or multiple osm.pbf extract(s)\n"); // clang-format off options.add_options() @@ -48,86 +50,55 @@ int main(int argc, char** argv) { ("i,inline-config", "Inline JSON config", cxxopts::value()) ("s,start", "Starting stage of the build pipeline", cxxopts::value()->default_value("initialize")) ("e,end", "End stage of the build pipeline", cxxopts::value()->default_value("cleanup")) - ("input_files", "positional arguments", cxxopts::value>(input_files)); + ("input_files", "positional arguments", cxxopts::value>(input_files)) + ("j,concurrency", "Number of threads to use. Defaults to all threads.", cxxopts::value()); // clang-format on options.parse_positional({"input_files"}); options.positional_help("OSM PBF file(s)"); auto result = options.parse(argc, argv); - - if (result.count("version")) { - std::cout << "valhalla_build_tiles " << VALHALLA_VERSION << "\n"; + if (!parse_common_args(program, options, result, config, "mjolnir.logging", true, &list_stages)) return EXIT_SUCCESS; - } - - if (result.count("help")) { - std::cout << options.help() << "\n"; - list_stages(); - return EXIT_SUCCESS; - } - - // Read the config file - if (result.count("inline-config")) { - std::stringstream ss; - ss << result["inline-config"].as(); - rapidjson::read_json(ss, pt); - } else if (result.count("config") && - filesystem::is_regular_file( - config_file_path = filesystem::path(result["config"].as()))) { - rapidjson::read_json(config_file_path.string(), pt); - } else { - std::cerr << "Configuration is required\n\n" << options.help() << "\n\n"; - return EXIT_FAILURE; - } - - // configure logging - auto logging_subtree = pt.get_child_optional("mjolnir.logging"); - if (logging_subtree) { - auto subtree = logging_subtree.get(); - auto logging_config = - valhalla::midgard::ToMap>(subtree); - valhalla::midgard::logging::Configure(logging_config); - } // Convert stage strings to BuildStage if (result.count("start")) { start_stage = string_to_buildstage(result["start"].as()); if (start_stage == BuildStage::kInvalid) { - std::cerr << "Invalid start stage" << std::endl; list_stages(); - return EXIT_FAILURE; + throw cxxopts::exceptions::exception("Invalid start stage, see above"); } } if (result.count("end")) { end_stage = string_to_buildstage(result["end"].as()); if (end_stage == BuildStage::kInvalid) { - std::cerr << "Invalid end stage" << std::endl; list_stages(); - return EXIT_FAILURE; + throw cxxopts::exceptions::exception("Invalid end stage, see above"); } } LOG_INFO("Start stage = " + to_string(start_stage) + " End stage = " + to_string(end_stage)); // Make sure start stage < end stage if (static_cast(start_stage) > static_cast(end_stage)) { - std::cerr << "Starting build stage is after ending build stage in pipeline. " - << " Please revise options!" << std::endl; list_stages(); - return EXIT_FAILURE; + throw cxxopts::exceptions::exception( + "Starting build stage is after ending build stage in pipeline, see above"); } if (!result.count("input_files") && start_stage <= BuildStage::kParseNodes && end_stage >= BuildStage::kParseWays) { - std::cerr << "Input file is required\n\n" << options.help() << "\n\n"; - return EXIT_FAILURE; + throw cxxopts::exceptions::exception("Input file is required\n\n" + options.help() + "\n\n"); } - } catch (const cxxopts::OptionException& e) { - std::cout << "Unable to parse command line options because: " << e.what() << std::endl; + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; + return EXIT_FAILURE; } // Build some tiles! - if (build_tile_set(pt, input_files, start_stage, end_stage)) { + if (build_tile_set(config, input_files, start_stage, end_stage)) { return EXIT_SUCCESS; } else { return EXIT_FAILURE; diff --git a/src/mjolnir/valhalla_convert_transit.cc b/src/mjolnir/valhalla_convert_transit.cc index 425a30d06d..f9011cbb69 100644 --- a/src/mjolnir/valhalla_convert_transit.cc +++ b/src/mjolnir/valhalla_convert_transit.cc @@ -1,28 +1,33 @@ +#include + #include "baldr/rapidjson_utils.h" #include "filesystem.h" #include "mjolnir/convert_transit.h" #include "mjolnir/validatetransit.h" -#include "config.h" -#include +#include "argparse_utils.h" -filesystem::path config_file_path; -boost::property_tree::ptree pt; -std::vector onestoptests; +int main(int argc, char** argv) { + const auto program = filesystem::path(__FILE__).stem().string(); + // args + boost::property_tree::ptree pt; + std::vector onestoptests; + boost::property_tree::ptree config; -bool ParseArguments(int argc, char* argv[]) { try { // clang-format off cxxopts::Options options( - "valhalla_convert_transit", - "valhalla_convert_transit " VALHALLA_VERSION "\n\n" - "valhalla_convert_transit is a program that reads protobuf files and creates Level 3 Transit Tiles." + program, + program + " " + VALHALLA_VERSION + "\n\n" + "a program that reads protobuf files and creates Level 3 Transit Tiles." "\n\n"); options.add_options() ("h,help", "Print this help message.") ("v,version", "Print the version of this software.") + ("i,inline-config", "Inline JSON config", cxxopts::value()) ("c,config", "Path to the json configuration file.", cxxopts::value()) + ("j,concurrency", "Number of threads to use. Defaults to all threads.", cxxopts::value()) ("target_directory", "Path to write transit tiles", cxxopts::value()) ("test_file", "file where tests are written", cxxopts::value()); @@ -30,29 +35,12 @@ bool ParseArguments(int argc, char* argv[]) { // clang-format on auto result = options.parse(argc, argv); - - if (result.count("version")) { - std::cout << "convert_transit " << VALHALLA_VERSION << "\n"; - exit(0); - } - - if (result.count("help")) { - std::cout << options.help() << "\n"; - exit(0); - } - - if (result.count("config") && - filesystem::is_regular_file(config_file_path = - filesystem::path(result["config"].as()))) { - rapidjson::read_json(config_file_path.string(), pt); - return true; - } else { - std::cerr << "Configuration file is required\n" << options.help() << "\n\n"; - } + if (!parse_common_args(program, options, result, config, "mjolnir.logging", true)) + return EXIT_SUCCESS; if (result.count("target_directory")) { - pt.get_child("mjolnir").erase("transit_dir"); - pt.add("mjolnir.transit_dir", result["target_directory"].as()); + config.get_child("mjolnir").erase("transit_dir"); + config.add("mjolnir.transit_dir", result["target_directory"].as()); } if (result.count("test_file")) { @@ -62,32 +50,16 @@ bool ParseArguments(int argc, char* argv[]) { onestoptests = valhalla::mjolnir::ParseTestFile(testfile); std::sort(onestoptests.begin(), onestoptests.end()); } - - if (result.count("target_directory")) { - pt.get_child("mjolnir").erase("tile_dir"); - pt.add("mjolnir.tile_dir", result["target_directory"].as()); - } - - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { std::cerr << "Unable to parse command line options because: " << e.what() << "\n" << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; - - std::cerr << "Usage: " << std::string(argv[0]) - << " valhalla_config [target_directory] [test_file]" << std::endl; - std::cerr << "Sample: " << std::string(argv[0]) << " conf/valhalla.json ./transit_tiles" - << std::endl; - } - - return EXIT_FAILURE; -} - -int main(int argc, char** argv) { - if (!ParseArguments(argc, argv)) { return EXIT_FAILURE; } - LOG_INFO("Building transit network."); - auto all_tiles = valhalla::mjolnir::convert_transit(pt); - valhalla::mjolnir::ValidateTransit::Validate(pt, all_tiles, onestoptests); + auto all_tiles = valhalla::mjolnir::convert_transit(config); + valhalla::mjolnir::ValidateTransit::Validate(config, all_tiles, onestoptests); return 0; } diff --git a/src/mjolnir/valhalla_ingest_transit.cc b/src/mjolnir/valhalla_ingest_transit.cc index 16f3bbef57..9180d25df0 100644 --- a/src/mjolnir/valhalla_ingest_transit.cc +++ b/src/mjolnir/valhalla_ingest_transit.cc @@ -1,69 +1,53 @@ +#include + #include "baldr/rapidjson_utils.h" #include "mjolnir/ingest_transit.h" -#include "config.h" -#include +#include "argparse_utils.h" -filesystem::path config_file_path; +int main(int argc, char** argv) { + const auto program = filesystem::path(__FILE__).stem().string(); + // args + boost::property_tree::ptree config; -bool ParseArguments(int argc, char* argv[]) { try { // clang-format off cxxopts::Options options( - "valhalla_ingest_transit", - "valhalla_ingest_transit " VALHALLA_VERSION "\n\n" - "valhalla_ingest_transit is a program that reads GTFS data. It converts a directory of transit feeds into protobuf tiles." + program, + program + " " + VALHALLA_VERSION + "\n\n" + "a program that reads GTFS data. It converts a directory of transit feeds into protobuf tiles." "\n\n"); options.add_options() ("h,help", "Print this help message.") ("v,version", "Print the version of this software.") - ("c,config", "Path to the json configuration file.", cxxopts::value()); + ("c,config", "Path to the json configuration file.", cxxopts::value()) + ("j,concurrency", "Number of threads to use. Defaults to all threads.", cxxopts::value()); // clang-format on auto result = options.parse(argc, argv); - - if (result.count("version")) { - std::cout << "ingest_transit " << VALHALLA_VERSION << "\n"; - exit(0); - } - - if (result.count("help")) { - std::cout << options.help() << "\n"; - exit(0); - } - - if (result.count("config") && - filesystem::is_regular_file(config_file_path = - filesystem::path(result["config"].as()))) { - return true; - } else { - std::cerr << "Configuration file is required\n" << options.help() << "\n\n"; - } - } catch (cxxopts::OptionException& e) { + if (!parse_common_args(program, options, result, config, "mjolnir.logging", true)) + return EXIT_SUCCESS; + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { std::cerr << "Unable to parse command line options because: " << e.what() << "\n" << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; + return EXIT_FAILURE; } - return EXIT_FAILURE; -} - -int main(int argc, char** argv) { - - if (!ParseArguments(argc, argv)) { + try { + // spawn threads to download all the tiles returning a list of + // tiles that ended up having dangling stop pairs + auto dangling_tiles = valhalla::mjolnir::ingest_transit(config); + + // spawn threads to connect dangling stop pairs to adjacent tiles' stops + valhalla::mjolnir::stitch_transit(config, dangling_tiles); + } catch (const std::runtime_error& e) { + LOG_ERROR(e.what()); return EXIT_FAILURE; } - // args and config file loading - boost::property_tree::ptree pt; - rapidjson::read_json(config_file_path.string(), pt); - - // spawn threads to download all the tiles returning a list of - // tiles that ended up having dangling stop pairs - auto dangling_tiles = valhalla::mjolnir::ingest_transit(pt); - - // spawn threads to connect dangling stop pairs to adjacent tiles' stops - valhalla::mjolnir::stitch_transit(pt, dangling_tiles); - - return 0; + return EXIT_SUCCESS; } diff --git a/src/mjolnir/valhalla_query_transit.cc b/src/mjolnir/valhalla_query_transit.cc index 1ab15219fd..fa3e109f95 100644 --- a/src/mjolnir/valhalla_query_transit.cc +++ b/src/mjolnir/valhalla_query_transit.cc @@ -3,26 +3,24 @@ #include "baldr/rapidjson_utils.h" #include #include -#include #include #include #include #include -#include -#include #include #include #include "baldr/graphreader.h" #include "baldr/tilehierarchy.h" -#include "config.h" #include "filesystem.h" #include "midgard/logging.h" #include "midgard/util.h" #include "mjolnir/servicedays.h" #include "valhalla/proto/transit.pb.h" +#include "argparse_utils.h" + using namespace valhalla::midgard; using namespace valhalla::baldr; using namespace valhalla::mjolnir; @@ -361,70 +359,57 @@ GraphId GetGraphId(Transit& transit, const std::string& onestop_id) { // Main method for testing a single path int main(int argc, char* argv[]) { + const auto program = filesystem::path(__FILE__).stem().string(); // args - std::string config; - float o_lng, o_lat, d_lng, d_lat; + double o_lng, o_lat, d_lng, d_lat; std::string o_onestop_id, d_onestop_id, time; int tripid; + boost::property_tree::ptree config; try { // clang-format off cxxopts::Options options( - "valhalla_query_transit", - "valhalla_query_transit " VALHALLA_VERSION "\n\n" - "valhalla_query_transit is a simple command line test tool to log transit stop info.\n\n"); + program, + program + " " + VALHALLA_VERSION + "\n\n" + "a simple command line test tool to log transit stop info.\n\n"); options.add_options() ("h,help", "Print this help message.") ("v,version", "Print the version of this software.") - ("o_lat", "Origin latitude", cxxopts::value(o_lat)) - ("o_lng", "Origin longitude", cxxopts::value(o_lng)) - ("d_lat", "Destination latitude", cxxopts::value(d_lat)) - ("d_lng", "Destination longitude", cxxopts::value(d_lng)) + ("o_lat", "Origin latitude", cxxopts::value(o_lat)) + ("o_lng", "Origin longitude", cxxopts::value(o_lng)) + ("d_lat", "Destination latitude", cxxopts::value(d_lat)) + ("d_lng", "Destination longitude", cxxopts::value(d_lng)) ("o,o_onestop_id", "Origin OneStop ID", cxxopts::value(o_onestop_id)) ("d,d_onestop_id", "Destination OneStop ID", cxxopts::value(d_onestop_id)) ("i,tripid", "Trip ID", cxxopts::value(tripid)->default_value("0")) ("t,time", "Time", cxxopts::value(time)) - ("c,config", "Config path", cxxopts::value(config)); + ("c,config", "Config path", cxxopts::value()); // clang-format on auto result = options.parse(argc, argv); - - if (result.count("version")) { - std::cout << "valhalla_query_transit " << VALHALLA_VERSION << "\n"; - return EXIT_SUCCESS; - } - - if (result.count("help")) { - std::cout << options.help() << "\n"; + if (!parse_common_args(program, options, result, config, "mjolnir.logging", true)) return EXIT_SUCCESS; - } - - if (!result.count("config") || !filesystem::is_regular_file(filesystem::path(config))) { - std::cerr << "Configuration file is required\n\n" << options.help() << "\n\n"; - return EXIT_FAILURE; - } - for (const auto& arg : std::vector{"o_onestop_id", "o_lat", "o_lng", "conf"}) { + for (const auto& arg : std::vector{"o_onestop_id", "o_lat", "o_lng"}) { if (result.count(arg) == 0) { - std::cerr << "The <" << arg << "> argument was not provided, but is mandatory\n\n"; - std::cerr << options.help() << "\n"; - return EXIT_FAILURE; + const std::string msg = "The <" + arg + "> argument was not provided, but is mandatory\n\n"; + throw cxxopts::exceptions::exception(msg + options.help()); } } - } catch (const cxxopts::OptionException& e) { - std::cout << "Unable to parse command line options because: " << e.what() << std::endl; + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; return EXIT_FAILURE; } - // Read config - boost::property_tree::ptree pt; - rapidjson::read_json(config, pt); - LOG_INFO("Read config"); // Bail if no transit dir - auto transit_dir = pt.get_optional("mjolnir.transit_dir"); + auto transit_dir = config.get_optional("mjolnir.transit_dir"); if (!transit_dir || !filesystem::exists(*transit_dir) || !filesystem::is_directory(*transit_dir)) { LOG_INFO("Transit directory not found."); return 0; diff --git a/src/mjolnir/valhalla_validate_transit.cc b/src/mjolnir/valhalla_validate_transit.cc index 631cd552c1..2e243be7af 100644 --- a/src/mjolnir/valhalla_validate_transit.cc +++ b/src/mjolnir/valhalla_validate_transit.cc @@ -1,4 +1,3 @@ -#include #include #include @@ -7,7 +6,6 @@ #include "baldr/graphid.h" #include "baldr/rapidjson_utils.h" -#include "config.h" #include "filesystem.h" #include "midgard/aabb2.h" #include "midgard/logging.h" @@ -16,71 +14,42 @@ #include "mjolnir/graphbuilder.h" #include "mjolnir/validatetransit.h" +#include "argparse_utils.h" + using namespace valhalla::mjolnir; -filesystem::path config_file_path; +int main(int argc, char** argv) { + const auto program = filesystem::path(__FILE__).stem().string(); + // args + boost::property_tree::ptree config; -bool ParseArguments(int argc, char* argv[]) { try { // clang-format off cxxopts::Options options( - "valhalla_validate_transit", - "valhalla_validate_transit " VALHALLA_VERSION "\n\n" - "valhalla_validate_transit is a program that validates the transit graph and \n" + program, + program + " " + VALHALLA_VERSION + "\n\n" + "a program that validates the transit graph and \n" "schedule at a particular time. It will not use the route tiles at all. It \n" "will only use the transit tiles.\n\n"); options.add_options() ("h,help", "Print this help message.") ("v,version", "Print the version of this software.") - ("c,config", "Path to the json configuration file.", cxxopts::value()); + ("c,config", "Path to the json configuration file.", cxxopts::value()) + ("i,inline-config", "Inline JSON config", cxxopts::value()) + ("j,concurrency", "Number of threads to use. Defaults to all threads.", cxxopts::value()); // clang-format on auto result = options.parse(argc, argv); - - if (result.count("help")) { - std::cout << options.help() << "\n"; - exit(0); - } - - if (result.count("version")) { - std::cout << "valhalla_validate_transit " << VALHALLA_VERSION << "\n"; - exit(0); - } - - if (result.count("config") && - filesystem::is_regular_file(config_file_path = - filesystem::path(result["config"].as()))) { - return true; - } else { - std::cerr << "Configuration file is required\n\n" << options.help() << "\n\n"; - } - - return false; - } catch (const cxxopts::OptionException& e) { - std::cout << "Unable to parse command line options because: " << e.what() << std::endl; - } - - return EXIT_FAILURE; -} - -int main(int argc, char** argv) { - - if (!ParseArguments(argc, argv)) { + if (!parse_common_args(program, options, result, config, "mjolnir.logging", true)) + return EXIT_SUCCESS; + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; return EXIT_FAILURE; - } - - // check what type of input we are getting - boost::property_tree::ptree pt; - rapidjson::read_json(config_file_path.string(), pt); - - // configure logging - auto logging_subtree = pt.get_child_optional("mjolnir.logging"); - if (logging_subtree) { - auto logging_config = - valhalla::midgard::ToMap>(logging_subtree.get()); - valhalla::midgard::logging::Configure(logging_config); } std::string testfile, build_validate; @@ -98,7 +67,7 @@ int main(int argc, char** argv) { std::sort(onestoptests.begin(), onestoptests.end()); // Validate transit std::unordered_set all_tiles; - if (!ValidateTransit::Validate(pt, all_tiles, onestoptests)) { + if (!ValidateTransit::Validate(config, all_tiles, onestoptests)) { return EXIT_FAILURE; } } else if (build_validate == "build") { diff --git a/src/mjolnir/valhalla_ways_to_edges.cc b/src/mjolnir/valhalla_ways_to_edges.cc index 780f92cc5b..0505707fde 100644 --- a/src/mjolnir/valhalla_ways_to_edges.cc +++ b/src/mjolnir/valhalla_ways_to_edges.cc @@ -5,27 +5,23 @@ #include #include -#include "config.h" - -#include "baldr/rapidjson_utils.h" #include #include -#include #include "baldr/directededge.h" #include "baldr/edgeinfo.h" #include "baldr/graphreader.h" #include "baldr/graphtile.h" +#include "baldr/rapidjson_utils.h" #include "baldr/tilehierarchy.h" #include "filesystem.h" #include "midgard/logging.h" +#include "argparse_utils.h" + using namespace valhalla::baldr; using namespace valhalla::midgard; -filesystem::path config_file_path; -boost::property_tree::ptree pt; - // Structure holding an edge Id and forward flag struct EdgeAndDirection { bool forward; @@ -35,13 +31,19 @@ struct EdgeAndDirection { } }; -bool ParseArguments(int argc, char* argv[]) { +// Main application to create a list wayids and directed edges belonging +// to ways that are drivable. +int main(int argc, char** argv) { + const auto program = filesystem::path(__FILE__).stem().string(); + // args + boost::property_tree::ptree config; + try { // clang-format off cxxopts::Options options( - "valhalla_ways_to_edges", - "valhalla_ways_to_edges " VALHALLA_VERSION "\n\n" - "valhalla_ways_to_edges is a program that creates a list of edges for each auto-driveable OSM way.\n\n"); + program, + program + " " + VALHALLA_VERSION + "\n\n" + "a program that creates a list of edges for each auto-drivable OSM way.\n\n"); options.add_options() ("h,help", "Print this help message.") @@ -51,51 +53,22 @@ bool ParseArguments(int argc, char* argv[]) { // clang-format on auto result = options.parse(argc, argv); + if (!parse_common_args(program, options, result, config, "mjolnir.logging")) + return EXIT_SUCCESS; - if (result.count("help")) { - std::cout << options.help() << "\n"; - exit(0); - } - - if (result.count("version")) { - std::cout << "valhalla_ways_to_edges " << VALHALLA_VERSION << "\n"; - exit(0); - } - - // Read the config file - if (result.count("inline-config")) { - std::stringstream ss; - ss << result["inline-config"].as(); - rapidjson::read_json(ss, pt); - return true; - } else if (result.count("config") && - filesystem::is_regular_file( - config_file_path = filesystem::path(result["config"].as()))) { - rapidjson::read_json(config_file_path.string(), pt); - return true; - } else { - std::cerr << "Configuration is required\n" << options.help() << std::endl; - return false; - } - } catch (const cxxopts::OptionException& e) { - std::cout << "Unable to parse command line options because: " << e.what() << std::endl; - } - - return false; -} - -// Main application to create a list wayids and directed edges belonging -// to ways that are driveable. -int main(int argc, char** argv) { - // Parse command line arguments - if (!ParseArguments(argc, argv)) { + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; return EXIT_FAILURE; } // Create an unordered map of OSM ways Ids and their associated graph edges std::unordered_map> ways_edges; - GraphReader reader(pt.get_child("mjolnir")); + GraphReader reader(config.get_child("mjolnir")); // Iterate through all tiles for (auto edge_id : reader.GetTileSet()) { // If tile exists add it to the queue @@ -127,7 +100,7 @@ int main(int argc, char** argv) { } std::ofstream ways_file; - std::string fname = pt.get("mjolnir.tile_dir") + + std::string fname = config.get("mjolnir.tile_dir") + filesystem::path::preferred_separator + "way_edges.txt"; ways_file.open(fname, std::ofstream::out | std::ofstream::trunc); for (const auto& way : ways_edges) { diff --git a/src/mjolnir/validatetransit.cc b/src/mjolnir/validatetransit.cc index 44ab6fd62e..2ee45a41fa 100644 --- a/src/mjolnir/validatetransit.cc +++ b/src/mjolnir/validatetransit.cc @@ -5,7 +5,6 @@ #include "mjolnir/servicedays.h" #include -#include #include #include @@ -260,7 +259,7 @@ void validate(const boost::property_tree::ptree& pt, } } - std::set failures; + std::unordered_set failures; for (auto p = failed_tests.begin(); p != failed_tests.end(); ++p) { bool bfound = false; auto tests = passed_tests.equal_range(p->first); @@ -554,7 +553,7 @@ bool ValidateTransit::Validate(const boost::property_tree::ptree& pt, } auto t2 = std::chrono::high_resolution_clock::now(); - uint32_t secs = std::chrono::duration_cast(t2 - t1).count(); + [[maybe_unused]] uint32_t secs = std::chrono::duration_cast(t2 - t1).count(); LOG_INFO("Finished validating transit network - took " + std::to_string(secs) + " secs"); if (failure_count) { diff --git a/src/odin/CMakeLists.txt b/src/odin/CMakeLists.txt index 6bc04b75de..af779b3e7c 100644 --- a/src/odin/CMakeLists.txt +++ b/src/odin/CMakeLists.txt @@ -1,7 +1,7 @@ file(GLOB locale_jsons ${VALHALLA_SOURCE_DIR}/locales *.json) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/locales.h - COMMAND ${CMAKE_COMMAND} -DMSVC=${MSVC} -P cmake/Binary2Header.cmake ${VALHALLA_SOURCE_DIR}/locales/ ${CMAKE_CURRENT_BINARY_DIR}/locales.h --locales + COMMAND ${CMAKE_COMMAND} -DMSVC=${MSVC} -P ${VALHALLA_SOURCE_DIR}/cmake/ValhallaBin2Header.cmake ${VALHALLA_SOURCE_DIR}/locales/ ${CMAKE_CURRENT_BINARY_DIR}/locales.h --locales WORKING_DIRECTORY ${VALHALLA_SOURCE_DIR} COMMENT "Compiling locales/*.json to locales.h" DEPENDS ${locale_jsons} @@ -28,11 +28,9 @@ set(sources_with_warnings narrativebuilder.cc util.cc) -# treat date library as system -set(system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include) -if(APPLE) - list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) -endif() +set(system_includes + ${date_include_dir} + $<$:${dirent_include_dir}>) valhalla_module(NAME odin SOURCES ${sources} @@ -42,15 +40,15 @@ valhalla_module(NAME odin PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include ${CMAKE_CURRENT_BINARY_DIR} SYSTEM_INCLUDE_DIRECTORIES PUBLIC ${system_includes} + PRIVATE + ${rapidjson_include_dir} DEPENDS valhalla::proto ${valhalla_protobuf_targets} Boost::boost - libprime_server) + ${libprime_server_targets}) diff --git a/src/odin/directionsbuilder.cc b/src/odin/directionsbuilder.cc index 76a08092de..cb6eed7585 100644 --- a/src/odin/directionsbuilder.cc +++ b/src/odin/directionsbuilder.cc @@ -1,8 +1,5 @@ -#include -#include - -#include "midgard/logging.h" #include "odin/directionsbuilder.h" +#include "midgard/logging.h" #include "odin/enhancedtrippath.h" #include "odin/maneuversbuilder.h" #include "odin/markup_formatter.h" @@ -95,9 +92,6 @@ void DirectionsBuilder::PopulateDirectionsLeg(const Options& options, EnhancedTripLeg* etp, std::list& maneuvers, DirectionsLeg& trip_directions) { - bool has_toll = false; - bool has_highway = false; - bool has_ferry = false; // Populate trip and leg IDs trip_directions.set_trip_id(etp->trip_id()); trip_directions.set_leg_id(etp->leg_id()); @@ -147,15 +141,12 @@ void DirectionsBuilder::PopulateDirectionsLeg(const Options& options, trip_maneuver->set_end_shape_index(maneuver.end_shape_index()); if (maneuver.portions_toll()) { trip_maneuver->set_portions_toll(maneuver.portions_toll()); - has_toll = true; } if (maneuver.portions_highway()) { trip_maneuver->set_portions_highway(maneuver.portions_highway()); - has_highway = true; } if (maneuver.ferry()) { trip_maneuver->set_portions_ferry(maneuver.ferry()); - has_ferry = true; } trip_maneuver->set_has_time_restrictions(maneuver.has_time_restrictions()); @@ -391,6 +382,12 @@ void DirectionsBuilder::PopulateDirectionsLeg(const Options& options, auto* trip_bss_info = trip_maneuver->mutable_bss_info(); trip_bss_info->CopyFrom(maneuver.bss_info()); + // set landmarks + for (auto& l : maneuver.landmarks()) { + auto* landmark = trip_maneuver->mutable_landmarks()->Add(); + landmark->CopyFrom(l); + } + // Travel type switch (maneuver.travel_mode()) { case TravelMode::kDrive: { @@ -433,9 +430,9 @@ void DirectionsBuilder::PopulateDirectionsLeg(const Options& options, trip_directions.mutable_summary()->set_has_time_restrictions(has_time_restrictions); // Populate toll, highway, ferry tags - trip_directions.mutable_summary()->set_has_toll(has_toll); - trip_directions.mutable_summary()->set_has_highway(has_highway); - trip_directions.mutable_summary()->set_has_ferry(has_ferry); + trip_directions.mutable_summary()->set_has_toll(etp->summary().has_toll()); + trip_directions.mutable_summary()->set_has_highway(etp->summary().has_highway()); + trip_directions.mutable_summary()->set_has_ferry(etp->summary().has_ferry()); } } // namespace odin diff --git a/src/odin/enhancedtrippath.cc b/src/odin/enhancedtrippath.cc index 0179874e51..d93017c971 100644 --- a/src/odin/enhancedtrippath.cc +++ b/src/odin/enhancedtrippath.cc @@ -1,6 +1,5 @@ #include #include -#include #include "baldr/turn.h" #include "baldr/turnlanes.h" @@ -31,8 +30,8 @@ constexpr uint32_t kBackwardTurnDegreeUpperBound = 236; const std::string& Pronunciation_Alphabet_Name(valhalla::Pronunciation_Alphabet alphabet) { static const std::unordered_map values{{valhalla::Pronunciation_Alphabet::Pronunciation_Alphabet_kIpa, "kIpa"}, - {valhalla::Pronunciation_Alphabet::Pronunciation_Alphabet_kXKatakana, "kXKatakana"}, - {valhalla::Pronunciation_Alphabet::Pronunciation_Alphabet_kXJeita, "kXJeita"}, + {valhalla::Pronunciation_Alphabet::Pronunciation_Alphabet_kKatakana, "kKatakana"}, + {valhalla::Pronunciation_Alphabet::Pronunciation_Alphabet_kJeita, "kJeita"}, {valhalla::Pronunciation_Alphabet::Pronunciation_Alphabet_kNtSampa, "kNtSampa"}}; auto f = values.find(alphabet); if (f == values.cend()) @@ -122,7 +121,7 @@ const std::string& TripLeg_TravelMode_Name(int v) { const std::string& TripLeg_VehicleType_Name(int v) { static const std::unordered_map values{ - {0, "kCar"}, {1, "kMotorcycle"}, {2, "kAutoBus"}, {3, "kTractorTrailer"}, {4, "kMotorScooter"}, + {0, "kCar"}, {1, "kMotorcycle"}, {2, "kAutoBus"}, {3, "kTruck"}, {4, "kMotorScooter"}, }; auto f = values.find(v); if (f == values.cend()) @@ -131,11 +130,9 @@ const std::string& TripLeg_VehicleType_Name(int v) { } const std::string& TripLeg_PedestrianType_Name(int v) { - static const std::unordered_map values{ - {0, "kFoot"}, - {1, "kWheelchair"}, - {2, "kSegway"}, - }; + static const std::unordered_map values{{0, "kFoot"}, + {1, "kWheelchair"}, + {2, "kBlind"}}; auto f = values.find(v); if (f == values.cend()) throw std::runtime_error("Missing value in protobuf enum to string"); @@ -480,7 +477,7 @@ bool EnhancedTripLeg_Edge::IsUnnamedMountainBikeTrail() const { } bool EnhancedTripLeg_Edge::IsHighway() const { - return ((road_class() == RoadClass::kMotorway) && (!IsRampUse())); + return ((road_class() == RoadClass::kMotorway) && (!IsRampUse() && !IsTurnChannelUse())); } bool EnhancedTripLeg_Edge::IsOneway() const { @@ -1373,7 +1370,8 @@ bool EnhancedTripLeg_IntersectingEdge::IsTraversableOutbound(const TravelMode tr } bool EnhancedTripLeg_IntersectingEdge::IsHighway() const { - return ((road_class() == RoadClass::kMotorway) && !(use() == TripLeg_Use_kRampUse)); + return ((road_class() == RoadClass::kMotorway) && + !(use() == TripLeg_Use_kRampUse || use() == TripLeg_Use_kTurnChannelUse)); } std::string EnhancedTripLeg_IntersectingEdge::ToString() const { @@ -1550,7 +1548,7 @@ void EnhancedTripLeg_Node::CalculateRightLeftIntersectingEdgeCounts( } } -bool EnhancedTripLeg_Node::HasFowardIntersectingEdge(uint32_t from_heading) { +bool EnhancedTripLeg_Node::HasForwardIntersectingEdge(uint32_t from_heading) { for (int i = 0; i < intersecting_edge_size(); ++i) { if (is_forward(GetTurnDegree(from_heading, intersecting_edge(i).begin_heading()))) { diff --git a/src/odin/maneuver.cc b/src/odin/maneuver.cc index 1450afc8d0..f42c4a5c34 100644 --- a/src/odin/maneuver.cc +++ b/src/odin/maneuver.cc @@ -1,4 +1,3 @@ -#include #include #include @@ -135,7 +134,8 @@ Maneuver::Maneuver() imminent_verbal_multi_cue_(false), distant_verbal_multi_cue_(false), to_stay_on_(false), drive_on_right_(true), has_time_restrictions_(false), has_right_traversable_outbound_intersecting_edge_(false), - has_left_traversable_outbound_intersecting_edge_(false), + has_left_traversable_outbound_intersecting_edge_(false), has_node_type_(false), + traffic_signal_(false), is_steps_(false), is_bridge_(false), is_tunnel_(false), bss_maneuver_type_(DirectionsLeg_Maneuver_BssManeuverType_kNoneAction), include_verbal_pre_transition_length_(false), contains_obvious_maneuver_(false), roundabout_exit_count_(0), has_combined_enter_exit_roundabout_(false), roundabout_length_(0.0f), @@ -160,6 +160,46 @@ void Maneuver::set_type(const DirectionsLeg_Maneuver_Type& type) { type_ = type; } +// Set the node type. +void Maneuver::set_node_type(const TripLeg_Node_Type type) { + node_type_ = type; + has_node_type_ = true; +} +/** + * Gets the node type. + * @return Returns the node type. + */ +TripLeg_Node_Type Maneuver::node_type() const { + return node_type_; +} +bool Maneuver::has_node_type() const { + return has_node_type_; +} +bool Maneuver::traffic_signal() const { + return traffic_signal_; +} +void Maneuver::set_traffic_signal(bool traffic_signal) { + traffic_signal_ = traffic_signal; +} +bool Maneuver::is_steps() const { + return is_steps_; +} +void Maneuver::set_steps(bool steps) { + is_steps_ = steps; +} +bool Maneuver::is_bridge() const { + return is_bridge_; +} +void Maneuver::set_bridge(bool bridge) { + is_bridge_ = bridge; +} +bool Maneuver::is_tunnel() const { + return is_tunnel_; +} +void Maneuver::set_tunnel(bool tunnel) { + is_tunnel_ = tunnel; +} + bool Maneuver::IsStartType() const { return ((type_ == DirectionsLeg_Maneuver_Type_kStart) || (type_ == DirectionsLeg_Maneuver_Type_kStartLeft) || @@ -1180,8 +1220,16 @@ std::string Maneuver::end_level_ref() const { return end_level_ref_; } -void Maneuver::set_end_level_ref(std::string end_level_ref) { - end_level_ref_ = std::move(end_level_ref); +void Maneuver::set_end_level_ref(const std::string& end_level_ref) { + end_level_ref_ = end_level_ref; +} + +const std::vector& Maneuver::landmarks() const { + return landmarks_; +} + +void Maneuver::set_landmarks(const std::vector& landmarks) { + landmarks_ = landmarks; } #ifdef LOGGING_LEVEL_TRACE diff --git a/src/odin/maneuversbuilder.cc b/src/odin/maneuversbuilder.cc index 87c5776513..4c306d1eb4 100644 --- a/src/odin/maneuversbuilder.cc +++ b/src/odin/maneuversbuilder.cc @@ -1,11 +1,8 @@ #include #include -#include -#include #include #include #include -#include #include #include @@ -143,6 +140,10 @@ std::list ManeuversBuilder::Build() { // activate the correct lanes. ProcessTurnLanes(maneuvers); + // Add landmarks to maneuvers as direction guidance support + // Each maneuver should get the landmarks associated with edges in the previous maneuver + AddLandmarksFromTripLegToManeuvers(maneuvers); + ProcessVerbalSuccinctTransitionInstruction(maneuvers); #ifdef LOGGING_LEVEL_TRACE @@ -236,6 +237,7 @@ std::list ManeuversBuilder::Produce() { // Step through nodes in reverse order to produce maneuvers // excluding the last and first nodes for (int i = (trip_path_->GetLastNodeIndex() - 1); i > 0; --i) { + auto node = trip_path_->GetEnhancedNode(i); #ifdef LOGGING_LEVEL_TRACE auto prev_edge = trip_path_->GetPrevEdge(i); @@ -249,7 +251,6 @@ std::list ManeuversBuilder::Produce() { LOG_TRACE(std::string(" curr_edge=") + (curr_edge ? curr_edge->ToString() : "NONE")); LOG_TRACE(std::string(" prev2curr_turn_degree=") + std::to_string(prev2curr_turn_degree) + " is a " + Turn::GetTypeString(Turn::GetType(prev2curr_turn_degree))); - auto node = trip_path_->GetEnhancedNode(i); for (size_t z = 0; z < node->intersecting_edge_size(); ++z) { auto intersecting_edge = node->GetIntersectingEdge(z); auto xturn_degree = GetTurnDegree(prev_edge->end_heading(), intersecting_edge->begin_heading()); @@ -274,7 +275,34 @@ std::list ManeuversBuilder::Produce() { std::string(" | left_similar_traversable_outbound =") + std::to_string(xedge_counts.left_similar_traversable_outbound)); #endif - + if (trip_path_->GetCurrEdge(i)->pedestrian_type() == PedestrianType::kBlind) { + switch (node->type()) { + case TripLeg_Node_Type_kStreetIntersection: { + std::vector> name_list; + for (size_t z = 0; z < node->intersecting_edge_size(); ++z) { + auto intersecting_edge = node->GetIntersectingEdge(z); + for (const auto& name : intersecting_edge->name()) { + std::pair cur_street = {name.value(), name.is_route_number()}; + if (std::find(name_list.begin(), name_list.end(), cur_street) == name_list.end()) + name_list.push_back(cur_street); + } + } + if (!name_list.empty()) { + maneuvers.front().set_cross_street_names(name_list); + maneuvers.front().set_node_type(node->type()); + if (node->traffic_signal()) + maneuvers.front().set_traffic_signal(true); + } + break; + } + case TripLeg_Node_Type_kGate: + case TripLeg_Node_Type_kBollard: + maneuvers.front().set_node_type(node->type()); + break; + default: + break; + } + } if (CanManeuverIncludePrevEdge(maneuvers.front(), i)) { UpdateManeuver(maneuvers.front(), i); } else { @@ -509,6 +537,19 @@ void ManeuversBuilder::Combine(std::list& maneuvers) { ++next_man; } // Do not combine + // if has node_type + else if ((curr_man->pedestrian_type() == PedestrianType::kBlind && + next_man->pedestrian_type() == PedestrianType::kBlind) && + (curr_man->has_node_type() || next_man->has_node_type() || curr_man->is_steps() || + next_man->is_steps() || curr_man->is_bridge() || next_man->is_bridge() || + curr_man->is_tunnel() || next_man->is_tunnel())) { + LOG_TRACE("+++ Do Not Combine: if has node type+++"); + // Update with no combine + prev_man = curr_man; + curr_man = next_man; + ++next_man; + } + // Do not combine // if trail type is different (unnamed/named pedestrian/bike/mtb) else if (curr_man->trail_type() != next_man->trail_type()) { LOG_TRACE("+++ Do Not Combine: if trail type is different +++"); @@ -1247,6 +1288,14 @@ void ManeuversBuilder::InitializeManeuver(Maneuver& maneuver, int node_index) { } } + if (maneuver.pedestrian_type() == PedestrianType::kBlind) { + if (prev_edge->use() == TripLeg_Use_kStepsUse) + maneuver.set_steps(true); + if (prev_edge->bridge()) + maneuver.set_bridge(true); + if (prev_edge->tunnel()) + maneuver.set_tunnel(true); + } // TODO - what about street names; maybe check name flag UpdateManeuver(maneuver, node_index); } @@ -1377,7 +1426,6 @@ void ManeuversBuilder::UpdateManeuver(Maneuver& maneuver, int node_index) { } void ManeuversBuilder::FinalizeManeuver(Maneuver& maneuver, int node_index) { - auto prev_edge = trip_path_->GetPrevEdge(node_index); auto curr_edge = trip_path_->GetCurrEdge(node_index); auto node = trip_path_->GetEnhancedNode(node_index); @@ -1478,7 +1526,6 @@ void ManeuversBuilder::FinalizeManeuver(Maneuver& maneuver, int node_index) { maneuver.set_begin_street_names(std::move(curr_edge_names)); } } - if (node->type() == TripLeg_Node_Type::TripLeg_Node_Type_kBikeShare && prev_edge && (prev_edge->travel_mode() == TravelMode::kBicycle) && maneuver.travel_mode() == TravelMode::kPedestrian) { @@ -1630,7 +1677,7 @@ void ManeuversBuilder::SetManeuverType(Maneuver& maneuver, bool none_type_allowe // Process Turn Channel else if (none_type_allowed && maneuver.turn_channel()) { maneuver.set_type(DirectionsLeg_Maneuver_Type_kNone); - LOG_TRACE("ManeuverType=TURN_CHANNNEL"); + LOG_TRACE("ManeuverType=TURN_CHANNEL"); } // Process exit // if maneuver is ramp @@ -2059,6 +2106,9 @@ bool ManeuversBuilder::CanManeuverIncludePrevEdge(Maneuver& maneuver, int node_i auto node = trip_path_->GetEnhancedNode(node_index); auto turn_degree = GetTurnDegree(prev_edge->end_heading(), curr_edge->begin_heading()); + if (curr_edge->pedestrian_type() == PedestrianType::kBlind && maneuver.has_node_type()) { + return false; + } if (node->type() == TripLeg_Node_Type::TripLeg_Node_Type_kBikeShare) { return false; } @@ -3939,5 +3989,40 @@ void ManeuversBuilder::CollapseMergeManeuvers(std::list& maneuvers) { } } +void ManeuversBuilder::AddLandmarksFromTripLegToManeuvers(std::list& maneuvers) { + std::vector landmarks{}; + for (auto man = maneuvers.begin(); man != maneuvers.end(); ++man) { + // set landmarks correlated with edges in the previous maneuver to the current maneuver + if (!landmarks.empty()) { + man->set_landmarks(landmarks); + } + landmarks.clear(); + + // accumulate landmarks in the current maneuver + double distance_from_begin_to_curr_edge = 0; // distance from the begin point of the whole + // manerver to the begin point of the current edge + double maneuver_total_distance = + man->length() * kMetersPerKm; // total distance of the maneuver in meters + + for (auto node = man->begin_node_index(); node < man->end_node_index(); ++node) { + auto curr_edge = trip_path_->GetCurrEdge(node); + // multipoint routes with `through` or `via` types can have consecutive copies of the same edge + if (curr_edge != trip_path_->GetCurrEdge(node + 1)) { + // every time we are about to leave an edge, collect all landmarks in it + // and reset distance of each landmark to the distance from the landmark to the maneuver point + auto curr_landmarks = curr_edge->landmarks(); + for (auto& l : curr_landmarks) { + double new_distance = + maneuver_total_distance - l.distance() - distance_from_begin_to_curr_edge; + l.set_distance(new_distance); + } + std::move(curr_landmarks.begin(), curr_landmarks.end(), std::back_inserter(landmarks)); + // accumulate the distance from maneuver to the curr edge we are working on + distance_from_begin_to_curr_edge += curr_edge->length_km() * kMetersPerKm; + } + } + } +} + } // namespace odin } // namespace valhalla diff --git a/src/odin/markup_formatter.cc b/src/odin/markup_formatter.cc index 5b2eb48a70..2d33ee8cbc 100644 --- a/src/odin/markup_formatter.cc +++ b/src/odin/markup_formatter.cc @@ -4,7 +4,6 @@ #include #include -#include #include "odin/markup_formatter.h" #include "proto/common.pb.h" @@ -21,8 +20,8 @@ constexpr auto KDoubleQuotes = "\""; const std::string& PronunciationAlphabetToString(valhalla::Pronunciation_Alphabet alphabet) { static const std::unordered_map values{{valhalla::Pronunciation_Alphabet::Pronunciation_Alphabet_kIpa, "ipa"}, - {valhalla::Pronunciation_Alphabet::Pronunciation_Alphabet_kXKatakana, "x-katakana"}, - {valhalla::Pronunciation_Alphabet::Pronunciation_Alphabet_kXJeita, "x-jeita"}, + {valhalla::Pronunciation_Alphabet::Pronunciation_Alphabet_kKatakana, "katakana"}, + {valhalla::Pronunciation_Alphabet::Pronunciation_Alphabet_kJeita, "jeita"}, {valhalla::Pronunciation_Alphabet::Pronunciation_Alphabet_kNtSampa, "nt-sampa"}}; auto f = values.find(alphabet); if (f == values.cend()) diff --git a/src/odin/narrative_dictionary.cc b/src/odin/narrative_dictionary.cc index a71117a82e..b5d168255f 100644 --- a/src/odin/narrative_dictionary.cc +++ b/src/odin/narrative_dictionary.cc @@ -1,5 +1,3 @@ -#include - #include #include "midgard/logging.h" @@ -315,6 +313,10 @@ void NarrativeDictionary::Load(const boost::property_tree::ptree& narrative_pt) // Populate approach_verbal_alert_subset Load(approach_verbal_alert_subset, narrative_pt.get_child(kApproachVerbalAlertKey)); + ///////////////////////////////////////////////////////////////////////////// + LOG_TRACE("Populate pass_subset..."); + // Populate pass_subset + Load(pass_subset, narrative_pt.get_child(kPassKey)); /////////////////////////////////////////////////////////////////////////// LOG_TRACE("Populate elevator_subset"); // Populate elevator_subset @@ -569,6 +571,16 @@ void NarrativeDictionary::Load(ApproachVerbalAlertSubset& approach_verbal_alert_ as_vector(approach_verbal_alert_subset_pt, kUsCustomaryLengthsKey); } +void NarrativeDictionary::Load(PassSubset& pass_handle, + const boost::property_tree::ptree& pass_subset_pt) { + + // Populate phrases + Load(static_cast(pass_handle), pass_subset_pt); + + // Populate object_labels + pass_handle.object_labels = as_vector(pass_subset_pt, kObjectLabelsKey); +} + void NarrativeDictionary::Load(EnterBuildingSubset& enter_building_handle, const boost::property_tree::ptree& enter_building_subset_pt) { diff --git a/src/odin/narrativebuilder.cc b/src/odin/narrativebuilder.cc index a35cabb0b8..db09fca81b 100644 --- a/src/odin/narrativebuilder.cc +++ b/src/odin/narrativebuilder.cc @@ -5,7 +5,6 @@ #include #include -#include #include "baldr/verbal_text_formatter.h" #include "midgard/constants.h" @@ -491,19 +490,27 @@ void NarrativeBuilder::Build(std::list& maneuvers) { } case DirectionsLeg_Maneuver_Type_kContinue: default: { - // Set instruction - maneuver.set_instruction(FormContinueInstruction(maneuver)); + if (maneuver.has_node_type()) { + std::string instr = FormPassInstruction(maneuver); + // Set instruction + maneuver.set_instruction(instr); + // Set verbal pre transition instruction + maneuver.set_verbal_pre_transition_instruction(instr); + } else { + // Set instruction + maneuver.set_instruction(FormContinueInstruction(maneuver)); - // Set verbal transition alert instruction - maneuver.set_verbal_transition_alert_instruction( - FormVerbalAlertContinueInstruction(maneuver)); + // Set verbal transition alert instruction + maneuver.set_verbal_transition_alert_instruction( + FormVerbalAlertContinueInstruction(maneuver)); - // Set verbal pre transition instruction - maneuver.set_verbal_pre_transition_instruction(FormVerbalContinueInstruction(maneuver)); + // Set verbal pre transition instruction + maneuver.set_verbal_pre_transition_instruction(FormVerbalContinueInstruction(maneuver)); - // Set verbal post transition instruction - maneuver.set_verbal_post_transition_instruction( - FormVerbalPostTransitionInstruction(maneuver)); + // Set verbal post transition instruction + maneuver.set_verbal_post_transition_instruction( + FormVerbalPostTransitionInstruction(maneuver)); + } break; } } @@ -4313,6 +4320,52 @@ std::string NarrativeBuilder::FormTransitPlatformCountLabel( return transit_stop_count_labels.at(kPluralCategoryOtherKey); } +std::string NarrativeBuilder::FormPassInstruction(Maneuver& maneuver) { + // "0": "Pass .", + // "1": "Pass traffic lights on .", + std::string instruction; + instruction.reserve(kInstructionInitialCapacity); + + // Determine which phrase to use + uint8_t phrase_id = 0; + std::string object_label; + auto dictionary_object_index = kStreetIntersectionIndex; // kGateIndex; + if (maneuver.has_node_type()) { + switch (maneuver.node_type()) { + case TripLeg_Node_Type_kGate: + dictionary_object_index = kGateIndex; + break; + case TripLeg_Node_Type_kBollard: + dictionary_object_index = kBollardIndex; + break; + case TripLeg_Node_Type_kStreetIntersection: + dictionary_object_index = kStreetIntersectionIndex; + if (maneuver.traffic_signal()) + phrase_id = 1; + if (maneuver.HasCrossStreetNames()) + object_label = FormStreetNames(maneuver, maneuver.cross_street_names()); + break; + default: + break; + } + if (object_label.empty()) + object_label = dictionary_.pass_subset.object_labels.at(dictionary_object_index); + } + + // Set instruction to the determined tagged phrase + instruction = dictionary_.pass_subset.phrases.at(std::to_string(phrase_id)); + + // Replace phrase tags with values + boost::replace_all(instruction, kObjectLabelTag, object_label); + + // If enabled, form articulated prepositions + if (articulated_preposition_enabled_) { + FormArticulatedPrepositions(instruction); + } + + return instruction; +} + std::string NarrativeBuilder::GetPluralCategory(size_t count) { if (count == 1) { return kPluralCategoryOneKey; @@ -4567,11 +4620,19 @@ NarrativeBuilder::FormStreetNames(const Maneuver& maneuver, // If empty street names string // then determine if walkway or bike path if (enhance_empty_street_names && street_names_string.empty() && empty_street_name_labels) { - + // Set names in blind user mode: + if (maneuver.pedestrian_type() == PedestrianType::kBlind) { + if (maneuver.is_steps()) + street_names_string = empty_street_name_labels->at(kStepsIndex); + else if (maneuver.is_bridge()) + street_names_string = empty_street_name_labels->at(kBridgeIndex); + else if (maneuver.is_tunnel()) + street_names_string = empty_street_name_labels->at(kTunnelIndex); + } // If pedestrian travel mode on unnamed footway // then set street names string to walkway. Additionally, if the path // is a pedestrian crossing, use appropriate phrasing. - if ((maneuver.travel_mode() == TravelMode::kPedestrian) && maneuver.unnamed_walkway()) { + else if ((maneuver.travel_mode() == TravelMode::kPedestrian) && maneuver.unnamed_walkway()) { auto dictionary_index = maneuver.pedestrian_crossing() ? kPedestrianCrossingIndex : kWalkwayIndex; street_names_string = empty_street_name_labels->at(dictionary_index); @@ -4579,13 +4640,14 @@ NarrativeBuilder::FormStreetNames(const Maneuver& maneuver, // If bicycle travel mode on unnamed cycleway // then set street names string to cycleway - if ((maneuver.travel_mode() == TravelMode::kBicycle) && maneuver.unnamed_cycleway()) { + else if ((maneuver.travel_mode() == TravelMode::kBicycle) && maneuver.unnamed_cycleway()) { street_names_string = empty_street_name_labels->at(kCyclewayIndex); } // If bicycle travel mode on unnamed mountain bike trail // then set street names string to mountain bike trail - if ((maneuver.travel_mode() == TravelMode::kBicycle) && maneuver.unnamed_mountain_bike_trail()) { + else if ((maneuver.travel_mode() == TravelMode::kBicycle) && + maneuver.unnamed_mountain_bike_trail()) { street_names_string = empty_street_name_labels->at(kMountainBikeTrailIndex); } } @@ -4623,6 +4685,8 @@ std::string NarrativeBuilder::FormStreetNames(const StreetNames& street_names, void NarrativeBuilder::FormVerbalMultiCue(std::list& maneuvers) { Maneuver* prev_maneuver = nullptr; for (auto& maneuver : maneuvers) { + if (maneuver.pedestrian_type() == PedestrianType::kBlind) + continue; if (prev_maneuver && IsVerbalMultiCuePossible(*prev_maneuver, maneuver)) { // Determine if imminent or distant verbal multi-cue // if previous maneuver has an intersecting traversable outbound edge diff --git a/src/odin/signs.cc b/src/odin/signs.cc index a9bd13f435..3c10f813ef 100644 --- a/src/odin/signs.cc +++ b/src/odin/signs.cc @@ -1,6 +1,5 @@ #include #include -#include #include "midgard/logging.h" diff --git a/src/odin/util.cc b/src/odin/util.cc index 3e2adab525..fddfe6c9a5 100644 --- a/src/odin/util.cc +++ b/src/odin/util.cc @@ -98,7 +98,7 @@ bool IsSimilarTurnDegree(uint32_t path_turn_degree, return (turn_degree_delta <= turn_degree_threshold); } -// Get the time from the inputed date. +// Get the time from the provided date. // date_time is in the format of 2015-05-06T08:00-05:00 std::string get_localized_time(const std::string& date_time, const std::locale& locale) { if (date_time.find('T') == std::string::npos) { @@ -132,7 +132,7 @@ std::string get_localized_time(const std::string& date_time, const std::locale& return time; } -// Get the date from the inputed date. +// Get the date from the provided date. // date_time is in the format of 2015-05-06T08:00-05:00 std::string get_localized_date(const std::string& date_time, const std::locale& locale) { if (date_time.find('T') == std::string::npos) { diff --git a/src/odin/worker.cc b/src/odin/worker.cc index f1f1f54a39..1e69dd76a0 100644 --- a/src/odin/worker.cc +++ b/src/odin/worker.cc @@ -1,10 +1,5 @@ -#include #include -#include -#include #include -#include -#include #include @@ -50,7 +45,7 @@ std::string odin_worker_t::narrate(Api& request) const { } void odin_worker_t::status(Api&) const { -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES // if we are in the process of shutting down we signal that here // should react by draining traffic (though they are likely doing this as they are usually the ones // who sent us the request to shutdown) @@ -60,7 +55,7 @@ void odin_worker_t::status(Api&) const { #endif } -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES prime_server::worker_t::result_t odin_worker_t::work(const std::list& job, void* request_info, diff --git a/src/proto_conversions.cc b/src/proto_conversions.cc index b16473cf16..9f01d1d89b 100644 --- a/src/proto_conversions.cc +++ b/src/proto_conversions.cc @@ -3,7 +3,18 @@ using namespace valhalla; +const std::string empty_str; + namespace valhalla { +const std::string& MatrixAlgoToString(const valhalla::Matrix::Algorithm algo) { + static const std::unordered_map algos{ + {valhalla::Matrix::CostMatrix, "costmatrix"}, + {valhalla::Matrix::TimeDistanceMatrix, "timedistancematrix"}, + {valhalla::Matrix::TimeDistanceBSSMatrix, "timedistancbssematrix"}, + }; + auto i = algos.find(algo); + return i == algos.cend() ? empty_str : i->second; +}; std::string incidentTypeToString(const valhalla::IncidentsTile::Metadata::Type& incident_type) { switch (incident_type) { @@ -85,7 +96,6 @@ const char* incidentImpactToString(const valhalla::IncidentsTile::Metadata::Impa } const std::string& GuidanceViewTypeToString(const valhalla::DirectionsLeg_GuidanceView_Type type) { - static const std::string empty; static const std::unordered_map types{{DirectionsLeg_GuidanceView_Type_kJunction, "jct"}, {DirectionsLeg_GuidanceView_Type_kSapa, "sapa"}, @@ -97,7 +107,7 @@ const std::string& GuidanceViewTypeToString(const valhalla::DirectionsLeg_Guidan {DirectionsLeg_GuidanceView_Type_kDirectionboard, "directionboard"}, {DirectionsLeg_GuidanceView_Type_kSignboard, "signboard"}}; auto i = types.find(type); - return i == types.cend() ? empty : i->second; + return i == types.cend() ? empty_str : i->second; } bool Options_Action_Enum_Parse(const std::string& action, Options::Action* a) { @@ -123,9 +133,10 @@ bool Options_Action_Enum_Parse(const std::string& action, Options::Action* a) { } bool Options_ExpansionAction_Enum_Parse(const std::string& action, Options::Action* a) { - static const std::unordered_map actions{{"route", Options::route}, - {"isochrone", - Options::isochrone}}; + static const std::unordered_map + actions{{"route", Options::route}, + {"isochrone", Options::isochrone}, + {"sources_to_targets", Options::sources_to_targets}}; auto i = actions.find(action); if (i == actions.cend()) return false; @@ -134,7 +145,6 @@ bool Options_ExpansionAction_Enum_Parse(const std::string& action, Options::Acti } const std::string& Options_Action_Enum_Name(const Options::Action action) { - static const std::string empty; static const std::unordered_map actions{ {Options::route, "route"}, {Options::locate, "locate"}, @@ -150,7 +160,7 @@ const std::string& Options_Action_Enum_Name(const Options::Action action) { {Options::status, "status"}, }; auto i = actions.find(action); - return i == actions.cend() ? empty : i->second; + return i == actions.cend() ? empty_str : i->second; } bool Location_Type_Enum_Parse(const std::string& type, Location::Type* t) { @@ -167,7 +177,6 @@ bool Location_Type_Enum_Parse(const std::string& type, Location::Type* t) { return true; } const std::string& Location_Type_Enum_Name(const Location::Type type) { - static const std::string empty; static const std::unordered_map types{ {Location::kBreak, "break"}, {Location::kThrough, "through"}, @@ -175,18 +184,17 @@ const std::string& Location_Type_Enum_Name(const Location::Type type) { {Location::kVia, "via"}, }; auto i = types.find(type); - return i == types.cend() ? empty : i->second; + return i == types.cend() ? empty_str : i->second; } const std::string& Location_SideOfStreet_Enum_Name(const Location::SideOfStreet side) { - static const std::string empty; static const std::unordered_map sides{ {Location::kLeft, "left"}, {Location::kRight, "right"}, {Location::kNone, "none"}, }; auto i = sides.find(side); - return i == sides.cend() ? empty : i->second; + return i == sides.cend() ? empty_str : i->second; } bool Costing_Enum_Parse(const std::string& costing, Costing::Type* c) { @@ -215,7 +223,6 @@ bool Costing_Enum_Parse(const std::string& costing, Costing::Type* c) { } const std::string& Costing_Enum_Name(const Costing::Type costing) { - static const std::string empty; static const std::unordered_map costings{ {Costing::auto_, "auto"}, // auto_shorter is deprecated @@ -233,7 +240,7 @@ const std::string& Costing_Enum_Name(const Costing::Type costing) { {Costing::bikeshare, "bikeshare"}, }; auto i = costings.find(costing); - return i == costings.cend() ? empty : i->second; + return i == costings.cend() ? empty_str : i->second; } bool ShapeMatch_Enum_Parse(const std::string& match, ShapeMatch* s) { @@ -250,22 +257,19 @@ bool ShapeMatch_Enum_Parse(const std::string& match, ShapeMatch* s) { } const std::string& ShapeMatch_Enum_Name(const ShapeMatch match) { - static const std::string empty; static const std::unordered_map matches{ {ShapeMatch::edge_walk, "edge_walk"}, {ShapeMatch::map_snap, "map_snap"}, {ShapeMatch::walk_or_snap, "walk_or_snap"}, }; auto i = matches.find(match); - return i == matches.cend() ? empty : i->second; + return i == matches.cend() ? empty_str : i->second; } bool Options_Format_Enum_Parse(const std::string& format, Options::Format* f) { static const std::unordered_map formats{ - {"json", Options::json}, - {"gpx", Options::gpx}, - {"osrm", Options::osrm}, - {"pbf", Options::pbf}, + {"json", Options::json}, {"gpx", Options::gpx}, {"osrm", Options::osrm}, + {"pbf", Options::pbf}, {"geotiff", Options::geotiff}, }; auto i = formats.find(format); if (i == formats.cend()) @@ -275,25 +279,21 @@ bool Options_Format_Enum_Parse(const std::string& format, Options::Format* f) { } const std::string& Options_Format_Enum_Name(const Options::Format match) { - static const std::string empty; static const std::unordered_map formats{ - {Options::json, "json"}, - {Options::gpx, "gpx"}, - {Options::osrm, "osrm"}, - {Options::pbf, "pbf"}, + {Options::json, "json"}, {Options::gpx, "gpx"}, {Options::osrm, "osrm"}, + {Options::pbf, "pbf"}, {Options::geotiff, "geotiff"}, }; auto i = formats.find(match); - return i == formats.cend() ? empty : i->second; + return i == formats.cend() ? empty_str : i->second; } const std::string& Options_Units_Enum_Name(const Options::Units unit) { - static const std::string empty; static const std::unordered_map units{ {Options::kilometers, "kilometers"}, {Options::miles, "miles"}, }; auto i = units.find(unit); - return i == units.cend() ? empty : i->second; + return i == units.cend() ? empty_str : i->second; } bool FilterAction_Enum_Parse(const std::string& action, FilterAction* a) { @@ -309,13 +309,12 @@ bool FilterAction_Enum_Parse(const std::string& action, FilterAction* a) { } const std::string& FilterAction_Enum_Name(const FilterAction action) { - static const std::string empty; static const std::unordered_map actions{ {FilterAction::exclude, "exclude"}, {FilterAction::include, "include"}, }; auto i = actions.find(action); - return i == actions.cend() ? empty : i->second; + return i == actions.cend() ? empty_str : i->second; } bool DirectionsType_Enum_Parse(const std::string& dtype, DirectionsType* t) { @@ -365,11 +364,13 @@ bool RoadClass_Enum_Parse(const std::string& rc_name, valhalla::RoadClass* rc) { bool Options_ExpansionProperties_Enum_Parse(const std::string& prop, Options::ExpansionProperties* a) { static const std::unordered_map - actions{{"costs", Options_ExpansionProperties_costs}, - {"durations", Options_ExpansionProperties_durations}, - {"distances", Options_ExpansionProperties_distances}, - {"statuses", Options_ExpansionProperties_statuses}, - {"edge_ids", Options::ExpansionProperties::Options_ExpansionProperties_edge_ids}}; + actions{{"cost", Options_ExpansionProperties_cost}, + {"duration", Options_ExpansionProperties_duration}, + {"distance", Options_ExpansionProperties_distance}, + {"edge_status", Options_ExpansionProperties_edge_status}, + {"edge_id", Options::ExpansionProperties::Options_ExpansionProperties_edge_id}, + {"pred_edge_id", Options_ExpansionProperties_pred_edge_id}, + {"expansion_type", Options_ExpansionProperties_expansion_type}}; auto i = actions.find(prop); if (i == actions.cend()) return false; @@ -381,14 +382,13 @@ const std::unordered_map vehicle_to_string{ {static_cast(VehicleType::kCar), "car"}, {static_cast(VehicleType::kMotorcycle), "motorcycle"}, {static_cast(VehicleType::kAutoBus), "bus"}, - {static_cast(VehicleType::kTractorTrailer), "tractor_trailer"}, + {static_cast(VehicleType::kTruck), "truck"}, {static_cast(VehicleType::kMotorScooter), "motor_scooter"}, }; const std::unordered_map pedestrian_to_string{ {static_cast(PedestrianType::kFoot), "foot"}, {static_cast(PedestrianType::kWheelchair), "wheelchair"}, - {static_cast(PedestrianType::kSegway), "segway"}, }; const std::unordered_map bicycle_to_string{ @@ -437,4 +437,14 @@ travel_mode_type(const valhalla::DirectionsLeg_Maneuver& maneuver) { throw std::logic_error("Unknown travel mode"); } } + +const std::string& Expansion_EdgeStatus_Enum_Name(const Expansion_EdgeStatus status) { + static const std::unordered_map statuses{ + {Expansion_EdgeStatus_reached, "r"}, + {Expansion_EdgeStatus_settled, "s"}, + {Expansion_EdgeStatus_connected, "c"}, + }; + auto i = statuses.find(status); + return i == statuses.cend() ? empty_str : i->second; +} } // namespace valhalla diff --git a/src/sif/CMakeLists.txt b/src/sif/CMakeLists.txt index 0f5f0342c6..ec82dc56bc 100644 --- a/src/sif/CMakeLists.txt +++ b/src/sif/CMakeLists.txt @@ -13,12 +13,11 @@ set(sources dynamiccost.cc recost.cc) -# treat date library as system -set(system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include) -if(APPLE) - list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) -endif() - +set(system_includes + ${date_include_dir} + $<$:${dirent_include_dir}> + ${rapidjson_include_dir}) + valhalla_module(NAME sif SOURCES ${sources} HEADERS ${headers} @@ -26,9 +25,6 @@ valhalla_module(NAME sif PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> - PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include SYSTEM_INCLUDE_DIRECTORIES PUBLIC ${system_includes} diff --git a/src/sif/autocost.cc b/src/sif/autocost.cc index 17eff49e86..e280e52dee 100644 --- a/src/sif/autocost.cc +++ b/src/sif/autocost.cc @@ -46,6 +46,8 @@ constexpr float kTCCrossing = 2.0f; constexpr float kTCUnfavorable = 2.5f; constexpr float kTCUnfavorableSharp = 3.5f; constexpr float kTCReverse = 9.5f; +constexpr float kTCRamp = 1.5f; +constexpr float kTCRoundabout = 0.5f; // How much to favor taxi roads. constexpr float kTaxiFactor = 0.85f; @@ -68,7 +70,6 @@ constexpr float kMinFactor = 0.1f; constexpr float kMaxFactor = 100000.0f; // Default auto attributes -const std::string kDefaultAutoType = "car"; constexpr float kDefaultAutoHeight = 1.6f; // Meters (62.9921 inches) constexpr float kDefaultAutoWidth = 1.9f; // Meters (74.8031 inches) @@ -80,6 +81,8 @@ constexpr ranged_default_t kUseDistanceRange{0, kDefaultUseDistance, 1.0f constexpr ranged_default_t kAutoHeightRange{0, kDefaultAutoHeight, 10.0f}; constexpr ranged_default_t kAutoWidthRange{0, kDefaultAutoWidth, 10.0f}; constexpr ranged_default_t kProbabilityRange{0, kDefaultRestrictionProbability, 100}; +constexpr ranged_default_t kVehicleSpeedRange{10, baldr::kMaxAssumedSpeed, + baldr::kMaxSpeedKph}; constexpr float kHighwayFactor[] = { 1.0f, // Motorway @@ -352,20 +355,7 @@ AutoCost::AutoCost(const Costing& costing, uint32_t access_mask) // Get the vehicle type - enter as string and convert to enum. // Used to set the surface factor - penalize some roads based on surface type. surface_factor_ = 0.5f; - const std::string& type = costing_options.transport_type(); - if (type == "motorcycle") { - type_ = VehicleType::kMotorcycle; - surface_factor_ = 1.0f; - } else if (type == "bus") { - type_ = VehicleType::kBus; - } else if (type == "tractor_trailer") { - type_ = VehicleType::kTractorTrailer; - } else if (type == "four_wheel_drive") { - type_ = VehicleType::kFourWheelDrive; - surface_factor_ = 0.0f; - } else { - type_ = VehicleType::kCar; - } + type_ = VehicleType::kCar; // Get the base transition costs get_base_costs(costing); @@ -435,7 +425,7 @@ bool AutoCost::Allowed(const baldr::DirectedEdge* edge, // a not thru region and a heading selected an edge entering the // region. if (!IsAccessible(edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((pred.restrictions() & (1 << edge->localedgeidx())) && !ignore_restrictions_) || + ((pred.restrictions() & (1 << edge->localedgeidx())) && !ignore_turn_restrictions_) || edge->surface() == Surface::kImpassable || IsUserAvoidEdge(edgeid) || (!allow_destination_only_ && !pred.destonly() && edge->destonly()) || (pred.closure_pruning() && IsClosed(edge, tile)) || @@ -460,7 +450,7 @@ bool AutoCost::AllowedReverse(const baldr::DirectedEdge* edge, // Check access, U-turn, and simple turn restriction. // Allow U-turns at dead-end nodes. if (!IsAccessible(opp_edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_restrictions_) || + ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_turn_restrictions_) || opp_edge->surface() == Surface::kImpassable || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly()) || (pred.closure_pruning() && IsClosed(opp_edge, tile)) || @@ -468,8 +458,8 @@ bool AutoCost::AllowedReverse(const baldr::DirectedEdge* edge, return false; } - return DynamicCost::EvaluateRestrictions(access_mask_, edge, false, tile, opp_edgeid, current_time, - tz_index, restriction_idx); + return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, + current_time, tz_index, restriction_idx); } bool AutoCost::ModeSpecificAllowed(const baldr::AccessRestriction& restriction) const { @@ -564,7 +554,7 @@ Cost AutoCost::TransitionCost(const baldr::DirectedEdge* edge, // destination only, alley, maneuver penalty uint32_t idx = pred.opp_local_idx(); Cost c = base_transition_cost(node, edge, &pred, idx); - c.secs = OSRMCarTurnDuration(edge, node, pred.opp_local_idx()); + c.secs += OSRMCarTurnDuration(edge, node, pred.opp_local_idx()); // Transition time = turncost * stopimpact * densityfactor if (edge->stopimpact(idx) > 0 && !shortest_) { @@ -579,9 +569,9 @@ Cost AutoCost::TransitionCost(const baldr::DirectedEdge* edge, if ((edge->use() != Use::kRamp && pred.use() == Use::kRamp) || (edge->use() == Use::kRamp && pred.use() != Use::kRamp)) { - turn_cost += 1.5f; + turn_cost += kTCRamp; if (edge->roundabout()) - turn_cost += 0.5f; + turn_cost += kTCRoundabout; } float seconds = turn_cost; @@ -632,7 +622,7 @@ Cost AutoCost::TransitionCostReverse(const uint32_t idx, // Get the transition cost for country crossing, ferry, gate, toll booth, // destination only, alley, maneuver penalty Cost c = base_transition_cost(node, edge, pred, idx); - c.secs = OSRMCarTurnDuration(edge, node, pred->opp_local_idx()); + c.secs += OSRMCarTurnDuration(edge, node, pred->opp_local_idx()); // Transition time = turncost * stopimpact * densityfactor if (edge->stopimpact(idx) > 0 && !shortest_) { @@ -647,9 +637,9 @@ Cost AutoCost::TransitionCostReverse(const uint32_t idx, if ((edge->use() != Use::kRamp && pred->use() == Use::kRamp) || (edge->use() == Use::kRamp && pred->use() != Use::kRamp)) { - turn_cost += 1.5f; + turn_cost += kTCRamp; if (edge->roundabout()) - turn_cost += 0.5f; + turn_cost += kTCRoundabout; } float seconds = turn_cost; @@ -697,7 +687,6 @@ void ParseAutoCostOptions(const rapidjson::Document& doc, const auto& json = rapidjson::get_child(doc, costing_options_key.c_str(), dummy); ParseBaseCostOptions(json, c, kBaseCostOptsConfig); - JSON_PBF_DEFAULT(co, kDefaultAutoType, json, "/type", transport_type); JSON_PBF_RANGED_DEFAULT(co, kAlleyFactorRange, json, "/alley_factor", alley_factor); JSON_PBF_RANGED_DEFAULT(co, kUseHighwaysRange, json, "/use_highways", use_highways); JSON_PBF_RANGED_DEFAULT(co, kUseTollsRange, json, "/use_tolls", use_tolls); @@ -709,6 +698,7 @@ void ParseAutoCostOptions(const rapidjson::Document& doc, JSON_PBF_DEFAULT(co, false, json, "/include_hot", include_hot); JSON_PBF_DEFAULT(co, false, json, "/include_hov2", include_hov2); JSON_PBF_DEFAULT(co, false, json, "/include_hov3", include_hov3); + JSON_PBF_RANGED_DEFAULT(co, kVehicleSpeedRange, json, "/top_speed", top_speed); } cost_ptr_t CreateAutoCost(const Costing& costing_options) { @@ -823,7 +813,7 @@ bool BusCost::AllowedReverse(const baldr::DirectedEdge* edge, // Check access, U-turn, and simple turn restriction. // Allow U-turns at dead-end nodes. if (!IsAccessible(opp_edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_restrictions_) || + ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_turn_restrictions_) || opp_edge->surface() == Surface::kImpassable || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly()) || (pred.closure_pruning() && IsClosed(opp_edge, tile)) || @@ -831,8 +821,8 @@ bool BusCost::AllowedReverse(const baldr::DirectedEdge* edge, return false; } - return DynamicCost::EvaluateRestrictions(access_mask_, edge, false, tile, opp_edgeid, current_time, - tz_index, restriction_idx); + return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, + current_time, tz_index, restriction_idx); } void ParseBusCostOptions(const rapidjson::Document& doc, @@ -1002,15 +992,15 @@ bool TaxiCost::AllowedReverse(const baldr::DirectedEdge* edge, // Check access, U-turn, and simple turn restriction. // Allow U-turns at dead-end nodes. if (!IsAccessible(opp_edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_restrictions_) || + ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_turn_restrictions_) || opp_edge->surface() == Surface::kImpassable || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly()) || (pred.closure_pruning() && IsClosed(opp_edge, tile)) || (exclude_unpaved_ && !pred.unpaved() && opp_edge->unpaved())) { return false; } - return DynamicCost::EvaluateRestrictions(access_mask_, edge, false, tile, opp_edgeid, current_time, - tz_index, restriction_idx); + return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, + current_time, tz_index, restriction_idx); } void ParseTaxiCostOptions(const rapidjson::Document& doc, @@ -1059,8 +1049,8 @@ template std::shared_ptr make_autocost_from_json(const std::string& property, T testVal, const std::string& extra_json = "") { std::stringstream ss; - ss << R"({"costing_options":{"auto":{")" << property << R"(":)" << testVal << "}}" << extra_json - << "}"; + ss << R"({"costing": "auto", "costing_options":{"auto":{")" << property << R"(":)" << testVal + << "}}" << extra_json << "}"; Api request; ParseApi(ss.str(), valhalla::Options::route, request); return std::make_shared(request.options().costings().find(Costing::auto_)->second); diff --git a/src/sif/bicyclecost.cc b/src/sif/bicyclecost.cc index 6dacc93ae9..720c159547 100644 --- a/src/sif/bicyclecost.cc +++ b/src/sif/bicyclecost.cc @@ -396,7 +396,7 @@ class BicycleCost : public DynamicCost { Surface minimal_surface_penalized_; Surface worst_allowed_surface_; - // Cycle lane accomodation factors + // Cycle lane accommodation factors float cyclelane_factor_[8]; float path_cyclelane_factor_[4]; @@ -545,7 +545,7 @@ bool BicycleCost::Allowed(const baldr::DirectedEdge* edge, if (!IsAccessible(edge) || edge->is_shortcut() || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx() && pred.mode() == TravelMode::kBicycle) || - (!ignore_restrictions_ && (pred.restrictions() & (1 << edge->localedgeidx()))) || + (!ignore_turn_restrictions_ && (pred.restrictions() & (1 << edge->localedgeidx()))) || IsUserAvoidEdge(edgeid)) { return false; } @@ -582,7 +582,7 @@ bool BicycleCost::AllowedReverse(const baldr::DirectedEdge* edge, opp_edge->use() == Use::kPlatformConnection || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx() && pred.mode() == TravelMode::kBicycle) || - (!ignore_restrictions_ && (opp_edge->restrictions() & (1 << pred.opp_local_idx()))) || + (!ignore_turn_restrictions_ && (opp_edge->restrictions() & (1 << pred.opp_local_idx()))) || IsUserAvoidEdge(opp_edgeid)) { return false; } @@ -591,8 +591,8 @@ bool BicycleCost::AllowedReverse(const baldr::DirectedEdge* edge, if (edge->surface() > worst_allowed_surface_) { return false; } - return DynamicCost::EvaluateRestrictions(access_mask_, edge, false, tile, opp_edgeid, current_time, - tz_index, restriction_idx); + return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, + current_time, tz_index, restriction_idx); } // Returns the cost to traverse the edge and an estimate of the actual time @@ -909,7 +909,8 @@ class TestBicycleCost : public BicycleCost { TestBicycleCost* make_bicyclecost_from_json(const std::string& property, float testVal) { std::stringstream ss; - ss << R"({"costing_options":{"bicycle":{")" << property << R"(":)" << testVal << "}}}"; + ss << R"({"costing": "bicycle", "costing_options":{"bicycle":{")" << property << R"(":)" << testVal + << "}}}"; Api request; ParseApi(ss.str(), valhalla::Options::route, request); return new TestBicycleCost(request.options().costings().find(Costing::bicycle)->second); diff --git a/src/sif/dynamiccost.cc b/src/sif/dynamiccost.cc index 8bb4a7684f..dfc07f8490 100644 --- a/src/sif/dynamiccost.cc +++ b/src/sif/dynamiccost.cc @@ -1,5 +1,4 @@ #include -#include #include "baldr/graphconstants.h" #include "midgard/util.h" @@ -108,15 +107,13 @@ constexpr float kDefaultClosureFactor = 9.0f; // non-closure end constexpr ranged_default_t kClosureFactorRange{1.0f, kDefaultClosureFactor, 10.0f}; -constexpr ranged_default_t kVehicleSpeedRange{10, baldr::kMaxAssumedSpeed, - baldr::kMaxSpeedKph}; constexpr ranged_default_t kFixedSpeedRange{0, baldr::kDisableFixedSpeed, baldr::kMaxSpeedKph}; } // namespace /* * Assign default values for costing options in constructor. In case of different - * default values they should be overrided in "cost.cc" file. + * default values they should be overridden in "cost.cc" file. */ BaseCostingOptionsConfig::BaseCostingOptionsConfig() : dest_only_penalty_{0.f, kDefaultDestinationOnlyPenalty, kMaxPenalty}, @@ -151,6 +148,9 @@ DynamicCost::DynamicCost(const Costing& costing, closure_factor_(kDefaultClosureFactor), flow_mask_(kDefaultFlowMask), shortest_(costing.options().shortest()), ignore_restrictions_(costing.options().ignore_restrictions()), + ignore_non_vehicular_restrictions_(costing.options().ignore_non_vehicular_restrictions()), + ignore_turn_restrictions_(costing.options().ignore_restrictions() || + costing.options().ignore_non_vehicular_restrictions()), ignore_oneways_(costing.options().ignore_oneways()), ignore_access_(costing.options().ignore_access()), ignore_closures_(costing.options().ignore_closures()), @@ -299,6 +299,10 @@ bool DynamicCost::bicycle() const { return false; } +bool DynamicCost::is_hgv() const { + return false; +} + // Add to the exclude list. void DynamicCost::AddToExcludeList(const graph_tile_ptr&) { } @@ -385,6 +389,8 @@ void ParseBaseCostOptions(const rapidjson::Value& json, JSON_PBF_DEFAULT(co, false, json, "/ignore_oneways", ignore_oneways); JSON_PBF_DEFAULT(co, false, json, "/ignore_access", ignore_access); JSON_PBF_DEFAULT(co, false, json, "/ignore_closures", ignore_closures); + JSON_PBF_DEFAULT_V2(co, false, json, "/ignore_non_vehicular_restrictions", + ignore_non_vehicular_restrictions); // shortest JSON_PBF_DEFAULT(co, false, json, "/shortest", shortest); @@ -393,9 +399,6 @@ void ParseBaseCostOptions(const rapidjson::Value& json, co->set_disable_hierarchy_pruning( rapidjson::get(json, "/disable_hierarchy_pruning", co->disable_hierarchy_pruning())); - // top speed - JSON_PBF_RANGED_DEFAULT(co, kVehicleSpeedRange, json, "/top_speed", top_speed); - // destination only penalty JSON_PBF_RANGED_DEFAULT(co, cfg.dest_only_penalty_, json, "/destination_only_penalty", destination_only_penalty); @@ -478,23 +481,22 @@ void ParseBaseCostOptions(const rapidjson::Value& json, JSON_PBF_DEFAULT(co, cfg.include_hov2_, json, "/include_hov2", include_hov2); JSON_PBF_DEFAULT(co, cfg.include_hov3_, json, "/include_hov3", include_hov3); - co->set_fixed_speed( - kFixedSpeedRange(rapidjson::get(json, "/fixed_speed", co->fixed_speed()))); + JSON_PBF_RANGED_DEFAULT_V2(co, kFixedSpeedRange, json, "/fixed_speed", fixed_speed); } void ParseCosting(const rapidjson::Document& doc, const std::string& costing_options_key, Options& options) { - // if specified, get the costing options in there - for (auto i = Costing::Type_MIN; i <= Costing::Type_MAX; i = Costing::Type(i + 1)) { + // get the needed costing options in there + for (const auto& costing_type : kCostingTypeMapping.at(options.costing_type())) { // Create the costing options key - const auto& costing_str = valhalla::Costing_Enum_Name(i); + const auto& costing_str = valhalla::Costing_Enum_Name(costing_type); if (costing_str.empty()) continue; const auto key = costing_options_key + "/" + costing_str; // Parse the costing options - auto& costing = (*options.mutable_costings())[i]; - ParseCosting(doc, key, &costing, i); + auto& costing = (*options.mutable_costings())[costing_type]; + ParseCosting(doc, key, &costing, costing_type); } } diff --git a/src/sif/motorcyclecost.cc b/src/sif/motorcyclecost.cc index be37baf1d8..040fede3ee 100644 --- a/src/sif/motorcyclecost.cc +++ b/src/sif/motorcyclecost.cc @@ -30,7 +30,7 @@ constexpr float kDefaultUseHighways = 0.5f; // Factor between 0 and 1 constexpr float kDefaultUseTolls = 0.5f; // Factor between 0 and 1 constexpr float kDefaultUseTrails = 0.0f; // Factor between 0 and 1 -constexpr Surface kMinimumMotorcycleSurface = Surface::kDirt; +constexpr Surface kMinimumMotorcycleSurface = Surface::kImpassable; // Default turn costs constexpr float kTCStraight = 0.5f; @@ -41,6 +41,8 @@ constexpr float kTCCrossing = 2.0f; constexpr float kTCUnfavorable = 2.5f; constexpr float kTCUnfavorableSharp = 3.5f; constexpr float kTCReverse = 9.5f; +constexpr float kTCRamp = 1.5f; +constexpr float kTCRoundabout = 0.5f; // Turn costs based on side of street driving constexpr float kRightSideTurnCosts[] = {kTCStraight, kTCSlight, kTCFavorable, @@ -54,6 +56,8 @@ constexpr float kLeftSideTurnCosts[] = {kTCStraight, kTCSlight, kTCUnfa constexpr ranged_default_t kUseHighwaysRange{0, kDefaultUseHighways, 1.0f}; constexpr ranged_default_t kUseTollsRange{0, kDefaultUseTolls, 1.0f}; constexpr ranged_default_t kUseTrailsRange{0, kDefaultUseTrails, 1.0f}; +constexpr ranged_default_t kMotorcycleSpeedRange{10, baldr::kMaxAssumedSpeed, + baldr::kMaxSpeedKph}; constexpr float kHighwayFactor[] = { 1.0f, // Motorway @@ -355,7 +359,7 @@ bool MotorcycleCost::Allowed(const baldr::DirectedEdge* edge, // Check access, U-turn, and simple turn restriction. // Allow U-turns at dead-end nodes. if (!IsAccessible(edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((pred.restrictions() & (1 << edge->localedgeidx())) && !ignore_restrictions_) || + ((pred.restrictions() & (1 << edge->localedgeidx())) && !ignore_turn_restrictions_) || (edge->surface() > kMinimumMotorcycleSurface) || IsUserAvoidEdge(edgeid) || (!allow_destination_only_ && !pred.destonly() && edge->destonly()) || (pred.closure_pruning() && IsClosed(edge, tile))) { @@ -379,15 +383,15 @@ bool MotorcycleCost::AllowedReverse(const baldr::DirectedEdge* edge, // Check access, U-turn, and simple turn restriction. // Allow U-turns at dead-end nodes. if (!IsAccessible(opp_edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_restrictions_) || + ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_turn_restrictions_) || (opp_edge->surface() > kMinimumMotorcycleSurface) || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly()) || (pred.closure_pruning() && IsClosed(opp_edge, tile))) { return false; } - return DynamicCost::EvaluateRestrictions(access_mask_, edge, false, tile, opp_edgeid, current_time, - tz_index, restriction_idx); + return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, + current_time, tz_index, restriction_idx); } Cost MotorcycleCost::EdgeCost(const baldr::DirectedEdge* edge, @@ -444,7 +448,7 @@ Cost MotorcycleCost::TransitionCost(const baldr::DirectedEdge* edge, // destination only, alley, maneuver penalty uint32_t idx = pred.opp_local_idx(); Cost c = base_transition_cost(node, edge, &pred, idx); - c.secs = OSRMCarTurnDuration(edge, node, idx); + c.secs += OSRMCarTurnDuration(edge, node, idx); // Transition time = turncost * stopimpact * densityfactor if (edge->stopimpact(idx) > 0 && !shortest_) { @@ -459,9 +463,9 @@ Cost MotorcycleCost::TransitionCost(const baldr::DirectedEdge* edge, if ((edge->use() != Use::kRamp && pred.use() == Use::kRamp) || (edge->use() == Use::kRamp && pred.use() != Use::kRamp)) { - turn_cost += 1.5f; + turn_cost += kTCRamp; if (edge->roundabout()) - turn_cost += 0.5f; + turn_cost += kTCRoundabout; } float seconds = turn_cost; @@ -513,7 +517,7 @@ Cost MotorcycleCost::TransitionCostReverse(const uint32_t idx, // Get the transition cost for country crossing, ferry, gate, toll booth, // destination only, alley, maneuver penalty Cost c = base_transition_cost(node, edge, pred, idx); - c.secs = OSRMCarTurnDuration(edge, node, pred->opp_local_idx()); + c.secs += OSRMCarTurnDuration(edge, node, pred->opp_local_idx()); // Transition time = turncost * stopimpact * densityfactor if (edge->stopimpact(idx) > 0 && !shortest_) { @@ -528,9 +532,9 @@ Cost MotorcycleCost::TransitionCostReverse(const uint32_t idx, if ((edge->use() != Use::kRamp && pred->use() == Use::kRamp) || (edge->use() == Use::kRamp && pred->use() != Use::kRamp)) { - turn_cost += 1.5f; + turn_cost += kTCRamp; if (edge->roundabout()) - turn_cost += 0.5f; + turn_cost += kTCRoundabout; } float seconds = turn_cost; @@ -578,6 +582,7 @@ void ParseMotorcycleCostOptions(const rapidjson::Document& doc, JSON_PBF_RANGED_DEFAULT(co, kUseHighwaysRange, json, "/use_highways", use_highways); JSON_PBF_RANGED_DEFAULT(co, kUseTollsRange, json, "/use_tolls", use_tolls); JSON_PBF_RANGED_DEFAULT(co, kUseTrailsRange, json, "/use_trails", use_trails); + JSON_PBF_RANGED_DEFAULT(co, kMotorcycleSpeedRange, json, "/top_speed", top_speed); } cost_ptr_t CreateMotorcycleCost(const Costing& costing_options) { @@ -613,7 +618,8 @@ class TestMotorcycleCost : public MotorcycleCost { TestMotorcycleCost* make_motorcyclecost_from_json(const std::string& property, float testVal) { std::stringstream ss; - ss << R"({"costing_options":{"motorcycle":{")" << property << R"(":)" << testVal << "}}}"; + ss << R"({"costing": "motorcycle", "costing_options":{"motorcycle":{")" << property << R"(":)" + << testVal << "}}}"; Api request; ParseApi(ss.str(), valhalla::Options::route, request); return new TestMotorcycleCost(request.options().costings().find(Costing::motorcycle)->second); diff --git a/src/sif/motorscootercost.cc b/src/sif/motorscootercost.cc index 6c7c35a6d3..9923acb54a 100644 --- a/src/sif/motorscootercost.cc +++ b/src/sif/motorscootercost.cc @@ -46,6 +46,8 @@ constexpr float kTCCrossing = 2.0f; constexpr float kTCUnfavorable = 2.5f; constexpr float kTCUnfavorableSharp = 3.5f; constexpr float kTCReverse = 9.5f; +constexpr float kTCRamp = 1.5f; +constexpr float kTCRoundabout = 0.5f; // Turn costs based on side of street driving constexpr float kRightSideTurnCosts[] = {kTCStraight, kTCSlight, kTCFavorable, @@ -376,7 +378,7 @@ bool MotorScooterCost::Allowed(const baldr::DirectedEdge* edge, // Check access, U-turn, and simple turn restriction. // Allow U-turns at dead-end nodes. if (!IsAccessible(edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((pred.restrictions() & (1 << edge->localedgeidx())) && !ignore_restrictions_) || + ((pred.restrictions() & (1 << edge->localedgeidx())) && !ignore_turn_restrictions_) || (edge->surface() > kMinimumScooterSurface) || IsUserAvoidEdge(edgeid) || (!allow_destination_only_ && !pred.destonly() && edge->destonly()) || (pred.closure_pruning() && IsClosed(edge, tile))) { @@ -400,15 +402,15 @@ bool MotorScooterCost::AllowedReverse(const baldr::DirectedEdge* edge, // Check access, U-turn, and simple turn restriction. // Allow U-turns at dead-end nodes. if (!IsAccessible(opp_edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_restrictions_) || + ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_turn_restrictions_) || (opp_edge->surface() > kMinimumScooterSurface) || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly()) || (pred.closure_pruning() && IsClosed(opp_edge, tile))) { return false; } - return DynamicCost::EvaluateRestrictions(access_mask_, edge, false, tile, opp_edgeid, current_time, - tz_index, restriction_idx); + return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, + current_time, tz_index, restriction_idx); } Cost MotorScooterCost::EdgeCost(const baldr::DirectedEdge* edge, @@ -426,9 +428,11 @@ Cost MotorScooterCost::EdgeCost(const baldr::DirectedEdge* edge, return {sec * ferry_factor_, sec}; } + // prevent scooter speed to become 0 uint32_t scooter_speed = - (std::min(top_speed_, speed) * kSurfaceSpeedFactors[static_cast(edge->surface())] * - kGradeBasedSpeedFactor[static_cast(edge->weighted_grade())]); + std::max(1.f, (std::min(top_speed_, speed) * + kSurfaceSpeedFactors[static_cast(edge->surface())] * + kGradeBasedSpeedFactor[static_cast(edge->weighted_grade())])); assert(scooter_speed < speedfactor_.size()); float sec = (edge->length() * speedfactor_[scooter_speed]); @@ -469,7 +473,7 @@ Cost MotorScooterCost::TransitionCost(const baldr::DirectedEdge* edge, // destination only, alley, maneuver penalty uint32_t idx = pred.opp_local_idx(); Cost c = base_transition_cost(node, edge, &pred, idx); - c.secs = OSRMCarTurnDuration(edge, node, idx); + c.secs += OSRMCarTurnDuration(edge, node, idx); // Transition time = turncost * stopimpact * densityfactor if (edge->stopimpact(idx) > 0 && !shortest_) { @@ -484,9 +488,9 @@ Cost MotorScooterCost::TransitionCost(const baldr::DirectedEdge* edge, if ((edge->use() != Use::kRamp && pred.use() == Use::kRamp) || (edge->use() == Use::kRamp && pred.use() != Use::kRamp)) { - turn_cost += 1.5f; + turn_cost += kTCRamp; if (edge->roundabout()) - turn_cost += 0.5f; + turn_cost += kTCRoundabout; } float seconds = turn_cost; @@ -538,7 +542,7 @@ Cost MotorScooterCost::TransitionCostReverse(const uint32_t idx, // Get the transition cost for country crossing, ferry, gate, toll booth, // destination only, alley, maneuver penalty Cost c = base_transition_cost(node, edge, pred, idx); - c.secs = OSRMCarTurnDuration(edge, node, pred->opp_local_idx()); + c.secs += OSRMCarTurnDuration(edge, node, pred->opp_local_idx()); // Transition time = turncost * stopimpact * densityfactor if (edge->stopimpact(idx) > 0 && !shortest_) { @@ -553,9 +557,9 @@ Cost MotorScooterCost::TransitionCostReverse(const uint32_t idx, if ((edge->use() != Use::kRamp && pred->use() == Use::kRamp) || (edge->use() == Use::kRamp && pred->use() != Use::kRamp)) { - turn_cost += 1.5f; + turn_cost += kTCRamp; if (edge->roundabout()) - turn_cost += 0.5f; + turn_cost += kTCRoundabout; } float seconds = turn_cost; @@ -638,7 +642,8 @@ class TestMotorScooterCost : public MotorScooterCost { TestMotorScooterCost* make_motorscootercost_from_json(const std::string& property, float testVal) { std::stringstream ss; - ss << R"({"costing_options":{"motor_scooter":{")" << property << R"(":)" << testVal << "}}}"; + ss << R"({"costing": "motor_scooter", "costing_options":{"motor_scooter":{")" << property << R"(":)" + << testVal << "}}}"; Api request; ParseApi(ss.str(), valhalla::Options::route, request); return new TestMotorScooterCost(request.options().costings().find(Costing::motor_scooter)->second); diff --git a/src/sif/nocost.cc b/src/sif/nocost.cc index 083b996a85..dbd2a7a0df 100644 --- a/src/sif/nocost.cc +++ b/src/sif/nocost.cc @@ -10,7 +10,6 @@ #ifdef INLINE_TEST #include "test.h" #include "worker.h" -#include #endif using namespace valhalla::midgard; diff --git a/src/sif/pedestriancost.cc b/src/sif/pedestriancost.cc index 8288299c11..1833b68851 100644 --- a/src/sif/pedestriancost.cc +++ b/src/sif/pedestriancost.cc @@ -172,7 +172,7 @@ const BaseCostingOptionsConfig kBaseCostOptsConfig = GetBaseCostOptsConfig(); // The recipe from DIN 33466 goes: For every 300m ascent/500m descent, add // one hour to bucket H. For every 4km distance, add one hour to bucket D. // Now add half of the value in the smaller bucket to the bigger bucket -// to get the estimated duration. The method is recommeded by the DAV and +// to get the estimated duration. The method is recommended by the DAV and // the OEAV for casual hiking. // // For comparison, value researched by the Mountain Tactical Institute @@ -187,14 +187,14 @@ const BaseCostingOptionsConfig kBaseCostOptsConfig = GetBaseCostOptsConfig(); // intermediate hikers. This will bring the uphill values quite close to the // ones from the exponential while still keeping downhill speed below flat speed. // -// For negative angles(downhill), according to the DIN 33466, the speed decrease -// rapidly as it goes steeper, which could be true for moutain hikers because of -// the unpaved paths. However, we found it's against common sense that the speed -// factor goes under 1.0 (walk speed on a flat terrain) on a slight downhill slope, -// especially for city walkers. Thus, a modified Tobler's function is used here to -// correct the factor for negative angles. Hence, the walk speed increase slightly -// (faster than on a flat terrain) when the angle is between 0% and -5%, then -// decrease as shown by DIN 33466. +// For negative angles (downhill), as per DIN 33466, speed decreases rapidly +// as the slope becomes steeper. This behavior may hold true for mountain hikers +// due to unpaved paths. However, it contradicts common sense for city walkers +// when the speed factor drops below 1.0 (the walking speed on flat terrain) +// even on a slight downhill slope. To address this, we employ a modified Tobler's +// function to correct the factor for negative angles. Consequently, walk speed +// slightly increases (faster than on flat terrain) when the angle is between +// 0% and -5%, and then decreases as indicated by DIN 33466. // see https://gist.github.com/xlqian/0b25c8db6f45fb2c8bf68494e1ea54f1 constexpr float kGradeBasedSpeedFactor[] = { @@ -588,22 +588,17 @@ PedestrianCost::PedestrianCost(const Costing& costing) // Get the pedestrian type - enter as string and convert to enum const std::string& type = costing_options.transport_type(); if (type == "wheelchair") { + // TODO(nils): this needs to control much more in costing, e.g. + // we could add more AccessRestrictions for curb height or so type_ = PedestrianType::kWheelchair; - } else if (type == "segway") { - type_ = PedestrianType::kSegway; - } else { - type_ = PedestrianType::kFoot; - } - - // Set type specific defaults, override with URL inputs - if (type_ == PedestrianType::kWheelchair) { access_mask_ = kWheelchairAccess; minimal_allowed_surface_ = Surface::kCompacted; } else { - // Assume type = foot + type_ = type == "blind" ? PedestrianType::kBlind : PedestrianType::kFoot; access_mask_ = kPedestrianAccess; minimal_allowed_surface_ = Surface::kPath; } + max_distance_ = costing_options.max_distance(); speed_ = costing_options.walking_speed(); step_penalty_ = costing_options.step_penalty(); @@ -692,8 +687,8 @@ bool PedestrianCost::AllowedReverse(const baldr::DirectedEdge* edge, return false; } - return DynamicCost::EvaluateRestrictions(access_mask_, edge, false, tile, opp_edgeid, current_time, - tz_index, restriction_idx); + return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, + current_time, tz_index, restriction_idx); } // Returns the cost to traverse the edge and an estimate of the actual time @@ -893,7 +888,8 @@ TestPedestrianCost* make_pedestriancost_from_json(const std::string& property, float testVal, const std::string& /*type*/) { std::stringstream ss; - ss << R"({"costing_options":{"pedestrian":{")" << property << R"(":)" << testVal << "}}}"; + ss << R"({"costing": "pedestrian", "costing_options":{"pedestrian":{")" << property << R"(":)" + << testVal << "}}}"; Api request; ParseApi(ss.str(), valhalla::Options::route, request); return new TestPedestrianCost(request.options().costings().find(Costing::pedestrian)->second); diff --git a/src/sif/recost.cc b/src/sif/recost.cc index 788d316414..499fa9398b 100644 --- a/src/sif/recost.cc +++ b/src/sif/recost.cc @@ -8,7 +8,7 @@ namespace sif { * Will take a sequence of edges and create the set of edge labels that would represent it * Allows for the caller to essentially re-compute the costing of a given path * - * @param reader used to get access to graph data. modifyable because its got a cache + * @param reader used to get access to graph data. modifiable because its got a cache * @param costing single costing object to be used for costing/access computations * @param edge_cb the callback used to get each edge in the path * @param label_cb the callback used to emit each label in the path @@ -57,7 +57,7 @@ void recost_forward(baldr::GraphReader& reader, const baldr::NodeInfo* node = nullptr; // keep grabbing edges while we get valid ids - EdgeLabel label; + PathEdgeLabel label; uint32_t predecessor = baldr::kInvalidLabel; Cost cost{}; double length = 0; @@ -91,7 +91,7 @@ void recost_forward(baldr::GraphReader& reader, // TODO: if this edge begins a restriction, we need to start popping off edges into queue // so that we can find if we reach the end of the restriction. then we need to replay the // queued edges as normal - uint8_t time_restrictions_TODO = -1; + uint8_t time_restrictions_TODO = baldr::kInvalidRestriction; // if its not time dependent set to 0 for Allowed method below const uint64_t localtime = offset_time.valid ? offset_time.local_time : 0; // we should call 'Allowed' method even if 'ignore_access' flag is true in order to @@ -128,9 +128,9 @@ void recost_forward(baldr::GraphReader& reader, InternalTurn turn = node ? costing.TurnType(label.opp_local_idx(), node, edge) : InternalTurn::kNoTurn; - label = EdgeLabel(predecessor++, edge_id, edge, cost, cost.cost, 0, costing.travel_mode(), length, - transition_cost, time_restrictions_TODO, !ignore_access, - static_cast(flow_sources & baldr::kDefaultFlowMask), turn); + label = PathEdgeLabel(predecessor++, edge_id, edge, cost, cost.cost, costing.travel_mode(), + length, transition_cost, time_restrictions_TODO, !ignore_access, + static_cast(flow_sources & baldr::kDefaultFlowMask), turn); // hand back the label label_cb(label); // next edge diff --git a/src/sif/transitcost.cc b/src/sif/transitcost.cc index 534e9b0af2..37e1603e1f 100644 --- a/src/sif/transitcost.cc +++ b/src/sif/transitcost.cc @@ -237,7 +237,7 @@ class TransitCost : public DynamicCost { } /**This method adds to the exclude list based on the - * user inputed exclude and include lists. + * user-provided exclude and include lists. */ virtual void AddToExcludeList(const graph_tile_ptr& tile) override; @@ -716,7 +716,8 @@ namespace { TransitCost* make_transitcost_from_json(const std::string& property, float testVal) { std::stringstream ss; - ss << R"({"costing_options":{"transit":{")" << property << R"(":)" << testVal << "}}}"; + ss << R"({"costing": "transit", "costing_options":{"transit":{")" << property << R"(":)" << testVal + << "}}}"; Api request; ParseApi(ss.str(), valhalla::Options::route, request); return new TransitCost(request.options().costings().find(Costing::transit)->second); diff --git a/src/sif/truckcost.cc b/src/sif/truckcost.cc index 4853aa415e..de4a8152c7 100644 --- a/src/sif/truckcost.cc +++ b/src/sif/truckcost.cc @@ -46,6 +46,8 @@ constexpr float kTCCrossing = 2.0f; constexpr float kTCUnfavorable = 2.5f; constexpr float kTCUnfavorableSharp = 3.5f; constexpr float kTCReverse = 9.5f; +constexpr float kTCRamp = 1.5f; +constexpr float kTCRoundabout = 0.5f; // Default truck attributes constexpr float kDefaultTruckWeight = 21.77f; // Metric Tons (48,000 lbs) @@ -53,7 +55,7 @@ constexpr float kDefaultTruckAxleLoad = 9.07f; // Metric Tons (20,000 lbs) constexpr float kDefaultTruckHeight = 4.11f; // Meters (13 feet 6 inches) constexpr float kDefaultTruckWidth = 2.6f; // Meters (102.36 inches) constexpr float kDefaultTruckLength = 21.64f; // Meters (71 feet) -constexpr uint8_t kDefaultAxleCount = 5; // 5 axles for above truck config +constexpr uint32_t kDefaultAxleCount = 5; // 5 axles for above truck config // Turn costs based on side of street driving constexpr float kRightSideTurnCosts[] = {kTCStraight, kTCSlight, kTCFavorable, @@ -65,6 +67,8 @@ constexpr float kLeftSideTurnCosts[] = {kTCStraight, kTCSlight, kTCUnfa // How much to favor truck routes. constexpr float kTruckRouteFactor = 0.85f; +constexpr float kDefaultUseTruckRoute = 0.0f; +constexpr float kMinNonTruckRouteFactor = 1.0f; constexpr float kHighwayFactor[] = { 1.0f, // Motorway @@ -88,15 +92,18 @@ constexpr float kSurfaceFactor[] = { }; // Valid ranges and defaults -constexpr ranged_default_t kLowClassPenaltyRange{0, kDefaultLowClassPenalty, kMaxPenalty}; -constexpr ranged_default_t kTruckWeightRange{0, kDefaultTruckWeight, 100.0f}; -constexpr ranged_default_t kTruckAxleLoadRange{0, kDefaultTruckAxleLoad, 40.0f}; -constexpr ranged_default_t kTruckHeightRange{0, kDefaultTruckHeight, 10.0f}; -constexpr ranged_default_t kTruckWidthRange{0, kDefaultTruckWidth, 10.0f}; -constexpr ranged_default_t kTruckLengthRange{0, kDefaultTruckLength, 50.0f}; -constexpr ranged_default_t kUseTollsRange{0, kDefaultUseTolls, 1.0f}; -constexpr ranged_default_t kAxleCountRange{2, kDefaultAxleCount, 20}; -constexpr ranged_default_t kUseHighwaysRange{0, kDefaultUseHighways, 1.0f}; +constexpr ranged_default_t kLowClassPenaltyRange{0.f, kDefaultLowClassPenalty, kMaxPenalty}; +constexpr ranged_default_t kTruckWeightRange{0.f, kDefaultTruckWeight, 100.0f}; +constexpr ranged_default_t kTruckAxleLoadRange{0.f, kDefaultTruckAxleLoad, 40.0f}; +constexpr ranged_default_t kTruckHeightRange{0.f, kDefaultTruckHeight, 10.0f}; +constexpr ranged_default_t kTruckWidthRange{0.f, kDefaultTruckWidth, 10.0f}; +constexpr ranged_default_t kTruckLengthRange{0.f, kDefaultTruckLength, 50.0f}; +constexpr ranged_default_t kUseTollsRange{0.f, kDefaultUseTolls, 1.0f}; +constexpr ranged_default_t kAxleCountRange{2, kDefaultAxleCount, 20}; +constexpr ranged_default_t kUseHighwaysRange{0.f, kDefaultUseHighways, 1.0f}; +constexpr ranged_default_t kTopSpeedRange{10.f, kMaxAssumedTruckSpeed, kMaxSpeedKph}; +constexpr ranged_default_t kHGVNoAccessRange{0.f, kMaxPenalty, kMaxPenalty}; +constexpr ranged_default_t kUseTruckRouteRange{0.f, kDefaultUseTruckRoute, 1.0f}; BaseCostingOptionsConfig GetBaseCostOptsConfig() { BaseCostingOptionsConfig cfg{}; @@ -269,6 +276,12 @@ class TruckCost : public DynamicCost { */ virtual uint8_t travel_type() const override; + /** + * Is the current vehicle type HGV? + * @return Returns whether it's a truck. + */ + virtual bool is_hgv() const override; + /** * Function to be used in location searching which will * exclude and allow ranking results from the search by looking at each @@ -286,24 +299,28 @@ class TruckCost : public DynamicCost { } public: - VehicleType type_; // Vehicle type: tractor trailer + VehicleType type_; // Vehicle type: truck std::vector speedfactor_; float density_factor_[16]; // Density factor float toll_factor_; // Factor applied when road has a toll float low_class_penalty_; // Penalty (seconds) to go to residential or service road // Vehicle attributes (used for special restrictions and costing) - bool hazmat_; // Carrying hazardous materials - float weight_; // Vehicle weight in metric tons - float axle_load_; // Axle load weight in metric tons - float height_; // Vehicle height in meters - float width_; // Vehicle width in meters - float length_; // Vehicle length in meters - float highway_factor_; // Factor applied when road is a motorway or trunk - uint8_t axle_count_; // Vehicle axle count + bool hazmat_; // Carrying hazardous materials + float weight_; // Vehicle weight in metric tons + float axle_load_; // Axle load weight in metric tons + float height_; // Vehicle height in meters + float width_; // Vehicle width in meters + float length_; // Vehicle length in meters + float highway_factor_; // Factor applied when road is a motorway or trunk + float non_truck_route_factor_; // Factor applied when road is not part of a designated truck route + uint8_t axle_count_; // Vehicle axle count // Density factor used in edge transition costing std::vector trans_density_factor_; + + // determine if we should allow hgv=no edges and penalize them instead + float no_hgv_access_penalty_; }; // Constructor @@ -313,12 +330,16 @@ TruckCost::TruckCost(const Costing& costing) 1.4f, 1.6f, 1.9f, 2.2f, 2.5f, 2.8f, 3.1f, 3.5f} { const auto& costing_options = costing.options(); - type_ = VehicleType::kTractorTrailer; + type_ = VehicleType::kTruck; // Get the base costs get_base_costs(costing); low_class_penalty_ = costing_options.low_class_penalty(); + non_truck_route_factor_ = + costing_options.use_truck_route() < 0.5f + ? kMinNonTruckRouteFactor + 2.f * costing_options.use_truck_route() + : ((kMinNonTruckRouteFactor - 5.f) + 12.f * costing_options.use_truck_route()); // Get the vehicle attributes hazmat_ = costing_options.hazmat(); @@ -361,6 +382,12 @@ TruckCost::TruckCost(const Costing& costing) for (uint32_t d = 0; d < 16; d++) { density_factor_[d] = 0.85f + (d * 0.025f); } + + // determine what to do with hgv=no edges + bool no_hgv_access_penalty_active = !(costing_options.hgv_no_access_penalty() == kMaxPenalty); + no_hgv_access_penalty_ = no_hgv_access_penalty_active * costing_options.hgv_no_access_penalty(); + // set the access mask to both car & truck if that penalty is active + access_mask_ = no_hgv_access_penalty_active ? (kAutoAccess | kTruckAccess) : kTruckAccess; } // Destructor @@ -432,9 +459,9 @@ inline bool TruckCost::Allowed(const baldr::DirectedEdge* edge, uint8_t& restriction_idx) const { // Check access, U-turn, and simple turn restriction. if (!IsAccessible(edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((pred.restrictions() & (1 << edge->localedgeidx())) && !ignore_restrictions_) || + ((pred.restrictions() & (1 << edge->localedgeidx())) && (!ignore_turn_restrictions_)) || edge->surface() == Surface::kImpassable || IsUserAvoidEdge(edgeid) || - (!allow_destination_only_ && !pred.destonly() && edge->destonly()) || + (!allow_destination_only_ && !pred.destonly() && edge->destonly_hgv()) || (pred.closure_pruning() && IsClosed(edge, tile)) || (exclude_unpaved_ && !pred.unpaved() && edge->unpaved())) { return false; @@ -456,16 +483,16 @@ bool TruckCost::AllowedReverse(const baldr::DirectedEdge* edge, uint8_t& restriction_idx) const { // Check access, U-turn, and simple turn restriction. if (!IsAccessible(opp_edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_restrictions_) || + ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_turn_restrictions_) || opp_edge->surface() == Surface::kImpassable || IsUserAvoidEdge(opp_edgeid) || - (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly()) || + (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly_hgv()) || (pred.closure_pruning() && IsClosed(opp_edge, tile)) || (exclude_unpaved_ && !pred.unpaved() && opp_edge->unpaved())) { return false; } - return DynamicCost::EvaluateRestrictions(access_mask_, edge, false, tile, opp_edgeid, current_time, - tz_index, restriction_idx); + return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, + current_time, tz_index, restriction_idx); } // Get the cost to traverse the edge in seconds @@ -478,7 +505,9 @@ Cost TruckCost::EdgeCost(const baldr::DirectedEdge* edge, &flow_sources, time_info.seconds_from_now) : fixed_speed_; - auto final_speed = std::min(edge_speed, top_speed_); + auto final_speed = + std::min(edge_speed, + edge->truck_speed() ? std::min(edge->truck_speed(), top_speed_) : top_speed_); float sec = edge->length() * speedfactor_[final_speed]; @@ -504,6 +533,8 @@ Cost TruckCost::EdgeCost(const baldr::DirectedEdge* edge, if (edge->truck_route() > 0) { factor *= kTruckRouteFactor; + } else { + factor *= non_truck_route_factor_; } if (edge->toll()) { @@ -534,7 +565,7 @@ Cost TruckCost::TransitionCost(const baldr::DirectedEdge* edge, // destination only, alley, maneuver penalty uint32_t idx = pred.opp_local_idx(); Cost c = base_transition_cost(node, edge, &pred, idx); - c.secs = OSRMCarTurnDuration(edge, node, idx); + c.secs += OSRMCarTurnDuration(edge, node, idx); // Penalty to transition onto low class roads. if (edge->classification() == baldr::RoadClass::kResidential || @@ -542,6 +573,10 @@ Cost TruckCost::TransitionCost(const baldr::DirectedEdge* edge, c.cost += low_class_penalty_; } + // Penalty if the request wants to avoid hgv=no edges instead of disallowing + c.cost += + no_hgv_access_penalty_ * (pred.has_hgv_access() && !(edge->forwardaccess() & kTruckAccess)); + // Transition time = turncost * stopimpact * densityfactor if (edge->stopimpact(idx) > 0 && !shortest_) { float turn_cost; @@ -555,9 +590,9 @@ Cost TruckCost::TransitionCost(const baldr::DirectedEdge* edge, if ((edge->use() != Use::kRamp && pred.use() == Use::kRamp) || (edge->use() == Use::kRamp && pred.use() != Use::kRamp)) { - turn_cost += 1.5f; + turn_cost += kTCRamp; if (edge->roundabout()) - turn_cost += 0.5f; + turn_cost += kTCRoundabout; } float seconds = turn_cost; @@ -607,7 +642,7 @@ Cost TruckCost::TransitionCostReverse(const uint32_t idx, // Get the transition cost for country crossing, ferry, gate, toll booth, // destination only, alley, maneuver penalty Cost c = base_transition_cost(node, edge, pred, idx); - c.secs = OSRMCarTurnDuration(edge, node, pred->opp_local_idx()); + c.secs += OSRMCarTurnDuration(edge, node, pred->opp_local_idx()); // Penalty to transition onto low class roads. if (edge->classification() == baldr::RoadClass::kResidential || @@ -615,6 +650,10 @@ Cost TruckCost::TransitionCostReverse(const uint32_t idx, c.cost += low_class_penalty_; } + // Penalty if the request wants to avoid hgv=no edges instead of disallowing + c.cost += no_hgv_access_penalty_ * + ((pred->forwardaccess() & kTruckAccess) && !(edge->forwardaccess() & kTruckAccess)); + // Transition time = turncost * stopimpact * densityfactor if (edge->stopimpact(idx) > 0 && !shortest_) { float turn_cost; @@ -628,9 +667,9 @@ Cost TruckCost::TransitionCostReverse(const uint32_t idx, if ((edge->use() != Use::kRamp && pred->use() == Use::kRamp) || (edge->use() == Use::kRamp && pred->use() != Use::kRamp)) { - turn_cost += 1.5f; + turn_cost += kTCRamp; if (edge->roundabout()) - turn_cost += 0.5f; + turn_cost += kTCRoundabout; } float seconds = turn_cost; @@ -678,6 +717,10 @@ uint8_t TruckCost::travel_type() const { return static_cast(type_); } +bool TruckCost::is_hgv() const { + return true; +} + void ParseTruckCostOptions(const rapidjson::Document& doc, const std::string& costing_options_key, Costing* c) { @@ -698,8 +741,11 @@ void ParseTruckCostOptions(const rapidjson::Document& doc, JSON_PBF_RANGED_DEFAULT(co, kTruckLengthRange, json, "/length", length); JSON_PBF_RANGED_DEFAULT(co, kUseTollsRange, json, "/use_tolls", use_tolls); JSON_PBF_RANGED_DEFAULT(co, kUseHighwaysRange, json, "/use_highways", use_highways); - co->set_axle_count( - kAxleCountRange(rapidjson::get(json, "/axle_count", co->axle_count()))); + JSON_PBF_RANGED_DEFAULT_V2(co, kAxleCountRange, json, "/axle_count", axle_count); + JSON_PBF_RANGED_DEFAULT(co, kTopSpeedRange, json, "/top_speed", top_speed); + JSON_PBF_RANGED_DEFAULT(co, kHGVNoAccessRange, json, "/hgv_no_access_penalty", + hgv_no_access_penalty); + JSON_PBF_RANGED_DEFAULT_V2(co, kUseTruckRouteRange, json, "/use_truck_route", use_truck_route); } cost_ptr_t CreateTruckCost(const Costing& costing_options) { @@ -735,7 +781,8 @@ class TestTruckCost : public TruckCost { TestTruckCost* make_truckcost_from_json(const std::string& property, float testVal) { std::stringstream ss; - ss << R"({"costing_options":{"truck":{")" << property << R"(":)" << testVal << "}}}"; + ss << R"({"costing": "truck", "costing_options":{"truck":{")" << property << R"(":)" << testVal + << "}}}"; Api request; ParseApi(ss.str(), valhalla::Options::route, request); return new TestTruckCost(request.options().costings().find(Costing::truck)->second); diff --git a/src/skadi/CMakeLists.txt b/src/skadi/CMakeLists.txt index 6e3a96c2ce..5b5cbe53a4 100644 --- a/src/skadi/CMakeLists.txt +++ b/src/skadi/CMakeLists.txt @@ -4,20 +4,17 @@ valhalla_module(NAME skadi SOURCES sample.cc util.cc - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib/lz4.c - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib/lz4hc.c - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib/lz4frame.c - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib/xxhash.c HEADERS ${headers} INCLUDE_DIRECTORIES PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> - PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib + SYSTEM_INCLUDE_DIRECTORIES + PUBLIC + $<$:${dirent_include_dir}> DEPENDS valhalla::baldr Boost::boost - ZLIB::ZLIB) + PkgConfig::ZLIB + PkgConfig::LZ4) diff --git a/src/skadi/sample.cc b/src/skadi/sample.cc index 082bd32d06..7e1cd5e906 100644 --- a/src/skadi/sample.cc +++ b/src/skadi/sample.cc @@ -2,15 +2,12 @@ #include #include -#include #include -#include #include #include #include -#include -#include #include +#include #include #include @@ -23,7 +20,7 @@ #include "valhalla/baldr/curl_tilegetter.h" namespace { -// srtmgl1 holds 1x1 degree tiles but oversamples the egde of the tile +// srtmgl1 holds 1x1 degree tiles but oversamples the edge of the tile // by .5 seconds on all sides. that means that the center of pixel 0 is // located at the tiles lat,lon (which is important for bilinear filtering) // it also means that there are 3601 pixels per row and per column @@ -303,7 +300,7 @@ struct cache_t { } // no need for synchronization as size is constant(set in constructor - // and never change after thatn) + // and never change after that) std::size_t size() const noexcept { return cache.size(); } diff --git a/src/thor/CMakeLists.txt b/src/thor/CMakeLists.txt index 966b015ebb..d486d202b2 100644 --- a/src/thor/CMakeLists.txt +++ b/src/thor/CMakeLists.txt @@ -1,39 +1,37 @@ file(GLOB headers ${VALHALLA_SOURCE_DIR}/valhalla/thor/*.h) set(sources - alternates.cc - triplegbuilder.cc - worker.cc) - -set(sources_with_warnings astar_bss.cc + alternates.cc bidirectional_astar.cc - centroid.cc costmatrix.cc dijkstras.cc + matrix_action.cc + multimodal.cc + route_action.cc + timedistancebssmatrix.cc + timedistancematrix.cc + triplegbuilder.cc + unidirectional_astar.cc + worker.cc) + +set(sources_with_warnings + centroid.cc expansion_action.cc isochrone_action.cc isochrone.cc map_matcher.cc - matrix_action.cc - multimodal.cc optimized_route_action.cc optimizer.cc - route_action.cc route_matcher.cc status_action.cc - timedistancematrix.cc - timedistancebssmatrix.cc trace_attributes_action.cc trace_route_action.cc - triplegbuilder_utils.h - unidirectional_astar.cc) + triplegbuilder_utils.h) -# treat date library as system -set(system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include) -if(APPLE) - list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) -endif() +set(system_includes + ${date_include_dir} + $<$:${dirent_include_dir}>) valhalla_module(NAME thor SOURCES ${sources} @@ -43,18 +41,19 @@ valhalla_module(NAME thor PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include ${CMAKE_BINARY_DIR} SYSTEM_INCLUDE_DIRECTORIES PUBLIC ${system_includes} + PRIVATE + ${rapidjson_include_dir} + ${robinhoodhashing_include_dir} + DEPENDS valhalla::proto valhalla::sif valhalla::meili ${valhalla_protobuf_targets} Boost::boost - libprime_server - robin_hood::robin_hood) + ${libprime_server_targets}) \ No newline at end of file diff --git a/src/thor/alternates.cc b/src/thor/alternates.cc index 9afb86a84b..46ee725b1c 100644 --- a/src/thor/alternates.cc +++ b/src/thor/alternates.cc @@ -1,4 +1,3 @@ -#include #include #include "thor/alternates.h" @@ -10,7 +9,7 @@ using namespace valhalla::midgard; /* * Viability tests for alternate paths based on M. Kobitzsch's Alternative Route * Techniques (2015). Tests verify limited sharing between segments, bounded - * stretch, and local optimiality. Any candidate path that meets all the criteria + * stretch, and local optimality. Any candidate path that meets all the criteria * may be considered a valid alternate to the shortest path. */ namespace { diff --git a/src/thor/astar_bss.cc b/src/thor/astar_bss.cc index 4d004233d9..e4887b0cfa 100644 --- a/src/thor/astar_bss.cc +++ b/src/thor/astar_bss.cc @@ -2,8 +2,6 @@ #include "baldr/datetime.h" #include "midgard/logging.h" #include -#include // TODO remove if not needed -#include using namespace valhalla::baldr; using namespace valhalla::sif; @@ -29,9 +27,8 @@ constexpr uint32_t kMaxIterationsWithoutConvergence = 200000; AStarBSSAlgorithm::AStarBSSAlgorithm(const boost::property_tree::ptree& config) : PathAlgorithm(config.get("max_reserved_labels_count_astar", kInitialEdgeLabelCountAstar), - config.get("clear_reserved_memory", false)), - max_label_count_(std::numeric_limits::max()), mode_(travel_mode_t::kDrive), - travel_type_(0) { + config.get("clear_reserved_memory", false)) { + mode_ = travel_mode_t::kDrive; } // Destructor @@ -99,7 +96,7 @@ void AStarBSSAlgorithm::Init(const midgard::PointLL& origll, const midgard::Poin // edges if from_transition is false. void AStarBSSAlgorithm::ExpandForward(GraphReader& graphreader, const GraphId& node, - const EdgeLabel& pred, + const BDEdgeLabel& pred, const uint32_t pred_idx, const bool from_transition, const bool from_bss, @@ -195,7 +192,7 @@ void AStarBSSAlgorithm::ExpandForward(GraphReader& graphreader, // less cost the predecessor is updated and the sort cost is decremented // by the difference in real cost (A* heuristic doesn't change) if (current_es->set() == EdgeSet::kTemporary) { - EdgeLabel& lab = edgelabels_[current_es->index()]; + auto& lab = edgelabels_[current_es->index()]; if (newcost.cost < lab.cost().cost) { float newsortcost = lab.sortcost() - (lab.cost().cost - newcost.cost); adjacencylist_.decrease(current_es->index(), newsortcost); @@ -221,9 +218,9 @@ void AStarBSSAlgorithm::ExpandForward(GraphReader& graphreader, // Add to the adjacency list and edge labels. uint32_t idx = edgelabels_.size(); - edgelabels_.emplace_back(pred_idx, edgeid, directededge, newcost, sortcost, dist, mode, 0, - transition_cost, baldr::kInvalidRestriction, true, false, - InternalTurn::kNoTurn); + edgelabels_.emplace_back(pred_idx, edgeid, GraphId(), directededge, newcost, sortcost, dist, mode, + transition_cost, false, false, false, InternalTurn::kNoTurn, + baldr::kInvalidRestriction); *current_es = {EdgeSet::kTemporary, idx}; adjacencylist_.add(idx); } @@ -271,27 +268,19 @@ AStarBSSAlgorithm::GetBestPath(valhalla::Location& origin, // Initialize the origin and destination locations. Initialize the // destination first in case the origin edge includes a destination edge. - uint32_t density = SetDestination(graphreader, destination); + SetDestination(graphreader, destination); SetOrigin(graphreader, origin, destination); // Find shortest path uint32_t nc = 0; // Count of iterations with no convergence // towards destination std::pair best_path = std::make_pair(-1, 0.0f); - size_t total_labels = 0; + size_t n = 0; while (true) { // Allow this process to be aborted - size_t current_labels = edgelabels_.size(); - if (interrupt && - total_labels / kInterruptIterationsInterval < current_labels / kInterruptIterationsInterval) { + if (interrupt && (++n % kInterruptIterationsInterval) == 0) { (*interrupt)(); } - total_labels = current_labels; - - // Abort if max label count is exceeded - if (total_labels > max_label_count_) { - return {}; - } // Get next element from adjacency list. Check that it is valid. An // invalid label indicates there are no edges that can be expanded. @@ -303,10 +292,7 @@ AStarBSSAlgorithm::GetBestPath(valhalla::Location& origin, // Copy the EdgeLabel for use in costing. Check if this is a destination // edge and potentially complete the path. - EdgeLabel pred = edgelabels_[predindex]; - - graph_tile_ptr tile = graphreader.GetGraphTile(pred.endnode()); - auto ll = tile->get_node_ll(pred.endnode()); + auto pred = edgelabels_[predindex]; if (destinations_.find(pred.edgeid()) != destinations_.end() && pred.mode() == travel_mode_t::kPedestrian) { @@ -457,11 +443,12 @@ void AStarBSSAlgorithm::SetOrigin(GraphReader& graphreader, // Set the predecessor edge index to invalid to indicate the origin // of the path. uint32_t d = static_cast(directededge->length() * (1.0f - edge.percent_along())); - EdgeLabel edge_label(kInvalidLabel, edgeid, directededge, cost, sortcost, dist, - travel_mode_t::kPedestrian, d, Cost{}, baldr::kInvalidRestriction, true, - false, sif::InternalTurn::kNoTurn); - // Set the origin flag + BDEdgeLabel edge_label(kInvalidLabel, edgeid, directededge, cost, sortcost, dist, + travel_mode_t::kPedestrian, baldr::kInvalidRestriction, false, false, + sif::InternalTurn::kNoTurn); + // Set the origin flag and path distance edge_label.set_origin(); + edge_label.set_path_distance(d); // Add EdgeLabel to the adjacency list uint32_t idx = edgelabels_.size(); @@ -479,7 +466,7 @@ void AStarBSSAlgorithm::SetOrigin(GraphReader& graphreader, } // Add a destination edge -uint32_t AStarBSSAlgorithm::SetDestination(GraphReader& graphreader, const valhalla::Location& dest) { +void AStarBSSAlgorithm::SetDestination(GraphReader& graphreader, const valhalla::Location& dest) { // Only skip outbound edges if we have other options bool has_other_edges = false; @@ -488,8 +475,6 @@ uint32_t AStarBSSAlgorithm::SetDestination(GraphReader& graphreader, const valha has_other_edges = has_other_edges || !e.begin_node(); }); - // For each edge - uint32_t density = 0; for (const auto& edge : dest.correlation().edges()) { // If destination is at a node skip any outbound edges if (has_other_edges && edge.begin_node()) { @@ -512,11 +497,8 @@ uint32_t AStarBSSAlgorithm::SetDestination(GraphReader& graphreader, const valha pedestrian_costing_->EdgeCost(directededge, tile) * (1.0f - edge.percent_along()); // Edge score (penalty) is handled within GetPath. Do not add score here. - - // Get the tile relative density - density = tile->header()->density(); } - return density; + return; } // Form the path from the adjacency list. @@ -529,18 +511,16 @@ std::vector AStarBSSAlgorithm::FormPath(baldr::GraphReader& graphreade // Work backwards from the destination std::vector path; travel_mode_t old = travel_mode_t::kPedestrian; - int mode_change_count = 0; + [[maybe_unused]] int mode_change_count = 0; for (auto edgelabel_index = dest; edgelabel_index != kInvalidLabel; edgelabel_index = edgelabels_[edgelabel_index].predecessor()) { - const EdgeLabel& edgelabel = edgelabels_[edgelabel_index]; + const auto& edgelabel = edgelabels_[edgelabel_index]; path.emplace_back(edgelabel.mode(), edgelabel.cost(), edgelabel.edgeid(), 0, edgelabel.path_distance(), edgelabel.restriction_idx(), edgelabel.transition_cost()); graph_tile_ptr tile = graphreader.GetGraphTile(edgelabel.edgeid()); - const DirectedEdge* directededge = tile->directededge(edgelabel.edgeid()); - auto ll = tile->get_node_ll(directededge->endnode()); // Check if this is a ferry if (edgelabel.use() == Use::kFerry) { diff --git a/src/thor/bidirectional_astar.cc b/src/thor/bidirectional_astar.cc index 6dffe52edd..faae829ae6 100644 --- a/src/thor/bidirectional_astar.cc +++ b/src/thor/bidirectional_astar.cc @@ -90,7 +90,7 @@ void BidirectionalAStar::Clear() { // Set the ferry flag to false has_ferry_ = false; // Set not thru pruning to true - not_thru_pruning_ = true; + set_not_thru_pruning(true); // reset origin & destination pruning states pruning_disabled_at_origin_ = false; pruning_disabled_at_destination_ = false; @@ -107,8 +107,8 @@ void BidirectionalAStar::Init(const PointLL& origll, const PointLL& destll) { // Reserve size for edge labels - do this here rather than in constructor so // to limit how much extra memory is used for persistent objects - edgelabels_forward_.reserve(kInitialEdgeLabelCountBidirAstar); - edgelabels_reverse_.reserve(kInitialEdgeLabelCountBidirAstar); + edgelabels_forward_.reserve(max_reserved_labels_count_); + edgelabels_reverse_.reserve(max_reserved_labels_count_); // Construct adjacency list and initialize edge status lookup. // Set bucket size and cost range based on DynamicCost. @@ -136,23 +136,14 @@ void BidirectionalAStar::Init(const PointLL& origll, const PointLL& destll) { cost_threshold_ = std::numeric_limits::max(); iterations_threshold_ = std::numeric_limits::max(); - // Support for hierarchy transitions - hierarchy_limits_forward_ = costing_->GetHierarchyLimits(); - hierarchy_limits_reverse_ = costing_->GetHierarchyLimits(); - bool ignore_forward_limits = - std::all_of(hierarchy_limits_forward_.begin() + 1, - hierarchy_limits_forward_.begin() + TileHierarchy::levels().size(), - [](const HierarchyLimits& limits) { - return limits.max_up_transitions == kUnlimitedTransitions; - }); - bool ignore_reverse_limits = - std::all_of(hierarchy_limits_reverse_.begin() + 1, - hierarchy_limits_reverse_.begin() + TileHierarchy::levels().size(), - [](const HierarchyLimits& limits) { - return limits.max_up_transitions == kUnlimitedTransitions; - }); - // Set this flag to 'true' if we can expand edges at all hierarchy levels without limits - ignore_hierarchy_limits_ = ignore_forward_limits && ignore_reverse_limits; + auto& hierarchy_limits = costing_->GetHierarchyLimits(); + ignore_hierarchy_limits_ = std::all_of(hierarchy_limits.begin() + 1, + hierarchy_limits.begin() + TileHierarchy::levels().size(), + [](const HierarchyLimits& limits) { + return limits.max_up_transitions == kUnlimitedTransitions; + }); + hierarchy_limits_forward_ = hierarchy_limits; + hierarchy_limits_reverse_ = hierarchy_limits; } // Runs in the inner loop of `Expand`, essentially evaluating if @@ -246,7 +237,7 @@ inline bool BidirectionalAStar::ExpandInner(baldr::GraphReader& graphreader, // or if a complex restriction prevents transition onto this edge. // if its not time dependent set to 0 for Allowed and Restricted methods below const uint64_t localtime = time_info.valid ? time_info.local_time : 0; - uint8_t restriction_idx = -1; + uint8_t restriction_idx = kInvalidRestriction; if (FORWARD) { // Why is is_dest false? // We have to consider next cases: @@ -318,7 +309,9 @@ inline bool BidirectionalAStar::ExpandInner(baldr::GraphReader& graphreader, : astarheuristic_reverse_.Get(t2->get_node_ll(meta.edge->endnode()), dist)); // not_thru_pruning_ is only set to false on the 2nd pass in route_action. - bool thru = not_thru_pruning_ ? (pred.not_thru_pruning() || !meta.edge->not_thru()) : false; + // We allow settling not_thru edges so we can connect both trees on them. + bool not_thru_pruning = + not_thru_pruning_ ? (pred.not_thru_pruning() || !meta.edge->not_thru()) : false; // Add edge label, add to the adjacency list and set edge status uint32_t idx = 0; @@ -330,11 +323,14 @@ inline bool BidirectionalAStar::ExpandInner(baldr::GraphReader& graphreader, dist = astarheuristic_reverse_.GetDistance(t2->get_node_ll(meta.edge->endnode())); } edgelabels_forward_.emplace_back(pred_idx, meta.edge_id, opp_edge_id, meta.edge, newcost, - sortcost, dist, mode_, transition_cost, thru, + sortcost, dist, mode_, transition_cost, not_thru_pruning, (pred.closure_pruning() || !costing_->IsClosed(meta.edge, tile)), static_cast(flow_sources & kDefaultFlowMask), costing_->TurnType(pred.opp_local_idx(), nodeinfo, meta.edge), - restriction_idx); + restriction_idx, 0, + meta.edge->destonly() || + (costing_->is_hgv() && meta.edge->destonly_hgv()), + meta.edge->forwardaccess() & kTruckAccess); adjacencylist_forward_.add(idx); } else { idx = edgelabels_reverse_.size(); @@ -344,12 +340,15 @@ inline bool BidirectionalAStar::ExpandInner(baldr::GraphReader& graphreader, dist = astarheuristic_forward_.GetDistance(t2->get_node_ll(meta.edge->endnode())); } edgelabels_reverse_.emplace_back(pred_idx, meta.edge_id, opp_edge_id, meta.edge, newcost, - sortcost, dist, mode_, transition_cost, thru, - (pred.closure_pruning() || !costing_->IsClosed(meta.edge, tile)), + sortcost, dist, mode_, transition_cost, not_thru_pruning, + (pred.closure_pruning() || !costing_->IsClosed(opp_edge, t2)), static_cast(flow_sources & kDefaultFlowMask), costing_->TurnType(meta.edge->localedgeidx(), nodeinfo, opp_edge, opp_pred_edge), - restriction_idx); + restriction_idx, 0, + opp_edge->destonly() || + (costing_->is_hgv() && opp_edge->destonly_hgv()), + opp_edge->forwardaccess() & kTruckAccess); adjacencylist_reverse_.add(idx); } @@ -357,8 +356,14 @@ inline bool BidirectionalAStar::ExpandInner(baldr::GraphReader& graphreader, // setting this edge as reached if (expansion_callback_) { - expansion_callback_(graphreader, FORWARD ? meta.edge_id : opp_edge_id, "bidirectional_astar", "r", - pred.cost().secs, pred.path_distance(), pred.cost().cost); + const auto& prev_pred = + pred.predecessor() == kInvalidLabel + ? GraphId{} + : (FORWARD ? edgelabels_forward_ : edgelabels_reverse_)[pred.predecessor()].edgeid(); + expansion_callback_(graphreader, FORWARD ? meta.edge_id : opp_edge_id, prev_pred, + "bidirectional_astar", Expansion_EdgeStatus_reached, newcost.secs, + pred.path_distance() + meta.edge->length(), newcost.cost, + static_cast(expansion_direction)); } // we've just added this edge to the queue, but we won't expand from it if it's a not-thru edge that @@ -367,7 +372,7 @@ inline bool BidirectionalAStar::ExpandInner(baldr::GraphReader& graphreader, } template -bool BidirectionalAStar::Expand(baldr::GraphReader& graphreader, +void BidirectionalAStar::Expand(baldr::GraphReader& graphreader, const baldr::GraphId& node, sif::BDEdgeLabel& pred, const uint32_t pred_idx, @@ -379,7 +384,7 @@ bool BidirectionalAStar::Expand(baldr::GraphReader& graphreader, // with regional data sets) or if no access at the node. graph_tile_ptr tile = graphreader.GetGraphTile(node); if (tile == nullptr) { - return false; + return; } const NodeInfo* nodeinfo = tile->node(node); @@ -403,11 +408,12 @@ bool BidirectionalAStar::Expand(baldr::GraphReader& graphreader, // is labelled pred.set_deadend(true); // Check if edge is null before using it (can happen with regional data sets) - return opp_edge && - ExpandInner(graphreader, pred, opp_pred_edge, nodeinfo, pred_idx, - {opp_edge, opp_edge_id, - edgestatus.GetPtr(opp_edge_id, tile)}, - shortcuts, tile, offset_time); + if (opp_edge) { + ExpandInner(graphreader, pred, opp_pred_edge, nodeinfo, pred_idx, + {opp_edge, opp_edge_id, edgestatus.GetPtr(opp_edge_id, tile)}, + shortcuts, tile, offset_time); + } + return; } bool disable_uturn = false; @@ -421,14 +427,14 @@ bool BidirectionalAStar::Expand(baldr::GraphReader& graphreader, // If so, it means we are attempting a u-turn. In that case, lets wait with evaluating // this edge until last. If any other edges were emplaced, it means we should not // even try to evaluate a u-turn since u-turns should only happen for deadends - uturn_meta = pred.opp_local_idx() == meta.edge->localedgeidx() ? meta : uturn_meta; + bool is_uturn = pred.opp_local_idx() == meta.edge->localedgeidx(); + uturn_meta = is_uturn ? meta : uturn_meta; // Expand but only if this isnt the uturn, we'll try that later if nothing else works out - disable_uturn = - (pred.opp_local_idx() != meta.edge->localedgeidx() && - ExpandInner(graphreader, pred, opp_pred_edge, nodeinfo, pred_idx, meta, - shortcuts, tile, offset_time)) || - disable_uturn; + disable_uturn = (!is_uturn && ExpandInner(graphreader, pred, opp_pred_edge, + nodeinfo, pred_idx, meta, + shortcuts, tile, offset_time)) || + disable_uturn; } // Handle transitions - expand from the end node of each transition @@ -471,14 +477,12 @@ bool BidirectionalAStar::Expand(baldr::GraphReader& graphreader, // TODO Is there a shortcut that supersedes our u-turn? // Decide if we should expand a shortcut or the non-shortcut edge... - // Expand the uturn possiblity - disable_uturn = - ExpandInner(graphreader, pred, opp_pred_edge, nodeinfo, pred_idx, - uturn_meta, shortcuts, tile, offset_time) || - disable_uturn; + // Expand the uturn possibility + ExpandInner(graphreader, pred, opp_pred_edge, nodeinfo, pred_idx, uturn_meta, + shortcuts, tile, offset_time); } - return disable_uturn; + return; } // Calculate best path using bi-directional A*. No hierarchies or time @@ -544,7 +548,7 @@ BidirectionalAStar::GetBestPath(valhalla::Location& origin, // portion of the graph) rather than strictly alternating. // TODO - CostMatrix alternates, maybe should try alternating here? int n = 0; - uint32_t forward_pred_idx, reverse_pred_idx; + uint32_t forward_pred_idx{0}, reverse_pred_idx{0}; BDEdgeLabel fwd_pred, rev_pred; bool expand_forward = true; bool expand_reverse = true; @@ -581,8 +585,7 @@ BidirectionalAStar::GetBestPath(valhalla::Location& origin, if (opp_status.set() == EdgeSet::kPermanent || (opp_status.set() == EdgeSet::kTemporary && edgelabels_reverse_[opp_status.index()].predecessor() == kInvalidLabel)) { - if (SetForwardConnection(graphreader, fwd_pred) && - opp_status.set() == EdgeSet::kPermanent) { + if (SetForwardConnection(graphreader, fwd_pred)) { continue; } } @@ -631,8 +634,7 @@ BidirectionalAStar::GetBestPath(valhalla::Location& origin, if (opp_status.set() == EdgeSet::kPermanent || (opp_status.set() == EdgeSet::kTemporary && edgelabels_forward_[opp_status.index()].predecessor() == kInvalidLabel)) { - if (SetReverseConnection(graphreader, rev_pred) && - opp_status.set() == EdgeSet::kPermanent) { + if (SetReverseConnection(graphreader, rev_pred)) { continue; } } @@ -703,8 +705,13 @@ BidirectionalAStar::GetBestPath(valhalla::Location& origin, // setting this edge as settled if (expansion_callback_) { - expansion_callback_(graphreader, fwd_pred.edgeid(), "bidirectional_astar", "s", - fwd_pred.cost().secs, fwd_pred.path_distance(), fwd_pred.cost().cost); + const auto prev_pred = fwd_pred.predecessor() == kInvalidLabel + ? GraphId{} + : edgelabels_forward_[fwd_pred.predecessor()].edgeid(); + expansion_callback_(graphreader, fwd_pred.edgeid(), prev_pred, "bidirectional_astar", + Expansion_EdgeStatus_settled, fwd_pred.cost().secs, + fwd_pred.path_distance(), fwd_pred.cost().cost, + Expansion_ExpansionType_forward); } // Prune path if predecessor is not a through edge or if the maximum @@ -747,8 +754,13 @@ BidirectionalAStar::GetBestPath(valhalla::Location& origin, // setting this edge as settled, sending the opposing because this is the reverse tree if (expansion_callback_) { - expansion_callback_(graphreader, rev_pred.opp_edgeid(), "bidirectional_astar", "s", - rev_pred.cost().secs, rev_pred.path_distance(), rev_pred.cost().cost); + const auto prev_pred = rev_pred.predecessor() == kInvalidLabel + ? GraphId{} + : edgelabels_reverse_[rev_pred.predecessor()].edgeid(); + expansion_callback_(graphreader, rev_pred.opp_edgeid(), prev_pred, "bidirectional_astar", + Expansion_EdgeStatus_settled, rev_pred.cost().secs, + rev_pred.path_distance(), rev_pred.cost().cost, + Expansion_ExpansionType_reverse); } // Prune path if predecessor is not a through edge @@ -859,8 +871,12 @@ bool BidirectionalAStar::SetForwardConnection(GraphReader& graphreader, const BD // setting this edge as connected if (expansion_callback_) { - expansion_callback_(graphreader, pred.edgeid(), "bidirectional_astar", "c", pred.cost().secs, - pred.path_distance(), pred.cost().cost); + const auto prev_pred = pred.predecessor() == kInvalidLabel + ? GraphId{} + : edgelabels_forward_[pred.predecessor()].edgeid(); + expansion_callback_(graphreader, pred.edgeid(), prev_pred, "bidirectional_astar", + Expansion_EdgeStatus_connected, pred.cost().secs, pred.path_distance(), + pred.cost().cost, Expansion_ExpansionType_forward); } return true; @@ -928,8 +944,13 @@ bool BidirectionalAStar::SetReverseConnection(GraphReader& graphreader, const BD // setting this edge as connected, sending the opposing because this is the reverse tree if (expansion_callback_) { - expansion_callback_(graphreader, fwd_edge_id, "bidirectional_astar", "c", fwd_pred.cost().secs, - fwd_pred.path_distance(), fwd_pred.cost().cost); + const auto prev_pred = fwd_pred.predecessor() == kInvalidLabel + ? GraphId{} + : edgelabels_forward_[fwd_pred.predecessor()].edgeid(); + expansion_callback_(graphreader, fwd_edge_id, prev_pred, "bidirectional_astar", + Expansion_EdgeStatus_connected, fwd_pred.cost().secs, + fwd_pred.path_distance(), fwd_pred.cost().cost, + Expansion_ExpansionType_reverse); } return true; @@ -1002,24 +1023,29 @@ void BidirectionalAStar::SetOrigin(GraphReader& graphreader, dist = astarheuristic_reverse_.GetDistance(nodeinfo->latlng(endtile->header()->base_ll())); } edgelabels_forward_.emplace_back(kInvalidLabel, edgeid, directededge, cost, sortcost, dist, mode_, - -1, !(costing_->IsClosed(directededge, tile)), + kInvalidRestriction, !(costing_->IsClosed(directededge, tile)), static_cast(flow_sources & kDefaultFlowMask), - sif::InternalTurn::kNoTurn); + sif::InternalTurn::kNoTurn, 0, + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); adjacencylist_forward_.add(idx); // setting this edge as reached if (expansion_callback_) { - expansion_callback_(graphreader, edgeid, "bidirectional_astar", "r", cost.secs, edge.distance(), - cost.cost); + expansion_callback_(graphreader, edgeid, GraphId{}, "bidirectional_astar", + Expansion_EdgeStatus_reached, cost.secs, + static_cast(edge.distance() + 0.5), cost.cost, + Expansion_ExpansionType_forward); } // Set the initial not_thru flag to false. There is an issue with not_thru // flags on small loops. Set this to false here to override this for now. edgelabels_forward_.back().set_not_thru(false); - pruning_disabled_at_origin_ = pruning_disabled_at_origin_ || - !edgelabels_forward_.back().closure_pruning() || - !edgelabels_forward_.back().not_thru_pruning(); + pruning_disabled_at_origin_ = + pruning_disabled_at_origin_ || !edgelabels_forward_.back().closure_pruning() || + !edgelabels_forward_.back().not_thru_pruning() || edgelabels_forward_.back().destonly(); } // Set the origin timezone @@ -1098,22 +1124,27 @@ void BidirectionalAStar::SetDestination(GraphReader& graphreader, dist, mode_, c, !opp_dir_edge->not_thru(), !(costing_->IsClosed(directededge, tile)), static_cast(flow_sources & kDefaultFlowMask), - sif::InternalTurn::kNoTurn, -1); + sif::InternalTurn::kNoTurn, kInvalidRestriction, + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); adjacencylist_reverse_.add(idx); // setting this edge as reached, sending the opposing because this is the reverse tree if (expansion_callback_) { - expansion_callback_(graphreader, edgeid, "bidirectional_astar", "r", cost.secs, edge.distance(), - cost.cost); + expansion_callback_(graphreader, edgeid, GraphId{}, "bidirectional_astar", + Expansion_EdgeStatus_reached, cost.secs, + static_cast(edge.distance() + 0.5), cost.cost, + Expansion_ExpansionType_reverse); } // Set the initial not_thru flag to false. There is an issue with not_thru // flags on small loops. Set this to false here to override this for now. edgelabels_reverse_.back().set_not_thru(false); - pruning_disabled_at_destination_ = pruning_disabled_at_destination_ || - !edgelabels_reverse_.back().closure_pruning() || - !edgelabels_reverse_.back().not_thru_pruning(); + pruning_disabled_at_destination_ = + pruning_disabled_at_destination_ || !edgelabels_reverse_.back().closure_pruning() || + !edgelabels_reverse_.back().not_thru_pruning() || edgelabels_reverse_.back().destonly(); } } @@ -1176,8 +1207,8 @@ std::vector> BidirectionalAStar::FormPath(GraphReader& gra uint32_t idx2 = edgestatus_reverse_.Get(best_connection->opp_edgeid).index(); // Metrics (TODO - more accurate cost) - uint32_t pathcost = edgelabels_forward_[idx1].cost().cost + edgelabels_reverse_[idx2].cost().cost; - LOG_DEBUG("path_cost::" + std::to_string(pathcost)); + LOG_DEBUG("path_cost::" + std::to_string(edgelabels_forward_[idx1].cost().cost + + edgelabels_reverse_[idx2].cost().cost)); LOG_DEBUG("FormPath path_iterations::" + std::to_string(edgelabels_forward_.size()) + "," + std::to_string(edgelabels_reverse_.size())); @@ -1256,7 +1287,7 @@ std::vector> BidirectionalAStar::FormPath(GraphReader& gra return (edge_itr == path_edges.end()) ? GraphId{} : (*edge_itr++); }; - const auto label_cb = [&path, &recovered_inner_edges](const EdgeLabel& label) { + const auto label_cb = [&path, &recovered_inner_edges](const PathEdgeLabel& label) { path.emplace_back(label.mode(), label.cost(), label.edgeid(), 0, label.path_distance(), label.restriction_idx(), label.transition_cost(), recovered_inner_edges.count(label.edgeid())); @@ -1365,7 +1396,7 @@ bool IsBridgingEdgeRestricted(GraphReader& graphreader, // Check for double u-turn. It might happen in the following case: // forward and reverse searches traverse edges that belong to the same complex // restriction. Then forward/reverse search reaches last edge and due to the - // restriction can't go futher and make a u-turn. After that forward and reverse + // restriction can't go further and make a u-turn. After that forward and reverse // searches meet at some edge and compose double u-turn. if (std::find(patch_path.begin(), patch_path.end(), next_rev_pred.opp_edgeid()) != patch_path.end()) diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index edd794aa45..4c1cf16a5c 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -2,6 +2,8 @@ #include #include +#include "baldr/datetime.h" +#include "midgard/encoded.h" #include "midgard/logging.h" #include "sif/recost.h" #include "thor/costmatrix.h" @@ -16,11 +18,12 @@ namespace { constexpr uint32_t kMaxMatrixIterations = 2000000; constexpr uint32_t kMaxThreshold = std::numeric_limits::max(); +constexpr uint32_t kMaxLocationReservation = 25; // the default config for max matrix locations // Find a threshold to continue the search - should be based on // the max edge cost in the adjacency set? int GetThreshold(const travel_mode_t mode, const int n) { - return (mode == travel_mode_t::kDrive) ? std::min(2700, std::max(100, n / 3)) : 500; + return (mode == travel_mode_t::kDrive) ? std::min(2800, std::max(100, n / 3)) : 500; } bool equals(const valhalla::LatLng& a, const valhalla::LatLng& b) { @@ -28,10 +31,11 @@ bool equals(const valhalla::LatLng& a, const valhalla::LatLng& b) { (!a.has_lat_case() || a.lat() == b.lat()) && (!a.has_lng_case() || a.lng() == b.lng()); } -inline float find_percent_along(const valhalla::Location& location, const GraphId& edge_id) { +inline const valhalla::PathEdge& find_correlated_edge(const valhalla::Location& location, + const GraphId& edge_id) { for (const auto& e : location.correlation().edges()) { if (e.graph_id() == edge_id) - return static_cast(e.percent_along()); + return e; } throw std::logic_error("Could not find candidate edge used for label"); @@ -41,179 +45,189 @@ inline float find_percent_along(const valhalla::Location& location, const GraphI namespace valhalla { namespace thor { -class CostMatrix::TargetMap : public robin_hood::unordered_map> {}; +class CostMatrix::ReachedMap : public robin_hood::unordered_map> {}; // Constructor with cost threshold. CostMatrix::CostMatrix(const boost::property_tree::ptree& config) - : mode_(travel_mode_t::kDrive), access_mode_(kAutoAccess), source_count_(0), - remaining_sources_(0), target_count_(0), remaining_targets_(0), - current_cost_threshold_(0), targets_{new TargetMap}, + : MatrixAlgorithm(config), max_reserved_labels_count_(config.get("max_reserved_labels_count_bidir_dijkstras", - kInitialEdgeLabelCountBidirDijkstra)) { + kInitialEdgeLabelCountBidirDijkstra)), + max_reserved_locations_count_( + config.get("max_reserved_locations_costmatrix", kMaxLocationReservation)), + check_reverse_connections_(config.get("costmatrix_check_reverse_connection", false)), + access_mode_(kAutoAccess), + mode_(travel_mode_t::kDrive), locs_count_{0, 0}, locs_remaining_{0, 0}, + current_pathdist_threshold_(0), targets_{new ReachedMap}, sources_{new ReachedMap} { } CostMatrix::~CostMatrix() { } -float CostMatrix::GetCostThreshold(const float max_matrix_distance) { - float cost_threshold; - switch (mode_) { - case travel_mode_t::kBicycle: - cost_threshold = max_matrix_distance / kCostThresholdBicycleDivisor; - break; - case travel_mode_t::kPedestrian: - case travel_mode_t::kPublicTransit: - cost_threshold = max_matrix_distance / kCostThresholdPedestrianDivisor; - break; - case travel_mode_t::kDrive: - default: - cost_threshold = max_matrix_distance / kCostThresholdAutoDivisor; - } - - // Increase the cost threshold to make sure requests near the max distance succeed. - // Some costing models and locations require higher thresholds to succeed. - return cost_threshold * 2.0f; -} - // Clear the temporary information generated during time + distance matrix // construction. -void CostMatrix::clear() { +void CostMatrix::Clear() { // Clear the target edge markings - for (auto& iter : *targets_) { - iter.second.clear(); - iter.second.resize(0); - iter.second.shrink_to_fit(); - } targets_->clear(); + if (check_reverse_connections_) + sources_->clear(); - // Clear all source adjacency lists, edge labels, and edge status + // Clear all adjacency lists, edge labels, and edge status // Resize and shrink_to_fit so all capacity is reduced. - source_adjacency_.clear(); - source_adjacency_.resize(0); - source_adjacency_.shrink_to_fit(); - target_adjacency_.clear(); - target_adjacency_.resize(0); - target_adjacency_.shrink_to_fit(); - source_edgelabel_.clear(); - source_edgelabel_.resize(0); - source_edgelabel_.shrink_to_fit(); - target_edgelabel_.clear(); - target_edgelabel_.resize(0); - target_edgelabel_.shrink_to_fit(); - source_edgestatus_.clear(); - source_edgestatus_.resize(0); - source_edgestatus_.shrink_to_fit(); - target_edgestatus_.clear(); - target_edgestatus_.resize(0); - target_edgestatus_.shrink_to_fit(); - source_hierarchy_limits_.clear(); - source_hierarchy_limits_.resize(0); - source_hierarchy_limits_.shrink_to_fit(); - target_hierarchy_limits_.clear(); - target_hierarchy_limits_.resize(0); - target_hierarchy_limits_.shrink_to_fit(); - source_status_.clear(); - target_status_.clear(); + auto label_reservation = clear_reserved_memory_ ? 0 : max_reserved_labels_count_; + auto locs_reservation = clear_reserved_memory_ ? 0 : max_reserved_locations_count_; + for (const auto is_fwd : {MATRIX_FORW, MATRIX_REV}) { + // resize all relevant structures down to configured amount of locations (25 default) + if (locs_count_[is_fwd] > locs_reservation) { + edgelabel_[is_fwd].resize(locs_reservation); + edgelabel_[is_fwd].shrink_to_fit(); + adjacency_[is_fwd].resize(locs_reservation); + adjacency_[is_fwd].shrink_to_fit(); + edgestatus_[is_fwd].resize(locs_reservation); + edgestatus_[is_fwd].shrink_to_fit(); + astar_heuristics_[is_fwd].resize(locs_reservation); + astar_heuristics_[is_fwd].shrink_to_fit(); + } + for (auto& iter : edgelabel_[is_fwd]) { + if (iter.size() > label_reservation) { + iter.resize(label_reservation); + iter.shrink_to_fit(); + } + iter.clear(); + } + for (auto& iter : edgestatus_[is_fwd]) { + iter.clear(); + } + for (auto& iter : adjacency_[is_fwd]) { + iter.clear(); + } + hierarchy_limits_[is_fwd].clear(); + locs_status_[is_fwd].clear(); + astar_heuristics_[is_fwd].clear(); + } best_connection_.clear(); + set_not_thru_pruning(true); + ignore_hierarchy_limits_ = false; } // Form a time distance matrix from the set of source locations // to the set of target locations. -std::vector CostMatrix::SourceToTarget( - google::protobuf::RepeatedPtrField& source_location_list, - google::protobuf::RepeatedPtrField& target_location_list, - baldr::GraphReader& graphreader, - const sif::mode_costing_t& mode_costing, - const sif::travel_mode_t mode, - const float max_matrix_distance, - const bool has_time, - const bool invariant) { - - LOG_INFO("matrix::CostMatrix"); +bool CostMatrix::SourceToTarget(Api& request, + baldr::GraphReader& graphreader, + const sif::mode_costing_t& mode_costing, + const sif::travel_mode_t mode, + const float max_matrix_distance) { + request.mutable_matrix()->set_algorithm(Matrix::CostMatrix); + bool invariant = request.options().date_time_type() == Options::invariant; + auto shape_format = request.options().shape_format(); // Set the mode and costing mode_ = mode; costing_ = mode_costing[static_cast(mode_)]; access_mode_ = costing_->access_mode(); - current_cost_threshold_ = GetCostThreshold(max_matrix_distance); + auto& source_location_list = *request.mutable_options()->mutable_sources(); + auto& target_location_list = *request.mutable_options()->mutable_targets(); + + current_pathdist_threshold_ = max_matrix_distance / 2; auto time_infos = SetOriginTimes(source_location_list, graphreader); // Initialize best connections and status. Any locations that are the // same get set to 0 time, distance and are not added to the remaining // location set. - Initialize(source_location_list, target_location_list); + Initialize(source_location_list, target_location_list, request.matrix()); // Set the source and target locations // TODO: for now we only allow depart_at/current date_time SetSources(graphreader, source_location_list, time_infos); SetTargets(graphreader, target_location_list); + // Update hierarchy limits + if (!ignore_hierarchy_limits_) + ModifyHierarchyLimits(); + // Perform backward search from all target locations. Perform forward // search from all source locations. Connections between the 2 search // spaces is checked during the forward search. - int n = 0; + uint32_t n = 0; + uint32_t interrupt_n = 0; while (true) { - // Iterate all target locations in a backwards search - for (uint32_t i = 0; i < target_count_; i++) { - if (target_status_[i].threshold > 0) { - target_status_[i].threshold--; - BackwardSearch(i, graphreader); - // if we didn't see this - if (target_status_[i].threshold == 0) { - for (uint32_t source = 0; source < source_count_; source++) { - // Get all targets remaining for the origin - auto& targets = source_status_[source].remaining_locations; + // First iterate over all targets, then over all sources: we only for sure + // check the connection between both trees on the forward search, so reverse + // has to come first + for (uint32_t i = 0; i < locs_count_[MATRIX_REV]; i++) { + if (locs_status_[MATRIX_REV][i].threshold > 0) { + locs_status_[MATRIX_REV][i].threshold--; + Expand(i, n, graphreader, request.options()); + // if we exhausted this search + if (locs_status_[MATRIX_REV][i].threshold == 0) { + for (uint32_t source = 0; source < locs_count_[MATRIX_FORW]; source++) { + // update the target for each source's remaining targets, if it still exists + auto& targets = locs_status_[MATRIX_FORW][source].unfound_connections; auto it = targets.find(i); if (it != targets.end()) { + // remove the target so we don't come here again targets.erase(it); - if (targets.empty() && source_status_[source].threshold > 0) { - source_status_[i].threshold = -1; - if (remaining_sources_ > 0) { - remaining_sources_--; + // if there's no more target and the current source has not exhausted + // we update the source's threshold so that it doesn't enter its outer "if" statement + // anymore + if (targets.empty() && locs_status_[MATRIX_FORW][source].threshold > 0) { + // TODO(nils): shouldn't we extend the search here similar to bidir A* + // i.e. if pruning was disabled we extend the search in the other direction + locs_status_[MATRIX_FORW][source].threshold = -1; + if (locs_remaining_[MATRIX_FORW] > 0) { + locs_remaining_[MATRIX_FORW]--; } } } } - target_status_[i].threshold = -1; - if (remaining_targets_ > 0) { - remaining_targets_--; + // in any case make sure this was the last time we looked at this target + locs_status_[MATRIX_REV][i].threshold = -1; + if (locs_remaining_[MATRIX_REV] > 0) { + locs_remaining_[MATRIX_REV]--; } } } } - // Iterate all source locations in a forward search - for (uint32_t i = 0; i < source_count_; i++) { - if (source_status_[i].threshold > 0) { - source_status_[i].threshold--; - ForwardSearch(i, n, graphreader, time_infos[i], invariant); - if (source_status_[i].threshold == 0) { - for (uint32_t target = 0; target < target_count_; target++) { - // Get all sources remaining for the destination - auto& sources = target_status_[target].remaining_locations; + for (uint32_t i = 0; i < locs_count_[MATRIX_FORW]; i++) { + if (locs_status_[MATRIX_FORW][i].threshold > 0) { + locs_status_[MATRIX_FORW][i].threshold--; + Expand(i, n, graphreader, request.options(), time_infos[i], + invariant); + // if we exhausted this search + if (locs_status_[MATRIX_FORW][i].threshold == 0) { + for (uint32_t target = 0; target < locs_count_[MATRIX_REV]; target++) { + // if we still didn't find the connection between this pair + auto& sources = locs_status_[MATRIX_REV][target].unfound_connections; auto it = sources.find(i); if (it != sources.end()) { + // remove the source so we don't come here again sources.erase(it); - if (sources.empty() && target_status_[target].threshold > 0) { - target_status_[i].threshold = -1; - if (remaining_targets_ > 0) { - remaining_targets_--; + // if there's no more sources and the current target has not exhausted + // we update the target's threshold so that it doesn't enter this outer "if" statement + // anymore + if (sources.empty() && locs_status_[MATRIX_REV][target].threshold > 0) { + // TODO(nils): shouldn't we extend the search here similar to bidir A* + // i.e. if pruning was disabled we extend the search in the other direction + locs_status_[MATRIX_REV][target].threshold = -1; + if (locs_remaining_[MATRIX_REV] > 0) { + locs_remaining_[MATRIX_REV]--; } } } } - source_status_[i].threshold = -1; - if (remaining_sources_ > 0) { - remaining_sources_--; + // in any case make sure this was the last time we looked at this source + locs_status_[MATRIX_FORW][i].threshold = -1; + if (locs_remaining_[MATRIX_FORW] > 0) { + locs_remaining_[MATRIX_FORW]--; } } } } // Break out when remaining sources and targets to expand are both 0 - if (remaining_sources_ == 0 && remaining_targets_ == 0) { + if (locs_remaining_[MATRIX_FORW] == 0 && locs_remaining_[MATRIX_REV] == 0) { LOG_DEBUG("SourceToTarget iterations: n = " + std::to_string(n)); break; } @@ -223,31 +237,60 @@ std::vector CostMatrix::SourceToTarget( if (n >= kMaxMatrixIterations) { throw valhalla_exception_t{430}; } + // Allow this process to be aborted + if (interrupt_ && (interrupt_n++ % kInterruptIterationsInterval) == 0) { + (*interrupt_)(); + } n++; } - if (has_time) { - RecostPaths(graphreader, source_location_list, target_location_list, time_infos, invariant); - } - - // Form the time, distance matrix from the destinations list - std::vector td; - uint32_t count = 0; - for (const auto& connection : best_connection_) { - uint32_t target_idx = count % target_location_list.size(); - uint32_t origin_idx = count / target_location_list.size(); - if (has_time) { - auto date_time = get_date_time(source_location_list[origin_idx].date_time(), - time_infos[origin_idx].timezone_index, - target_edgelabel_[target_idx].front().edgeid(), graphreader, - static_cast(connection.cost.secs + .5f)); - td.emplace_back(std::round(connection.cost.secs), std::round(connection.distance), date_time); + // resize/reserve all properties of Matrix on first pass only + valhalla::Matrix& matrix = *request.mutable_matrix(); + reserve_pbf_arrays(matrix, best_connection_.size(), costing_->pass()); + + // Form the matrix PBF output + graph_tile_ptr tile; + bool connection_failed = false; + for (uint32_t connection_idx = 0; connection_idx < best_connection_.size(); connection_idx++) { + auto best_connection = best_connection_[connection_idx]; + // if this is the second pass we don't have to process previously found ones again + if (costing_->pass() > 0 && !(matrix.second_pass(connection_idx))) { + continue; + } + uint32_t target_idx = connection_idx % target_location_list.size(); + uint32_t source_idx = connection_idx / target_location_list.size(); + + // first recost and form the path, if desired (either time and/or geometry requested) + const auto shape = RecostFormPath(graphreader, best_connection, source_location_list[source_idx], + target_location_list[target_idx], source_idx, target_idx, + time_infos[source_idx], invariant, shape_format); + + float time = best_connection.cost.secs; + if (time < kMaxCost) { + auto dt_info = + DateTime::offset_date(source_location_list[source_idx].date_time(), + time_infos[source_idx].timezone_index, + graphreader.GetTimezoneFromEdge(edgelabel_[MATRIX_REV][target_idx] + .front() + .edgeid(), + tile), + time); + *matrix.mutable_date_times(connection_idx) = dt_info.date_time; + *matrix.mutable_time_zone_offsets(connection_idx) = dt_info.time_zone_offset; + *matrix.mutable_time_zone_names(connection_idx) = dt_info.time_zone_name; } else { - td.emplace_back(std::round(connection.cost.secs), std::round(connection.distance)); + // let's try a second pass for this connection + matrix.mutable_second_pass()->Set(connection_idx, true); + connection_failed = true; } - count++; + matrix.mutable_from_indices()->Set(connection_idx, source_idx); + matrix.mutable_to_indices()->Set(connection_idx, target_idx); + matrix.mutable_distances()->Set(connection_idx, best_connection.distance); + matrix.mutable_times()->Set(connection_idx, time); + *matrix.mutable_shapes(connection_idx) = shape; } - return td; + + return !connection_failed; } // Initialize all time distance to "not found". Any locations that @@ -255,322 +298,671 @@ std::vector CostMatrix::SourceToTarget( // remaining locations set. void CostMatrix::Initialize( const google::protobuf::RepeatedPtrField& source_locations, - const google::protobuf::RepeatedPtrField& target_locations) { - source_count_ = source_locations.size(); - target_count_ = target_locations.size(); - - // Add initial sources status - source_status_.reserve(source_count_); - source_hierarchy_limits_.reserve(source_count_); - source_adjacency_.reserve(source_count_); - source_edgestatus_.resize(source_count_); - source_edgelabel_.resize(source_count_); - for (uint32_t i = 0; i < source_count_; i++) { - // Allocate the adjacency list and hierarchy limits for this source. - // Use the cost threshold to size the adjacency list. - source_edgelabel_[i].reserve(max_reserved_labels_count_); - source_adjacency_.emplace_back(DoubleBucketQueue(0, current_cost_threshold_, - costing_->UnitSize(), - &source_edgelabel_[i])); - source_status_.emplace_back(kMaxThreshold); - source_hierarchy_limits_.emplace_back(costing_->GetHierarchyLimits()); - } + const google::protobuf::RepeatedPtrField& target_locations, + const valhalla::Matrix& matrix) { + + locs_count_[MATRIX_FORW] = source_locations.size(); + locs_count_[MATRIX_REV] = target_locations.size(); + astar_heuristics_[MATRIX_FORW].resize(target_locations.size()); + astar_heuristics_[MATRIX_REV].resize(source_locations.size()); + + const auto& hlimits = costing_->GetHierarchyLimits(); + ignore_hierarchy_limits_ = + std::all_of(hlimits.begin() + 1, hlimits.begin() + TileHierarchy::levels().size(), + [](const HierarchyLimits& limits) { + return limits.max_up_transitions == kUnlimitedTransitions; + }); - // Add initial targets status - target_status_.reserve(target_count_); - target_hierarchy_limits_.reserve(target_count_); - target_adjacency_.reserve(target_count_); - target_edgestatus_.resize(target_count_); - target_edgelabel_.resize(target_count_); - for (uint32_t i = 0; i < target_count_; i++) { - // Allocate the adjacency list and hierarchy limits for target location. - // Use the cost threshold to size the adjacency list. - target_edgelabel_[i].reserve(max_reserved_labels_count_); - target_adjacency_.emplace_back(DoubleBucketQueue(0, current_cost_threshold_, - costing_->UnitSize(), - &target_edgelabel_[i])); - target_status_.emplace_back(kMaxThreshold); - target_hierarchy_limits_.emplace_back(costing_->GetHierarchyLimits()); + const uint32_t bucketsize = costing_->UnitSize(); + const float range = kBucketCount * bucketsize; + + // Add initial sources & targets properties + for (const auto is_fwd : {MATRIX_FORW, MATRIX_REV}) { + const auto count = locs_count_[is_fwd]; + const auto other_count = locs_count_[!is_fwd]; + + const auto& locations = is_fwd ? source_locations : target_locations; + const auto& other_locations = is_fwd ? target_locations : source_locations; + + locs_status_[is_fwd].reserve(count); + hierarchy_limits_[is_fwd].resize(count); + adjacency_[is_fwd].resize(count); + edgestatus_[is_fwd].resize(count); + edgelabel_[is_fwd].resize(count); + for (uint32_t i = 0; i < count; i++) { + // Allocate the adjacency list and hierarchy limits for this source. + // Use the cost threshold to size the adjacency list. + edgelabel_[is_fwd][i].reserve(max_reserved_labels_count_); + locs_status_[is_fwd].emplace_back(kMaxThreshold); + hierarchy_limits_[is_fwd][i] = hlimits; + // for each source/target init the other direction's astar heuristic + auto& ll = locations[i].ll(); + astar_heuristics_[!is_fwd][i].Init({ll.lng(), ll.lat()}, costing_->AStarCostFactor()); + + // get the min heuristic to all targets/sources for this source's/target's adjacency list + float min_heuristic = std::numeric_limits::max(); + for (uint32_t j = 0; j < other_count; j++) { + auto& other_ll = other_locations[j].ll(); + auto heuristic = astar_heuristics_[!is_fwd][i].Get({other_ll.lng(), other_ll.lat()}); + min_heuristic = std::min(min_heuristic, heuristic); + } + // TODO(nils): previously we'd estimate the bucket range by the max matrix distance, + // which would lead to tons of RAM if a high value was chosen in the config; ideally + // this would be chosen based on the request (e.g. some factor to the A* distance) + adjacency_[is_fwd][i].reuse(min_heuristic, range, bucketsize, &edgelabel_[is_fwd][i]); + } } // Initialize best connection GraphId empty; Cost trivial_cost(0.0f, 0.0f); Cost max_cost(kMaxCost, kMaxCost); - best_connection_.reserve(source_count_ * target_count_); - for (uint32_t i = 0; i < source_count_; i++) { - for (uint32_t j = 0; j < target_count_; j++) { + best_connection_.reserve(locs_count_[MATRIX_FORW] * locs_count_[MATRIX_REV]); + for (uint32_t i = 0; i < locs_count_[MATRIX_FORW]; i++) { + for (uint32_t j = 0; j < locs_count_[MATRIX_REV]; j++) { + const auto connection_idx = i * static_cast(target_locations.size()) + j; if (equals(source_locations.Get(i).ll(), target_locations.Get(j).ll())) { best_connection_.emplace_back(empty, empty, trivial_cost, 0.0f); best_connection_.back().found = true; + } else if (costing_->pass() > 0 && !matrix.second_pass(connection_idx)) { + // we've found this connection in a previous pass, we only need the time & distance + best_connection_.emplace_back(empty, empty, Cost{0.0f, matrix.times(connection_idx)}, + matrix.distances(connection_idx)); + best_connection_.back().found = true; } else { - best_connection_.emplace_back(empty, empty, max_cost, kMaxCost); - source_status_[i].remaining_locations.insert(j); - target_status_[j].remaining_locations.insert(i); + // in a second pass this block makes sure that if e.g. A -> B is found, but B -> A isn't, + // we still expand both A & B to get the bidirectional benefit + best_connection_.emplace_back(empty, empty, max_cost, static_cast(kMaxCost)); + locs_status_[MATRIX_FORW][i].unfound_connections.insert(j); + locs_status_[MATRIX_REV][j].unfound_connections.insert(i); } } } // Set the remaining number of sources and targets - remaining_sources_ = 0; - for (const auto& s : source_status_) { - if (!s.remaining_locations.empty()) { - remaining_sources_++; + locs_remaining_[MATRIX_FORW] = 0; + for (auto& s : locs_status_[MATRIX_FORW]) { + if (!s.unfound_connections.empty()) { + locs_remaining_[MATRIX_FORW]++; + } else { + // don't look at sources which don't have unfound connections, important for second pass + s.threshold = 0; } } - remaining_targets_ = 0; - for (const auto& t : target_status_) { - if (!t.remaining_locations.empty()) { - remaining_targets_++; + locs_remaining_[MATRIX_REV] = 0; + for (auto& t : locs_status_[MATRIX_REV]) { + if (!t.unfound_connections.empty()) { + locs_remaining_[MATRIX_REV]++; + } else { + // don't look at targets which don't have unfound connections, important for second pass + t.threshold = 0; } } } -// Iterate the forward search from the source/origin location. -void CostMatrix::ForwardSearch(const uint32_t index, - const uint32_t n, - GraphReader& graphreader, - const baldr::TimeInfo& time_info, - const bool invariant) { - // Get the next edge from the adjacency list for this source location - auto& adj = source_adjacency_[index]; - auto& edgelabels = source_edgelabel_[index]; +template +bool CostMatrix::ExpandInner(baldr::GraphReader& graphreader, + const uint32_t index, + const sif::BDEdgeLabel& pred, + const baldr::DirectedEdge* opp_pred_edge, + const baldr::NodeInfo* nodeinfo, + const uint32_t pred_idx, + const EdgeMetadata& meta, + uint32_t& shortcuts, + const graph_tile_ptr& tile, + const baldr::TimeInfo& time_info) { + // Skip if this is a regular edge superseded by a shortcut. + if (shortcuts & meta.edge->superseded()) { + return false; + } + + graph_tile_ptr t2 = nullptr; + baldr::GraphId opp_edge_id; + const auto get_opp_edge_data = [&t2, &opp_edge_id, &graphreader, &meta, &tile]() { + t2 = meta.edge->leaves_tile() ? graphreader.GetGraphTile(meta.edge->endnode()) : tile; + if (t2 == nullptr) { + return false; + } + + opp_edge_id = t2->GetOpposingEdgeId(meta.edge); + return true; + }; + + if (meta.edge->is_shortcut()) { + // Skip shortcuts if hierarchy limits are disabled or the opposing tile doesn't exist + if (ignore_hierarchy_limits_ || !get_opp_edge_data()) + return false; + + // Skip shortcut edges until we have stopped expanding on the next level. Use regular + // edges while still expanding on the next level since we can still transition down to + // that level. If using a shortcut, set the shortcuts mask. Skip if this is a regular + // edge superseded by a shortcut. + if (hierarchy_limits_[FORWARD][index][meta.edge_id.level() + 1].StopExpanding()) { + shortcuts |= meta.edge->shortcut(); + } else { + return false; + } + } + + // Skip this edge if permanently labeled (best path already found to this + // directed edge) or if no access for this mode. + if (meta.edge_status->set() == EdgeSet::kPermanent) { + return true; + } + + // TODO(nils): refactor this whole thing to only have a single if (FORWARD) {} + + const baldr::DirectedEdge* opp_edge = nullptr; + if (!FORWARD) { + // Check the access mode and skip this edge if access is not allowed in the reverse + // direction. This avoids the (somewhat expensive) retrieval of the opposing directed + // edge when no access is allowed in the reverse direction. + if (!(meta.edge->reverseaccess() & access_mode_)) { + return false; + } + + if (t2 == nullptr && !get_opp_edge_data()) { + return false; + } + + opp_edge = t2->directededge(opp_edge_id); + } + + auto& edgelabels = edgelabel_[FORWARD][index]; + // Skip this edge if no access is allowed (based on costing method) + // or if a complex restriction prevents transition onto this edge. + uint8_t restriction_idx = kInvalidRestriction; + if (FORWARD) { + if (!costing_->Allowed(meta.edge, false, pred, tile, meta.edge_id, time_info.local_time, + time_info.timezone_index, restriction_idx) || + costing_->Restricted(meta.edge, pred, edgelabels, tile, meta.edge_id, true, + &edgestatus_[FORWARD][index], time_info.local_time, + time_info.timezone_index)) { + return false; + } + } else { + if (!costing_->AllowedReverse(meta.edge, pred, opp_edge, t2, opp_edge_id, time_info.local_time, + time_info.timezone_index, restriction_idx) || + costing_->Restricted(meta.edge, pred, edgelabels, tile, meta.edge_id, false, + &edgestatus_[FORWARD][index], time_info.local_time, + time_info.timezone_index)) { + return false; + } + } + + // Get cost. Separate out transition cost. + uint8_t flow_sources; + Cost newcost = pred.cost() + (FORWARD ? costing_->EdgeCost(meta.edge, tile, time_info, flow_sources) + : costing_->EdgeCost(opp_edge, t2, time_info, flow_sources)); + sif::Cost tc = + FORWARD ? costing_->TransitionCost(meta.edge, nodeinfo, pred) + : costing_->TransitionCostReverse(meta.edge->localedgeidx(), nodeinfo, opp_edge, + opp_pred_edge, + static_cast(flow_sources & kDefaultFlowMask), + pred.internal_turn()); + newcost += tc; + + const auto pred_dist = pred.path_distance() + meta.edge->length(); + auto& adj = adjacency_[FORWARD][index]; + // Check if edge is temporarily labeled and this path has less cost. If + // less cost the predecessor is updated and the sort cost is decremented + // by the difference in real cost (A* heuristic doesn't change) + if (meta.edge_status->set() == EdgeSet::kTemporary) { + BDEdgeLabel& lab = edgelabel_[FORWARD][index][meta.edge_status->index()]; + if (newcost.cost < lab.cost().cost) { + float newsortcost = lab.sortcost() - (lab.cost().cost - newcost.cost); + adj.decrease(meta.edge_status->index(), newsortcost); + lab.Update(pred_idx, newcost, newsortcost, tc, pred_dist, restriction_idx); + } + // Returning true since this means we approved the edge + return true; + } + + // Get end node tile (skip if tile is not found) and opposing edge Id + if (t2 == nullptr && !get_opp_edge_data()) { + return false; + } + + // not_thru_pruning_ is only set to false on the 2nd pass in matrix_action. + // We allow settling not_thru edges so we can connect both trees on them. + bool not_thru_pruning = + not_thru_pruning_ ? (pred.not_thru_pruning() || !meta.edge->not_thru()) : false; + + // TODO(nils): we could use the distance to the source/target to disable hierarchy limits + // , just as bidir a* /route does; that would make for more optimal paths in some edge cases but + // we'd pay a severe performance penalty, e.g. a request with distance checks (i.e. more expansion + // on lower levels) takes 100 secs, while without it takes 60 secs. + + // Add edge label, add to the adjacency list and set edge status + uint32_t idx = edgelabels.size(); + *meta.edge_status = {EdgeSet::kTemporary, idx}; + if (FORWARD) { + edgelabels.emplace_back(pred_idx, meta.edge_id, opp_edge_id, meta.edge, newcost, mode_, tc, + pred_dist, not_thru_pruning, + (pred.closure_pruning() || !costing_->IsClosed(meta.edge, tile)), + static_cast(flow_sources & kDefaultFlowMask), + costing_->TurnType(pred.opp_local_idx(), nodeinfo, meta.edge), + restriction_idx, 0, + meta.edge->destonly() || + (costing_->is_hgv() && meta.edge->destonly_hgv()), + meta.edge->forwardaccess() & kTruckAccess); + } else { + edgelabels.emplace_back(pred_idx, meta.edge_id, opp_edge_id, meta.edge, newcost, mode_, tc, + pred_dist, not_thru_pruning, + (pred.closure_pruning() || !costing_->IsClosed(opp_edge, t2)), + static_cast(flow_sources & kDefaultFlowMask), + costing_->TurnType(meta.edge->localedgeidx(), nodeinfo, opp_edge, + opp_pred_edge), + restriction_idx, 0, + opp_edge->destonly() || (costing_->is_hgv() && opp_edge->destonly_hgv()), + opp_edge->forwardaccess() & kTruckAccess); + } + auto newsortcost = + GetAstarHeuristic(index, t2->get_node_ll(meta.edge->endnode())); + edgelabels.back().SetSortCost(newcost.cost + newsortcost); + adj.add(idx); + + // mark the edge as settled for the connection check + if (!FORWARD) { + (*targets_)[meta.edge_id].push_back(index); + } else if (check_reverse_connections_) { + (*sources_)[meta.edge_id].push_back(index); + } + + // setting this edge as reached + if (expansion_callback_) { + expansion_callback_(graphreader, meta.edge_id, pred.edgeid(), "costmatrix", + Expansion_EdgeStatus_reached, newcost.secs, pred_dist, newcost.cost, + static_cast(expansion_direction)); + } + + return !(pred.not_thru_pruning() && meta.edge->not_thru()); +} + +template +bool CostMatrix::Expand(const uint32_t index, + const uint32_t n, + baldr::GraphReader& graphreader, + const valhalla::Options& options, + const baldr::TimeInfo& time_info, + const bool invariant) { + + auto& adj = adjacency_[FORWARD][index]; + auto& edgelabels = edgelabel_[FORWARD][index]; uint32_t pred_idx = adj.pop(); if (pred_idx == kInvalidLabel) { - // Forward search is exhausted - mark this and update so we don't + // search is exhausted - mark this and update so we don't // extend searches more than we need to - for (uint32_t target = 0; target < target_count_; target++) { - UpdateStatus(index, target); + for (uint32_t st = 0; st < locs_count_[!FORWARD]; st++) { + if (FORWARD) { + UpdateStatus(index, st); + } else { + UpdateStatus(st, index); + } } - source_status_[index].threshold = 0; - return; + locs_status_[FORWARD][index].threshold = 0; + return false; } // Get edge label and check cost threshold - BDEdgeLabel pred = edgelabels[pred_idx]; - if (pred.cost().secs > current_cost_threshold_) { - source_status_[index].threshold = 0; - return; + auto pred = edgelabels[pred_idx]; + if (pred.path_distance() > current_pathdist_threshold_) { + locs_status_[FORWARD][index].threshold = 0; + return false; } - // Settle this edge - auto& edgestate = source_edgestatus_[index]; - edgestate.Update(pred.edgeid(), EdgeSet::kPermanent); - - // Check for connections to backwards search. - CheckForwardConnections(index, pred, n); + // Settle this edge and log it if requested + auto& edgestatus = edgestatus_[FORWARD][index]; + edgestatus.Update(pred.edgeid(), EdgeSet::kPermanent); + if (expansion_callback_) { + auto prev_pred = + pred.predecessor() == kInvalidLabel ? GraphId{} : edgelabels[pred.predecessor()].edgeid(); + expansion_callback_(graphreader, pred.edgeid(), prev_pred, "costmatrix", + Expansion_EdgeStatus_settled, pred.cost().secs, pred.path_distance(), + pred.cost().cost, static_cast(expansion_direction)); + } - // Prune path if predecessor is not a through edge - if (pred.not_thru() && pred.not_thru_pruning()) { - return; + if (FORWARD) { + CheckForwardConnections(index, pred, n, graphreader, options); + } else if (check_reverse_connections_) { + CheckReverseConnections(index, pred, n, graphreader, options); } - // Get the end node of the prior directed edge. Do not expand on this - // hierarchy level if the maximum number of upward transitions has - // been exceeded. GraphId node = pred.endnode(); - auto& hierarchy_limits = source_hierarchy_limits_[index]; - if (hierarchy_limits[node.level()].StopExpanding()) { - return; + // Prune path if predecessor is not a through edge or if the maximum + // number of upward transitions has been exceeded on this hierarchy level. + if ((pred.not_thru() && pred.not_thru_pruning()) || + (!ignore_hierarchy_limits_ && + hierarchy_limits_[FORWARD][index][node.level()].StopExpanding())) { + return false; } - // lambda to expand search forward from the end node - std::function - expand; - expand = [&](graph_tile_ptr tile, const GraphId& node, const NodeInfo* nodeinfo, BDEdgeLabel& pred, - const uint32_t pred_idx, const bool from_transition, const baldr::TimeInfo& ti) { - // will be updated along the expansion - auto offset_time = from_transition ? ti - : ti.forward(invariant ? 0.f : pred.cost().secs, - static_cast(nodeinfo->timezone())); - uint32_t shortcuts = 0; - GraphId edgeid = {node.tileid(), node.level(), nodeinfo->edge_index()}; - EdgeStatusInfo* es = edgestate.GetPtr(edgeid, tile); - const DirectedEdge* directededge = tile->directededge(nodeinfo->edge_index()); - for (uint32_t i = 0; i < nodeinfo->edge_count(); i++, directededge++, ++edgeid, ++es) { - // Skip shortcut edges until we have stopped expanding on the next level. Use regular - // edges while still expanding on the next level since we can still transition down to - // that level. If using a shortcut, set the shortcuts mask. Skip if this is a regular - // edge superseded by a shortcut. - if (directededge->is_shortcut()) { - if (hierarchy_limits[edgeid.level() + 1].StopExpanding()) { - shortcuts |= directededge->shortcut(); - } else { - continue; - } - } else if (shortcuts & directededge->superseded()) { - continue; - } + // Get the tile and the node info. Skip if tile is null (can happen + // with regional data sets) or if no access at the node. + graph_tile_ptr tile = graphreader.GetGraphTile(node); + if (tile == nullptr) { + return false; + } + const NodeInfo* nodeinfo = tile->node(node); + + // set the time info + auto seconds_offset = invariant ? 0.f : pred.cost().secs; + auto offset_time = FORWARD + ? time_info.forward(seconds_offset, static_cast(nodeinfo->timezone())) + : time_info.reverse(seconds_offset, static_cast(nodeinfo->timezone())); + + // Get the opposing predecessor directed edge if this is reverse. + const DirectedEdge* opp_pred_edge = nullptr; + if (!FORWARD) { + const auto rev_pred_tile = graphreader.GetGraphTile(pred.opp_edgeid(), tile); + if (rev_pred_tile == nullptr) { + return false; + } + opp_pred_edge = rev_pred_tile->directededge(pred.opp_edgeid()); + } - // Skip this edge if permanently labeled (best path already found to this - // directed edge) or if no access for this mode. - if (es->set() == EdgeSet::kPermanent || !(directededge->forwardaccess() & access_mode_)) { - continue; - } + // keep track of shortcuts + uint32_t shortcuts = 0; + // If we encounter a node with an access restriction like a barrier we allow a uturn + if (!costing_->Allowed(nodeinfo)) { + const DirectedEdge* opp_edge = nullptr; + const GraphId opp_edge_id = graphreader.GetOpposingEdgeId(pred.edgeid(), opp_edge, tile); + // Mark the predecessor as a deadend to be consistent with how the + // edgelabels are set when an *actual* deadend (i.e. some dangling OSM geometry) + // is labelled + pred.set_deadend(true); + // Check if edge is null before using it (can happen with regional data sets) + return opp_edge && ExpandInner(graphreader, index, pred, opp_pred_edge, + nodeinfo, pred_idx, + {opp_edge, opp_edge_id, + edgestatus.GetPtr(opp_edge_id, tile)}, + shortcuts, tile, offset_time); + } - // Skip this edge if no access is allowed (based on costing method) - // or if a complex restriction prevents transition onto this edge. - uint8_t restriction_idx = -1; - if (!costing_->Allowed(directededge, false, pred, tile, edgeid, offset_time.local_time, - nodeinfo->timezone(), restriction_idx) || - costing_->Restricted(directededge, pred, edgelabels, tile, edgeid, true, nullptr, - offset_time.local_time, nodeinfo->timezone())) { - continue; - } + // catch u-turn attempts + bool disable_uturn = false; + EdgeMetadata meta = EdgeMetadata::make(node, nodeinfo, tile, edgestatus); + EdgeMetadata uturn_meta{}; + + // Expand from end node in direction. + for (uint32_t i = 0; i < nodeinfo->edge_count(); ++i, ++meta) { + + // Begin by checking if this is the opposing edge to pred. + // If so, it means we are attempting a u-turn. In that case, lets wait with evaluating + // this edge until last. If any other edges were emplaced, it means we should not + // even try to evaluate a u-turn since u-turns should only happen for deadends + const bool is_uturn = pred.opp_local_idx() == meta.edge->localedgeidx(); + uturn_meta = is_uturn ? meta : uturn_meta; + + // Expand but only if this isnt the uturn, we'll try that later if nothing else works out + disable_uturn = + (!is_uturn && + ExpandInner(graphreader, index, pred, opp_pred_edge, nodeinfo, pred_idx, + meta, shortcuts, tile, offset_time)) || + disable_uturn; + } - // Get cost. Separate out transition cost. - Cost tc = costing_->TransitionCost(directededge, nodeinfo, pred); - uint8_t flow_sources; - Cost newcost = - pred.cost() + tc + costing_->EdgeCost(directededge, tile, offset_time, flow_sources); - - // Check if edge is temporarily labeled and this path has less cost. If - // less cost the predecessor is updated along with new cost and distance. - if (es->set() == EdgeSet::kTemporary) { - BDEdgeLabel& lab = edgelabels[es->index()]; - if (newcost.cost < lab.cost().cost) { - adj.decrease(es->index(), newcost.cost); - lab.Update(pred_idx, newcost, newcost.cost, tc, - pred.path_distance() + directededge->length(), restriction_idx); - } + // Handle transitions - expand from the end node of each transition + if (nodeinfo->transition_count() > 0) { + const NodeTransition* trans = tile->transition(nodeinfo->transition_index()); + auto& hierarchy_limits = hierarchy_limits_[FORWARD][index]; + for (uint32_t i = 0; i < nodeinfo->transition_count(); ++i, ++trans) { + // if this is a downward transition (ups are always allowed) AND we are no longer allowed OR + // we cant get the tile at that level (local extracts could have this problem) THEN bail + graph_tile_ptr trans_tile = nullptr; + if ((!trans->up() && !ignore_hierarchy_limits_ && + hierarchy_limits[trans->endnode().level()].StopExpanding()) || + !(trans_tile = graphreader.GetGraphTile(trans->endnode()))) { continue; } - // Get end node tile (skip if tile is not found) and opposing edge Id - graph_tile_ptr t2 = - directededge->leaves_tile() ? graphreader.GetGraphTile(directededge->endnode()) : tile; - if (t2 == nullptr) { - continue; + // setup for expansion at this level + hierarchy_limits[node.level()].up_transition_count += trans->up(); + const auto* trans_node = trans_tile->node(trans->endnode()); + EdgeMetadata trans_meta = + EdgeMetadata::make(trans->endnode(), trans_node, trans_tile, edgestatus); + uint32_t trans_shortcuts = 0; + // expand the edges from this node at this level + for (uint32_t i = 0; i < trans_node->edge_count(); ++i, ++trans_meta) { + disable_uturn = ExpandInner(graphreader, index, pred, opp_pred_edge, + trans_node, pred_idx, trans_meta, + trans_shortcuts, trans_tile, offset_time) || + disable_uturn; } - GraphId oppedge = t2->GetOpposingEdgeId(directededge); - - // Add edge label, add to the adjacency list and set edge status - uint32_t idx = edgelabels.size(); - *es = {EdgeSet::kTemporary, idx}; - edgelabels.emplace_back(pred_idx, edgeid, oppedge, directededge, newcost, mode_, tc, - pred.path_distance() + directededge->length(), - (pred.not_thru_pruning() || !directededge->not_thru()), - (pred.closure_pruning() || !costing_->IsClosed(directededge, tile)), - static_cast(flow_sources & kDefaultFlowMask), - costing_->TurnType(pred.opp_local_idx(), nodeinfo, directededge), - restriction_idx); - adj.add(idx); } + } - // Handle transitions - expand from the end node of the transition - if (!from_transition && nodeinfo->transition_count() > 0) { - const NodeTransition* trans = tile->transition(nodeinfo->transition_index()); - for (uint32_t i = 0; i < nodeinfo->transition_count(); ++i, ++trans) { - if (trans->up()) { - hierarchy_limits[node.level()].up_transition_count++; - } else if (hierarchy_limits[trans->endnode().level()].StopExpanding()) { - continue; - } + // Now, after having looked at all the edges, including edges on other levels, + // we can say if this is a deadend or not, and if so, evaluate the uturn-edge (if it exists) + if (!disable_uturn && uturn_meta) { + // If we found no suitable edge to add, it means we're at a deadend + // so lets go back and re-evaluate a potential u-turn + pred.set_deadend(true); - // Expand from end node of this transition. - GraphId node = trans->endnode(); - graph_tile_ptr endtile = graphreader.GetGraphTile(node); - if (endtile != nullptr) { - expand(endtile, node, endtile->node(node), pred, pred_idx, true, ti); - } - } - } - }; + // TODO(nils): what if there is a shortcut that supersedes our u-turn? can that even be? + // We then need to decide if we should expand the shortcut or the non-shortcut edge... - // Expand from node in forward search path. Get the tile and the node info. - // Skip if tile is null (can happen with regional data sets) or if no access - // at the node. - graph_tile_ptr tile = graphreader.GetGraphTile(node); - if (tile != nullptr) { - const NodeInfo* nodeinfo = tile->node(node); - if (costing_->Allowed(nodeinfo)) { - expand(tile, node, nodeinfo, pred, pred_idx, false, time_info); - } + // Expand the uturn possibility + disable_uturn = + ExpandInner(graphreader, index, pred, opp_pred_edge, nodeinfo, pred_idx, + uturn_meta, shortcuts, tile, offset_time); } + + return disable_uturn; } // Check if the edge on the forward search connects to a reached edge // on the reverse search trees. void CostMatrix::CheckForwardConnections(const uint32_t source, - const BDEdgeLabel& pred, - const uint32_t n) { + const BDEdgeLabel& fwd_pred, + const uint32_t n, + GraphReader& graphreader, + const valhalla::Options& options) { // Disallow connections that are part of an uturn on an internal edge - if (pred.internal_turn() != InternalTurn::kNoTurn) { + if (fwd_pred.internal_turn() != InternalTurn::kNoTurn) { return; } // Disallow connections that are part of a complex restriction. // TODO - validate that we do not need to "walk" the paths forward // and backward to see if they match a restriction. - if (pred.on_complex_rest()) { + if (fwd_pred.on_complex_rest()) { + // TODO(nils): bidir a* is digging deeper return; } // Get the opposing edge. Get a list of target locations whose reverse // search has reached this edge. - GraphId oppedge = pred.opp_edgeid(); - auto targets = targets_->find(oppedge); + GraphId rev_edgeid = fwd_pred.opp_edgeid(); + auto targets = targets_->find(rev_edgeid); if (targets == targets_->end()) { return; } // Iterate through the targets for (auto target : targets->second) { - uint32_t idx = source * target_count_ + target; + uint32_t idx = source * locs_count_[MATRIX_REV] + target; if (best_connection_[idx].found) { continue; } // Update any targets whose threshold has been reached - if (best_connection_[idx].threshold > 0 && n > best_connection_[idx].threshold) { + if (best_connection_[idx].max_iterations > 0 && n > best_connection_[idx].max_iterations) { best_connection_[idx].found = true; continue; } - const auto& edgestate = target_edgestatus_[target]; + // If we came down here, we know this opposing edge is either settled, or it's a + // target correlated edge which hasn't been pulled out of the queue yet, so a path + // has been found to the end node of this directed edge + const auto& rev_edgestate = edgestatus_[MATRIX_REV][target]; + EdgeStatusInfo rev_edgestatus = rev_edgestate.Get(rev_edgeid); + const auto& rev_edgelabels = edgelabel_[MATRIX_REV][target]; + uint32_t rev_predidx = rev_edgelabels[rev_edgestatus.index()].predecessor(); + const BDEdgeLabel& rev_label = rev_edgelabels[rev_edgestatus.index()]; + + // Special case - common edge for source and target are both initial edges + if (fwd_pred.predecessor() == kInvalidLabel && rev_predidx == kInvalidLabel) { + // bail if forward edge wasn't allowed (see notes in SetSources/Targets) + if (!fwd_pred.path_id()) { + return; + } + + // if source percent along edge is larger than target percent along, + // can't connect on this edge + if (find_correlated_edge(options.sources(source), fwd_pred.edgeid()).percent_along() > + find_correlated_edge(options.targets(target), fwd_pred.edgeid()).percent_along()) { + return; + } + + // remember: transition_cost is abused in SetSources/Targets: cost is secs, secs is length + float s = + std::abs(fwd_pred.cost().secs + rev_label.cost().secs - rev_label.transition_cost().cost); + + // Update best connection and set found = true. + // distance computation only works with the casts. + uint32_t d = std::abs(static_cast(fwd_pred.path_distance()) + + static_cast(rev_label.path_distance()) - + static_cast(rev_label.transition_cost().secs)); + best_connection_[idx].Update(fwd_pred.edgeid(), rev_edgeid, Cost(s, s), d); + best_connection_[idx].found = true; + + // Update status and update threshold if this is the last location + // to find for this source or target + UpdateStatus(source, target); + } else { + float oppcost = (rev_predidx == kInvalidLabel) ? 0.f : rev_edgelabels[rev_predidx].cost().cost; + float c = fwd_pred.cost().cost + oppcost + rev_label.transition_cost().cost; + + // Check if best connection + if (c < best_connection_[idx].cost.cost) { + float oppsec = (rev_predidx == kInvalidLabel) ? 0.f : rev_edgelabels[rev_predidx].cost().secs; + uint32_t oppdist = + (rev_predidx == kInvalidLabel) ? 0U : rev_edgelabels[rev_predidx].path_distance(); + float s = fwd_pred.cost().secs + oppsec + rev_label.transition_cost().secs; + uint32_t d = fwd_pred.path_distance() + oppdist; + + // Update best connection and set a threshold + best_connection_[idx].Update(fwd_pred.edgeid(), rev_edgeid, Cost(c, s), d); + if (best_connection_[idx].max_iterations == 0) { + best_connection_[idx].max_iterations = + n + GetThreshold(mode_, edgelabel_[MATRIX_FORW][source].size() + + edgelabel_[MATRIX_REV][target].size()); + } + + // Update status and update threshold if this is the last location + // to find for this source or target + UpdateStatus(source, target); + } + } + // setting this edge as connected + if (expansion_callback_) { + auto prev_pred = fwd_pred.predecessor() == kInvalidLabel + ? GraphId{} + : edgelabel_[MATRIX_FORW][source][fwd_pred.predecessor()].edgeid(); + expansion_callback_(graphreader, fwd_pred.edgeid(), prev_pred, "costmatrix", + Expansion_EdgeStatus_connected, fwd_pred.cost().secs, + fwd_pred.path_distance(), fwd_pred.cost().cost, + Expansion_ExpansionType_forward); + } + } + + return; +} + +void CostMatrix::CheckReverseConnections(const uint32_t target, + const BDEdgeLabel& rev_pred, + const uint32_t n, + GraphReader& graphreader, + const valhalla::Options& options) { + + // Disallow connections that are part of an uturn on an internal edge + if (rev_pred.internal_turn() != InternalTurn::kNoTurn) { + return; + } + // Disallow connections that are part of a complex restriction. + // TODO - validate that we do not need to "walk" the paths forward + // and backward to see if they match a restriction. + if (rev_pred.on_complex_rest()) { + return; + } + + // Get the opposing edge. Get a list of source locations whose forward + // search has reached this edge. + GraphId fwd_edgeid = rev_pred.opp_edgeid(); + auto sources = sources_->find(fwd_edgeid); + if (sources == sources_->end()) { + return; + } + + // Iterate through the sources + for (auto source : sources->second) { + uint32_t source_idx = source * locs_count_[MATRIX_REV] + target; + if (best_connection_[source_idx].found) { + continue; + } + + // Update any targets whose threshold has been reached + if (best_connection_[source_idx].max_iterations > 0 && + n > best_connection_[source_idx].max_iterations) { + best_connection_[source_idx].found = true; + continue; + } // If this edge has been reached then a shortest path has been found // to the end node of this directed edge. - EdgeStatusInfo oppedgestatus = edgestate.Get(oppedge); - if (oppedgestatus.set() != EdgeSet::kUnreachedOrReset) { - const auto& edgelabels = target_edgelabel_[target]; - uint32_t predidx = edgelabels[oppedgestatus.index()].predecessor(); - const BDEdgeLabel& opp_el = edgelabels[oppedgestatus.index()]; + EdgeStatusInfo fwd_edgestatus = edgestatus_[MATRIX_FORW][source].Get(fwd_edgeid); + if (fwd_edgestatus.set() != EdgeSet::kUnreachedOrReset) { + const auto& fwd_edgelabels = edgelabel_[MATRIX_FORW][source]; + uint32_t fwd_predidx = fwd_edgelabels[fwd_edgestatus.index()].predecessor(); + const BDEdgeLabel& fwd_label = fwd_edgelabels[fwd_edgestatus.index()]; // Special case - common edge for source and target are both initial edges - if (pred.predecessor() == kInvalidLabel && predidx == kInvalidLabel) { - // TODO: shouldnt this use seconds? why is this using cost!? - float s = std::abs(pred.cost().secs + opp_el.cost().secs - opp_el.transition_cost().cost); + if (rev_pred.predecessor() == kInvalidLabel && fwd_predidx == kInvalidLabel) { + // bail if the edge wasn't allowed + if (!rev_pred.path_id()) { + return; + } + + if (find_correlated_edge(options.sources(source), fwd_label.edgeid()).percent_along() > + find_correlated_edge(options.targets(target), fwd_label.edgeid()).percent_along()) { + return; + } + + // remember: transition_cost is abused in SetSources/Targets: cost is secs, secs is length + float s = + std::abs(rev_pred.cost().secs + fwd_label.cost().secs - fwd_label.transition_cost().cost); // Update best connection and set found = true. // distance computation only works with the casts. - uint32_t d = std::abs(static_cast(pred.path_distance()) + - static_cast(opp_el.path_distance()) - - static_cast(opp_el.transition_cost().secs)); - best_connection_[idx].Update(pred.edgeid(), oppedge, Cost(s, s), d); - best_connection_[idx].found = true; + uint32_t d = std::abs(static_cast(rev_pred.path_distance()) + + static_cast(fwd_label.path_distance()) - + static_cast(fwd_label.transition_cost().secs)); + best_connection_[source_idx].Update(fwd_edgeid, rev_pred.edgeid(), Cost(s, s), d); + best_connection_[source_idx].found = true; // Update status and update threshold if this is the last location // to find for this source or target UpdateStatus(source, target); } else { - float oppcost = (predidx == kInvalidLabel) ? 0 : edgelabels[predidx].cost().cost; - float c = pred.cost().cost + oppcost + opp_el.transition_cost().cost; + float oppcost = (fwd_predidx == kInvalidLabel) ? 0 : fwd_edgelabels[fwd_predidx].cost().cost; + float c = rev_pred.cost().cost + oppcost + fwd_label.transition_cost().cost; // Check if best connection - if (c < best_connection_[idx].cost.cost) { - float oppsec = (predidx == kInvalidLabel) ? 0 : edgelabels[predidx].cost().secs; - uint32_t oppdist = (predidx == kInvalidLabel) ? 0 : edgelabels[predidx].path_distance(); - float s = pred.cost().secs + oppsec + opp_el.transition_cost().secs; - uint32_t d = pred.path_distance() + oppdist; + if (c < best_connection_[source_idx].cost.cost) { + float fwd_sec = + (fwd_predidx == kInvalidLabel) ? 0 : fwd_edgelabels[fwd_predidx].cost().secs; + uint32_t fwd_dist = + (fwd_predidx == kInvalidLabel) ? 0 : fwd_edgelabels[fwd_predidx].path_distance(); + float s = rev_pred.cost().secs + fwd_sec + fwd_label.transition_cost().secs; + uint32_t d = rev_pred.path_distance() + fwd_dist; // Update best connection and set a threshold - best_connection_[idx].Update(pred.edgeid(), oppedge, Cost(c, s), d); - if (best_connection_[idx].threshold == 0) { - best_connection_[idx].threshold = - n + GetThreshold(mode_, - source_edgelabel_[source].size() + target_edgelabel_[target].size()); + best_connection_[source_idx].Update(fwd_edgeid, rev_pred.edgeid(), Cost(c, s), d); + if (best_connection_[source_idx].max_iterations == 0) { + best_connection_[source_idx].max_iterations = + n + GetThreshold(mode_, edgelabel_[MATRIX_FORW][source].size() + + edgelabel_[MATRIX_REV][target].size()); } // Update status and update threshold if this is the last location @@ -578,209 +970,49 @@ void CostMatrix::CheckForwardConnections(const uint32_t source, UpdateStatus(source, target); } } + // setting this edge as connected + if (expansion_callback_) { + auto prev_pred = rev_pred.predecessor() == kInvalidLabel + ? GraphId{} + : edgelabel_[MATRIX_REV][target][rev_pred.predecessor()].edgeid(); + expansion_callback_(graphreader, rev_pred.edgeid(), prev_pred, "costmatrix", + Expansion_EdgeStatus_connected, rev_pred.cost().secs, + rev_pred.path_distance(), rev_pred.cost().cost, + Expansion_ExpansionType_reverse); + } } } + + return; } // Update status when a connection is found. void CostMatrix::UpdateStatus(const uint32_t source, const uint32_t target) { // Remove the target from the source status - auto& s = source_status_[source].remaining_locations; + auto& s = locs_status_[MATRIX_FORW][source].unfound_connections; auto it = s.find(target); if (it != s.end()) { s.erase(it); - if (s.empty() && source_status_[source].threshold > 0) { + if (s.empty() && locs_status_[MATRIX_FORW][source].threshold > 0) { // At least 1 connection has been found to each target for this source. // Set a threshold to continue search for a limited number of times. - source_status_[source].threshold = - GetThreshold(mode_, source_edgelabel_[source].size() + target_edgelabel_[target].size()); + locs_status_[MATRIX_FORW][source].threshold = + GetThreshold(mode_, edgelabel_[MATRIX_FORW][source].size() + + edgelabel_[MATRIX_REV][target].size()); } } // Remove the source from the target status - auto& t = target_status_[target].remaining_locations; + auto& t = locs_status_[MATRIX_REV][target].unfound_connections; it = t.find(source); if (it != t.end()) { t.erase(it); - if (t.empty() && target_status_[target].threshold > 0) { + if (t.empty() && locs_status_[MATRIX_REV][target].threshold > 0) { // At least 1 connection has been found to each source for this target. // Set a threshold to continue search for a limited number of times. - target_status_[target].threshold = - GetThreshold(mode_, source_edgelabel_[source].size() + target_edgelabel_[target].size()); - } - } -} - -// Expand the backwards search trees. -void CostMatrix::BackwardSearch(const uint32_t index, GraphReader& graphreader) { - // Get the next edge from the adjacency list for this target location - auto& adj = target_adjacency_[index]; - auto& edgelabels = target_edgelabel_[index]; - uint32_t pred_idx = adj.pop(); - if (pred_idx == kInvalidLabel) { - // Backward search is exhausted - mark this and update so we don't - // extend searches more than we need to - for (uint32_t source = 0; source < source_count_; source++) { - UpdateStatus(source, index); - } - target_status_[index].threshold = 0; - return; - } - - // Copy predecessor, check cost threshold - BDEdgeLabel pred = edgelabels[pred_idx]; - if (pred.cost().secs > current_cost_threshold_) { - target_status_[index].threshold = 0; - return; - } - - // Settle this edge - auto& edgestate = target_edgestatus_[index]; - edgestate.Update(pred.edgeid(), EdgeSet::kPermanent); - - // Prune path if predecessor is not a through edge - if (pred.not_thru() && pred.not_thru_pruning()) { - return; - } - - // Get the end node of the prior directed edge. Do not expand on this - // hierarchy level if the maximum number of upward transitions has - // been exceeded. - GraphId node = pred.endnode(); - auto& hierarchy_limits = target_hierarchy_limits_[index]; - if (hierarchy_limits[node.level()].StopExpanding()) { - return; - } - - // Expand from node in reverse direction. - std::function - expand; - expand = [&](graph_tile_ptr tile, const GraphId& node, const NodeInfo* nodeinfo, - const uint32_t index, BDEdgeLabel& pred, const uint32_t pred_idx, - const DirectedEdge* opp_pred_edge, const bool from_transition) { - uint32_t shortcuts = 0; - GraphId edgeid(node.tileid(), node.level(), nodeinfo->edge_index()); - EdgeStatusInfo* es = edgestate.GetPtr(edgeid, tile); - const DirectedEdge* directededge = tile->directededge(nodeinfo->edge_index()); - for (uint32_t i = 0; i < nodeinfo->edge_count(); i++, directededge++, ++edgeid, ++es) { - // Skip shortcut edges until we have stopped expanding on the next level. Use regular - // edges while still expanding on the next level since we can still transition down to - // that level. If using a shortcut, set the shortcuts mask. Skip if this is a regular - // edge superseded by a shortcut. - if (directededge->is_shortcut()) { - if (hierarchy_limits[edgeid.level() + 1].StopExpanding()) { - shortcuts |= directededge->shortcut(); - } else { - continue; - } - } else if (shortcuts & directededge->superseded()) { - continue; - } - - // Skip edges not allowed by the access mode. Do this here to avoid having - // to get opposing edge. Also skip edges that are permanently labeled ( - // best path already found to this directed edge). - if (!(directededge->reverseaccess() & access_mode_) || es->set() == EdgeSet::kPermanent) { - continue; - } - - // Get opposing edge Id and end node tile - graph_tile_ptr t2 = - directededge->leaves_tile() ? graphreader.GetGraphTile(directededge->endnode()) : tile; - if (t2 == nullptr) { - continue; - } - GraphId oppedge = t2->GetOpposingEdgeId(directededge); - - // Skip this edge if no access is allowed (based on costing method) - // or if a complex restriction prevents transition onto this edge. - const DirectedEdge* opp_edge = t2->directededge(oppedge); - uint8_t restriction_idx = -1; - if (!costing_->AllowedReverse(directededge, pred, opp_edge, t2, oppedge, 0, 0, - restriction_idx) || - costing_->Restricted(directededge, pred, edgelabels, tile, edgeid, false)) { - continue; - } - - // Get cost. Use opposing edge for EdgeCost. Separate the transition seconds so - // we can properly recover elapsed time on the reverse path. - uint8_t flow_sources; - Cost newcost = - pred.cost() + costing_->EdgeCost(opp_edge, t2, TimeInfo::invalid(), flow_sources); - - Cost tc = costing_->TransitionCostReverse(directededge->localedgeidx(), nodeinfo, opp_edge, - opp_pred_edge, - static_cast(flow_sources & kDefaultFlowMask), - pred.internal_turn()); - newcost += tc; - - // Check if edge is temporarily labeled and this path has less cost. If - // less cost the predecessor is updated along with new cost and distance. - if (es->set() == EdgeSet::kTemporary) { - BDEdgeLabel& lab = edgelabels[es->index()]; - if (newcost.cost < lab.cost().cost) { - adj.decrease(es->index(), newcost.cost); - lab.Update(pred_idx, newcost, newcost.cost, tc, - pred.path_distance() + directededge->length(), restriction_idx); - } - continue; - } - - // Add edge label, add to the adjacency list and set edge status - uint32_t idx = edgelabels.size(); - *es = {EdgeSet::kTemporary, idx}; - edgelabels.emplace_back(pred_idx, edgeid, oppedge, directededge, newcost, mode_, tc, - pred.path_distance() + directededge->length(), - (pred.not_thru_pruning() || !directededge->not_thru()), - (pred.closure_pruning() || !costing_->IsClosed(directededge, tile)), - static_cast(flow_sources & kDefaultFlowMask), - costing_->TurnType(directededge->localedgeidx(), nodeinfo, opp_edge, - opp_pred_edge), - restriction_idx); - adj.add(idx); - - // Add to the list of targets that have reached this edge - (*targets_)[edgeid].push_back(index); - } - - // Handle transitions - expand from the end node of the transition - if (!from_transition && nodeinfo->transition_count() > 0) { - const NodeTransition* trans = tile->transition(nodeinfo->transition_index()); - for (uint32_t i = 0; i < nodeinfo->transition_count(); ++i, ++trans) { - if (trans->up()) { - hierarchy_limits[node.level()].up_transition_count++; - } else if (hierarchy_limits[trans->endnode().level()].StopExpanding()) { - continue; - } - - // Expand from end node of this transition edge. - GraphId node = trans->endnode(); - graph_tile_ptr endtile = graphreader.GetGraphTile(node); - if (endtile != nullptr) { - expand(endtile, node, endtile->node(node), index, pred, pred_idx, opp_pred_edge, true); - } - continue; - } - } - }; - - // Get the tile and the node info. Skip if tile is null (can happen - // with regional data sets) or if no access at the node. - graph_tile_ptr tile = graphreader.GetGraphTile(node); - if (tile != nullptr) { - const NodeInfo* nodeinfo = tile->node(node); - if (costing_->Allowed(nodeinfo)) { - // Get the opposing predecessor directed edge. Need to make sure we get - // the correct one if a transition occurred - const DirectedEdge* opp_pred_edge; - if (pred.opp_edgeid().Tile_Base() == tile->id().Tile_Base()) { - opp_pred_edge = tile->directededge(pred.opp_edgeid().id()); - } else { - opp_pred_edge = - graphreader.GetGraphTile(pred.opp_edgeid().Tile_Base())->directededge(pred.opp_edgeid()); - } - expand(tile, node, nodeinfo, index, pred, pred_idx, opp_pred_edge, false); + locs_status_[MATRIX_REV][target].threshold = + GetThreshold(mode_, edgelabel_[MATRIX_FORW][source].size() + + edgelabel_[MATRIX_REV][target].size()); } } } @@ -816,8 +1048,9 @@ void CostMatrix::SetSources(GraphReader& graphreader, // Get the directed edge and the opposing edge Id graph_tile_ptr tile = graphreader.GetGraphTile(edgeid); + graph_tile_ptr opp_tile = tile; const DirectedEdge* directededge = tile->directededge(edgeid); - GraphId oppedge = graphreader.GetOpposingEdgeId(edgeid); + GraphId oppedgeid = graphreader.GetOpposingEdgeId(edgeid, opp_tile); // Get cost. Get distance along the remainder of this edge. uint8_t flow_sources; @@ -830,25 +1063,36 @@ void CostMatrix::SetSources(GraphReader& graphreader, // TODO: assumes 1m/s which is a maximum penalty this could vary per costing model cost.cost += edge.distance(); - // Store the edge cost and length in the transition cost (so we can - // recover the full length and cost for cases where origin and - // destination are on the same edge + // 2 adjustments related only to properly handle trivial routes: + // - "transition_cost" is used to store the traversed secs & length + // - "path_id" is used to store whether the edge is even allowed (e.g. no oneway) Cost ec(std::round(edgecost.secs), static_cast(directededge->length())); + BDEdgeLabel edge_label(kInvalidLabel, edgeid, oppedgeid, directededge, cost, mode_, ec, d, + !directededge->not_thru(), !(costing_->IsClosed(directededge, tile)), + static_cast(flow_sources & kDefaultFlowMask), + InternalTurn::kNoTurn, kInvalidRestriction, + static_cast(costing_->Allowed(directededge, tile)), + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); + auto newsortcost = + GetAstarHeuristic(index, opp_tile->get_node_ll( + directededge->endnode())); + edge_label.SetSortCost(cost.cost + newsortcost); // Set the initial not_thru flag to false. There is an issue with not_thru // flags on small loops. Set this to false here to override this for now. - BDEdgeLabel edge_label(kInvalidLabel, edgeid, oppedge, directededge, cost, mode_, ec, d, false, - true, static_cast(flow_sources & kDefaultFlowMask), - InternalTurn::kNoTurn, -1); edge_label.set_not_thru(false); // Add EdgeLabel to the adjacency list (but do not set its status). // Set the predecessor edge index to invalid to indicate the origin // of the path. - uint32_t idx = source_edgelabel_[index].size(); - source_edgelabel_[index].push_back(std::move(edge_label)); - source_adjacency_[index].add(idx); - source_edgestatus_[index].Set(edgeid, EdgeSet::kUnreachedOrReset, idx, tile); + uint32_t idx = edgelabel_[MATRIX_FORW][index].size(); + edgelabel_[MATRIX_FORW][index].push_back(std::move(edge_label)); + adjacency_[MATRIX_FORW][index].add(idx); + edgestatus_[MATRIX_FORW][index].Set(edgeid, EdgeSet::kTemporary, idx, tile); + if (check_reverse_connections_) + (*sources_)[edgeid].push_back(index); } index++; } @@ -889,11 +1133,12 @@ void CostMatrix::SetTargets(baldr::GraphReader& graphreader, const DirectedEdge* directededge = tile->directededge(edgeid); // Get the opposing directed edge, continue if we cannot get it - GraphId opp_edge_id = graphreader.GetOpposingEdgeId(edgeid); + graph_tile_ptr opp_tile = tile; + GraphId opp_edge_id = graphreader.GetOpposingEdgeId(edgeid, opp_tile); if (!opp_edge_id.Is_Valid()) { continue; } - const DirectedEdge* opp_dir_edge = graphreader.GetOpposingEdge(edgeid); + const DirectedEdge* opp_dir_edge = graphreader.GetOpposingEdge(edgeid, opp_tile); // Get cost. Get distance along the remainder of this edge. // Use the directed edge for costing, as this is the forward direction @@ -908,105 +1153,117 @@ void CostMatrix::SetTargets(baldr::GraphReader& graphreader, // TODO: assumes 1m/s which is a maximum penalty this could vary per costing model cost.cost += edge.distance(); - // Store the edge cost and length in the transition cost (so we can - // recover the full length and cost for cases where origin and - // destination are on the same edge + // 2 adjustments related only to properly handle trivial routes: + // - "transition_cost" is used to store the traversed secs & length + // - "path_id" is used to store whether the opp edge is even allowed (e.g. no oneway) Cost ec(std::round(edgecost.secs), static_cast(directededge->length())); - + BDEdgeLabel edge_label(kInvalidLabel, opp_edge_id, edgeid, opp_dir_edge, cost, mode_, ec, d, + !opp_dir_edge->not_thru(), !(costing_->IsClosed(directededge, tile)), + static_cast(flow_sources & kDefaultFlowMask), + InternalTurn::kNoTurn, kInvalidRestriction, + static_cast(costing_->Allowed(opp_dir_edge, opp_tile)), + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); + + auto newsortcost = + GetAstarHeuristic(index, + tile->get_node_ll(opp_dir_edge->endnode())); + edge_label.SetSortCost(cost.cost + newsortcost); // Set the initial not_thru flag to false. There is an issue with not_thru // flags on small loops. Set this to false here to override this for now. - BDEdgeLabel edge_label(kInvalidLabel, opp_edge_id, edgeid, opp_dir_edge, cost, mode_, ec, d, - false, true, static_cast(flow_sources & kDefaultFlowMask), - InternalTurn::kNoTurn, -1); edge_label.set_not_thru(false); // Add EdgeLabel to the adjacency list (but do not set its status). // Set the predecessor edge index to invalid to indicate the origin // of the path. Set the origin flag - uint32_t idx = target_edgelabel_[index].size(); - target_edgelabel_[index].push_back(std::move(edge_label)); - target_adjacency_[index].add(idx); - target_edgestatus_[index].Set(opp_edge_id, EdgeSet::kUnreachedOrReset, idx, - graphreader.GetGraphTile(opp_edge_id)); + uint32_t idx = edgelabel_[MATRIX_REV][index].size(); + edgelabel_[MATRIX_REV][index].push_back(std::move(edge_label)); + adjacency_[MATRIX_REV][index].add(idx); + edgestatus_[MATRIX_REV][index].Set(opp_edge_id, EdgeSet::kTemporary, idx, opp_tile); (*targets_)[opp_edge_id].push_back(index); } index++; } } -// Form the path from the adjacency list. -// TODO: move this function to PathInfo header or so, where both bidir A* and CostMatrix -// can see it -void CostMatrix::RecostPaths(GraphReader& graphreader, - google::protobuf::RepeatedPtrField& sources, - google::protobuf::RepeatedPtrField& targets, - const std::vector& time_infos, - bool invariant) { - uint32_t idx = 0; - for (auto best_connection = best_connection_.begin(); best_connection != best_connection_.end(); - ++best_connection, ++idx) { - // no need to look at 0 paths, i.e. source == target - if (best_connection->cost.secs == 0.f) { - continue; - } +// Form the path from the edfge labels and optionally return the shape +std::string CostMatrix::RecostFormPath(GraphReader& graphreader, + BestCandidate& connection, + const valhalla::Location& source, + const valhalla::Location& target, + const uint32_t source_idx, + const uint32_t target_idx, + const baldr::TimeInfo& time_info, + const bool invariant, + const ShapeFormat shape_format) { + // no need to look at source == target or missing connectivity + if ((!has_time_ && shape_format == no_shape) || connection.cost.secs == 0.f || + connection.distance == kMaxCost) { + return ""; + } - uint32_t source_idx = idx / static_cast(targets.size()); - uint32_t target_idx = idx % static_cast(targets.size()); - const auto& source = sources.Get(source_idx); - const auto& target = targets.Get(target_idx); - // Get the indexes where the connection occurs. - uint32_t connedge_idx1 = source_edgestatus_[source_idx].Get(best_connection->edgeid).index(); - uint32_t connedge_idx2 = target_edgestatus_[target_idx].Get(best_connection->opp_edgeid).index(); - - // set of edges recovered from shortcuts (excluding shortcut's start edges) - std::unordered_set recovered_inner_edges; - - // A place to keep the path - std::vector path_edges; - - // Work backwards on the forward path - graph_tile_ptr tile; - for (auto edgelabel_index = connedge_idx1; edgelabel_index != kInvalidLabel; - edgelabel_index = source_edgelabel_[source_idx][edgelabel_index].predecessor()) { - const BDEdgeLabel& edgelabel = source_edgelabel_[source_idx][edgelabel_index]; - - const DirectedEdge* edge = graphreader.directededge(edgelabel.edgeid(), tile); - if (edge == nullptr) { - throw tile_gone_error_t("CostMatrix::RecostPaths failed", edgelabel.edgeid()); - } + // Get the indices where the connection occurs. + uint32_t connedge_idx1 = edgestatus_[MATRIX_FORW][source_idx].Get(connection.edgeid).index(); + uint32_t connedge_idx2 = edgestatus_[MATRIX_REV][target_idx].Get(connection.opp_edgeid).index(); + + // set of edges recovered from shortcuts (excluding shortcut's start edges) + std::unordered_set recovered_inner_edges; + + // A place to keep the path + std::vector path_edges; + + // Work backwards on the forward path + graph_tile_ptr tile; + for (auto edgelabel_index = connedge_idx1; edgelabel_index != kInvalidLabel; + edgelabel_index = edgelabel_[MATRIX_FORW][source_idx][edgelabel_index].predecessor()) { + const BDEdgeLabel& edgelabel = edgelabel_[MATRIX_FORW][source_idx][edgelabel_index]; - if (edge->is_shortcut()) { - auto superseded = graphreader.RecoverShortcut(edgelabel.edgeid()); - recovered_inner_edges.insert(superseded.begin() + 1, superseded.end()); - std::move(superseded.rbegin(), superseded.rend(), std::back_inserter(path_edges)); - } else - path_edges.push_back(edgelabel.edgeid()); + const DirectedEdge* edge = graphreader.directededge(edgelabel.edgeid(), tile); + if (edge == nullptr) { + throw tile_gone_error_t("CostMatrix::RecostPaths failed", edgelabel.edgeid()); } - // Reverse the list - std::reverse(path_edges.begin(), path_edges.end()); - - // Append the reverse path from the destination - use opposing edges - // The first edge on the reverse path is the same as the last on the forward - // path, so get the predecessor. - for (auto edgelabel_index = target_edgelabel_[target_idx][connedge_idx2].predecessor(); - edgelabel_index != kInvalidLabel; - edgelabel_index = target_edgelabel_[target_idx][edgelabel_index].predecessor()) { - const BDEdgeLabel& edgelabel = target_edgelabel_[target_idx][edgelabel_index]; - const DirectedEdge* opp_edge = nullptr; - GraphId opp_edge_id = graphreader.GetOpposingEdgeId(edgelabel.edgeid(), opp_edge, tile); - if (opp_edge == nullptr) { - throw tile_gone_error_t("CostMatrix::RecostPaths failed", edgelabel.edgeid()); - } + if (edge->is_shortcut()) { + auto superseded = graphreader.RecoverShortcut(edgelabel.edgeid()); + recovered_inner_edges.insert(superseded.begin() + 1, superseded.end()); + std::move(superseded.rbegin(), superseded.rend(), std::back_inserter(path_edges)); + } else + path_edges.push_back(edgelabel.edgeid()); + } - if (opp_edge->is_shortcut()) { - auto superseded = graphreader.RecoverShortcut(opp_edge_id); - recovered_inner_edges.insert(superseded.begin() + 1, superseded.end()); - std::move(superseded.begin(), superseded.end(), std::back_inserter(path_edges)); - } else - path_edges.emplace_back(std::move(opp_edge_id)); + // Reverse the list + std::reverse(path_edges.begin(), path_edges.end()); + + // Append the reverse path from the destination - use opposing edges + // The first edge on the reverse path is the same as the last on the forward + // path, so get the predecessor. + auto& target_edgelabels = edgelabel_[MATRIX_REV][target_idx]; + for (auto edgelabel_index = target_edgelabels[connedge_idx2].predecessor(); + edgelabel_index != kInvalidLabel; + edgelabel_index = target_edgelabels[edgelabel_index].predecessor()) { + const BDEdgeLabel& edgelabel = target_edgelabels[edgelabel_index]; + const DirectedEdge* opp_edge = nullptr; + GraphId opp_edge_id = graphreader.GetOpposingEdgeId(edgelabel.edgeid(), opp_edge, tile); + if (opp_edge == nullptr) { + throw tile_gone_error_t("CostMatrix::RecostPaths failed", edgelabel.edgeid()); } + if (opp_edge->is_shortcut()) { + auto superseded = graphreader.RecoverShortcut(opp_edge_id); + recovered_inner_edges.insert(superseded.begin() + 1, superseded.end()); + std::move(superseded.begin(), superseded.end(), std::back_inserter(path_edges)); + } else + path_edges.emplace_back(std::move(opp_edge_id)); + } + + const auto& source_edge = find_correlated_edge(source, path_edges.front()); + const auto& target_edge = find_correlated_edge(target, path_edges.back()); + float source_pct = static_cast(source_edge.percent_along()); + float target_pct = static_cast(target_edge.percent_along()); + + // recost the path if this was a time-dependent expansion + if (has_time_) { auto edge_itr = path_edges.begin(); const auto edge_cb = [&edge_itr, &path_edges]() { return (edge_itr == path_edges.end()) ? GraphId{} : (*edge_itr++); @@ -1015,23 +1272,76 @@ void CostMatrix::RecostPaths(GraphReader& graphreader, Cost new_cost{0.f, 0.f}; const auto label_cb = [&new_cost](const EdgeLabel& label) { new_cost = label.cost(); }; - float source_pct = find_percent_along(source, path_edges.front()); - float target_pct = find_percent_along(target, path_edges.back()); - // recost edges in final path; ignore access restrictions - auto& time_info = time_infos[source_idx]; try { sif::recost_forward(graphreader, *costing_, edge_cb, label_cb, source_pct, target_pct, time_info, invariant, true); } catch (const std::exception& e) { LOG_ERROR(std::string("CostMatrix failed to recost final paths: ") + e.what()); - continue; + return ""; } // update the existing best_connection cost - best_connection->cost = new_cost; + connection.cost = new_cost; } + + // bail if no shape was requested + if (shape_format == no_shape) + return ""; + + auto source_vertex = PointLL{source_edge.ll().lng(), source_edge.ll().lat()}; + auto target_vertex = PointLL{target_edge.ll().lng(), target_edge.ll().lat()}; + std::vector points; + for (uint32_t i = 0; i < path_edges.size(); i++) { + auto& path_edge = path_edges[i]; + auto is_first_edge = i == 0; + auto is_last_edge = i == (path_edges.size() - 1); + + const auto* de = graphreader.directededge(path_edge, tile); + auto edge_shp = tile->edgeinfo(de).shape(); + + if (is_first_edge || is_last_edge) { + if (!de->forward()) + std::reverse(edge_shp.begin(), edge_shp.end()); + + float total = static_cast(de->length()); + if (is_first_edge && is_last_edge) { + trim_shape(source_pct * total, source_vertex, target_pct * total, target_vertex, edge_shp); + } else if (is_first_edge) { + trim_shape(source_pct * total, source_vertex, total, edge_shp.back(), edge_shp); + } // last edge + else { + trim_shape(0, edge_shp.front(), target_pct * total, target_vertex, edge_shp); + } + + points.insert(points.end(), edge_shp.begin() + !is_first_edge, edge_shp.end()); + } else { + if (de->forward()) { + points.insert(points.end(), edge_shp.begin() + 1, edge_shp.end()); + } else { + points.insert(points.end(), edge_shp.rbegin() + 1, edge_shp.rend()); + } + } + } + + // encode to 6 precision for geojson as well, which the serializer expects + return encode(points, shape_format != polyline5 ? 1e6 : 1e5); } +template +float CostMatrix::GetAstarHeuristic(const uint32_t loc_idx, const PointLL& ll) const { + if (locs_status_[FORWARD][loc_idx].unfound_connections.empty()) { + return 0.f; + } + + auto min_cost = std::numeric_limits::max(); + for (const auto other_idx : locs_status_[FORWARD][loc_idx].unfound_connections) { + const auto cost = astar_heuristics_[FORWARD][other_idx].Get(ll); + min_cost = std::min(cost, min_cost); + } + + return min_cost; +}; + } // namespace thor } // namespace valhalla diff --git a/src/thor/dijkstras.cc b/src/thor/dijkstras.cc index db0ed38974..21df148084 100644 --- a/src/thor/dijkstras.cc +++ b/src/thor/dijkstras.cc @@ -3,7 +3,6 @@ #include "midgard/distanceapproximator.h" #include "midgard/logging.h" #include -#include using namespace valhalla::midgard; using namespace valhalla::baldr; @@ -78,7 +77,7 @@ void Dijkstras::Initialize(label_container_t& labels, uint32_t edge_label_reservation; uint32_t bucket_count; GetExpansionHints(bucket_count, edge_label_reservation); - labels.reserve(std::min(max_reserved_labels_count_, edge_label_reservation)); + labels.reserve(max_reserved_labels_count_); // Set up lambda to get sort costs float range = bucket_count * bucket_size; @@ -178,7 +177,7 @@ void Dijkstras::ExpandInner(baldr::GraphReader& graphreader, // Check if the edge is allowed or if a restriction occurs EdgeStatus* todo = nullptr; - uint8_t restriction_idx = -1; + uint8_t restriction_idx = kInvalidRestriction; // is_dest is false, because it is a traversal algorithm in this context, not a path search // algorithm. In other words, destination edges are not defined for this Dijkstra's algorithm. const bool is_dest = false; @@ -238,7 +237,7 @@ void Dijkstras::ExpandInner(baldr::GraphReader& graphreader, // Only needed if you want to connect with a reverse path - for reverse mode, // these were populated earlier - if (expansion_direction == ExpansionType::forward) { + if (FORWARD) { t2 = tile; oppedgeid = graphreader.GetOpposingEdgeId(edgeid, t2); } @@ -246,15 +245,29 @@ void Dijkstras::ExpandInner(baldr::GraphReader& graphreader, // Add edge label, add to the adjacency list and set edge status uint32_t idx = bdedgelabels_.size(); *es = {EdgeSet::kTemporary, idx}; - bdedgelabels_.emplace_back(pred_idx, edgeid, oppedgeid, directededge, newcost, mode_, - transition_cost, path_dist, false, - (pred.closure_pruning() || !costing_->IsClosed(directededge, tile)), - static_cast(flow_sources & kDefaultFlowMask), - (expansion_direction == ExpansionType::forward) - ? costing_->TurnType(pred.opp_local_idx(), nodeinfo, directededge) - : costing_->TurnType(directededge->localedgeidx(), nodeinfo, - opp_edge, opp_pred_edge), - restriction_idx, pred.path_id()); + if (FORWARD) { + bdedgelabels_.emplace_back(pred_idx, edgeid, oppedgeid, directededge, newcost, mode_, + transition_cost, path_dist, false, + (pred.closure_pruning() || !costing_->IsClosed(directededge, tile)), + static_cast(flow_sources & kDefaultFlowMask), + costing_->TurnType(pred.opp_local_idx(), nodeinfo, directededge), + restriction_idx, pred.path_id(), + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); + + } else { + bdedgelabels_.emplace_back(pred_idx, edgeid, oppedgeid, directededge, newcost, mode_, + transition_cost, path_dist, false, + (pred.closure_pruning() || !costing_->IsClosed(opp_edge, t2)), + static_cast(flow_sources & kDefaultFlowMask), + costing_->TurnType(directededge->localedgeidx(), nodeinfo, opp_edge, + opp_pred_edge), + restriction_idx, pred.path_id(), + opp_edge->destonly() || + (costing_->is_hgv() && opp_edge->destonly_hgv()), + opp_edge->forwardaccess() & kTruckAccess); + } adjacencylist_.add(idx); } @@ -369,8 +382,13 @@ void Dijkstras::Compute(google::protobuf::RepeatedPtrField& } if (expansion_callback_) { - expansion_callback_(graphreader, pred.edgeid(), "dijkstras", "s", pred.cost().secs, - pred.path_distance(), pred.cost().cost); + const auto prev_pred = pred.predecessor() == kInvalidLabel + ? GraphId{} + : bdedgelabels_[pred.predecessor()].edgeid(); + expansion_callback_(graphreader, pred.edgeid(), prev_pred, "dijkstras", + Expansion_EdgeStatus_settled, pred.cost().secs, pred.path_distance(), + pred.cost().cost, + static_cast(expansion_direction)); } } } @@ -509,7 +527,7 @@ void Dijkstras::ExpandForwardMultiModal(GraphReader& graphreader, // costing - assume if you get a transit edge you walked to the transit stop uint32_t tripid = 0; uint32_t blockid = 0; - uint8_t restriction_idx = -1; + uint8_t restriction_idx = kInvalidRestriction; const bool is_dest = false; if (directededge->IsTransitLine()) { // Check if transit costing allows this edge @@ -786,7 +804,6 @@ void Dijkstras::SetOriginLocations(GraphReader& graphreader, if (!opp_edge_id.Is_Valid()) { continue; } - const DirectedEdge* opp_dir_edge = opp_tile->directededge(opp_edge_id); // Get cost uint8_t flow_sources; @@ -804,11 +821,13 @@ void Dijkstras::SetOriginLocations(GraphReader& graphreader, // Construct the edge label. Set the predecessor edge index to invalid // to indicate the origin of the path. uint32_t idx = bdedgelabels_.size(); - int restriction_idx = -1; bdedgelabels_.emplace_back(kInvalidLabel, edgeid, opp_edge_id, directededge, cost, mode_, Cost{}, path_dist, false, !(costing_->IsClosed(directededge, tile)), static_cast(flow_sources & kDefaultFlowMask), - InternalTurn::kNoTurn, restriction_idx, multipath_ ? path_id : 0); + InternalTurn::kNoTurn, kInvalidRestriction, multipath_ ? path_id : 0, + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); // Set the origin flag bdedgelabels_.back().set_origin(); @@ -897,7 +916,10 @@ void Dijkstras::SetDestinationLocations( bdedgelabels_.emplace_back(kInvalidLabel, opp_edge_id, edgeid, opp_dir_edge, cost, mode_, Cost{}, path_dist, false, !(costing_->IsClosed(directededge, tile)), static_cast(flow_sources & kDefaultFlowMask), - InternalTurn::kNoTurn, restriction_idx, multipath_ ? path_id : 0); + InternalTurn::kNoTurn, restriction_idx, multipath_ ? path_id : 0, + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); adjacencylist_.add(idx); edgestatus_.Set(opp_edge_id, EdgeSet::kTemporary, idx, opp_tile, multipath_ ? path_id : 0); } diff --git a/src/thor/expansion_action.cc b/src/thor/expansion_action.cc index bed9d30ff0..326e35d4a0 100644 --- a/src/thor/expansion_action.cc +++ b/src/thor/expansion_action.cc @@ -1,24 +1,91 @@ -#include "thor/worker.h" - -#include "baldr/json.h" -#include "baldr/rapidjson_utils.h" #include "midgard/constants.h" #include "midgard/logging.h" #include "midgard/polyline2.h" #include "midgard/util.h" +#include "thor/pathalgorithm.h" +#include "thor/worker.h" +#include "tyr/serializers.h" +#include using namespace rapidjson; using namespace valhalla::midgard; +using namespace valhalla::tyr; + +namespace { + +using namespace valhalla; + +void writeExpansionProgress(Expansion* expansion, + const baldr::GraphId& edgeid, + const baldr::GraphId& prev_edgeid, + const std::vector& shape, + const std::unordered_set& exp_props, + const Expansion_EdgeStatus& status, + const float& duration, + const uint32_t& distance, + const float& cost, + const Expansion_ExpansionType expansion_type) { + + auto* geom = expansion->add_geometries(); + // make the geom + for (const auto& p : shape) { + geom->add_coords(round(p.lng() * 1e6)); + geom->add_coords(round(p.lat() * 1e6)); + } + + // no properties asked for, don't collect any + if (!exp_props.size()) { + return; + } + + // make the properties + if (exp_props.count(Options_ExpansionProperties_duration)) + expansion->add_durations(static_cast(duration)); + if (exp_props.count(Options_ExpansionProperties_distance)) + expansion->add_distances(static_cast(distance)); + if (exp_props.count(Options_ExpansionProperties_cost)) + expansion->add_costs(static_cast(cost)); + if (exp_props.count(Options_ExpansionProperties_edge_status)) + expansion->add_edge_status(status); + if (exp_props.count(Options_ExpansionProperties_edge_id)) + expansion->add_edge_id(static_cast(edgeid)); + if (exp_props.count(Options_ExpansionProperties_pred_edge_id)) + expansion->add_pred_edge_id(static_cast(prev_edgeid)); + if (exp_props.count(Options_ExpansionProperties_expansion_type)) + expansion->add_expansion_type(expansion_type); +} + +struct expansion_properties_t { + baldr::GraphId prev_edgeid; + // highest status the edge has seen + Expansion_EdgeStatus status; + float duration; + std::vector shape; + uint32_t distance; + float cost; + Expansion_ExpansionType expansion_type; + + expansion_properties_t() = default; + expansion_properties_t(baldr::GraphId prev_edgeid, + Expansion_EdgeStatus status, + float duration, + uint32_t distance, + std::vector&& shape, + float cost, + Expansion_ExpansionType expansion_type) + : prev_edgeid(prev_edgeid), status(status), duration(duration), distance(distance), + shape(std::move(shape)), cost(cost), expansion_type(expansion_type){}; + + // check if status is higher or same – as we will keep track of the latest one + static bool is_latest_status(Expansion_EdgeStatus current, Expansion_EdgeStatus candidate) { + return candidate <= current; + } +}; +} // namespace namespace valhalla { namespace thor { -// indices correspond to Options::ExpansionProperties enum -const std::string kPropPaths[5] = {"/features/0/properties/costs", "/features/0/properties/durations", - "/features/0/properties/distances", - "/features/0/properties/statuses", - "/features/0/properties/edge_ids"}; - std::string thor_worker_t::expansion(Api& request) { // time this whole method and save that statistic measure_scope_time(request); @@ -27,107 +94,67 @@ std::string thor_worker_t::expansion(Api& request) { auto options = request.options(); auto exp_action = options.expansion_action(); bool skip_opps = options.skip_opposites(); + bool dedupe = options.dedupe(); std::unordered_set opp_edges; std::unordered_set exp_props; + typedef robin_hood::unordered_map edge_state_t; + edge_state_t edge_state; // default generalization to ~ zoom level 15 float gen_factor = options.has_generalize_case() ? options.generalize() : 10.f; - - // default the expansion geojson so its easy to add to as we go - Document dom; - dom.SetObject(); - // set algorithm to Dijkstra, will be overwritten by other algos - SetValueByPointer(dom, "/type", "FeatureCollection"); - SetValueByPointer(dom, "/features/0/type", "Feature"); - SetValueByPointer(dom, "/features/0/geometry/type", "MultiLineString"); - SetValueByPointer(dom, "/features/0/geometry/coordinates", Value(kArrayType)); - SetValueByPointer(dom, "/features/0/properties", Value(kObjectType)); for (const auto& prop : options.expansion_properties()) { - rapidjson::Pointer(kPropPaths[prop]).Set(dom, Value(kArrayType)); exp_props.insert(static_cast(prop)); } + auto* expansion = request.mutable_expansion(); // a lambda that the path algorithm can call to add stuff to the dom // route and isochrone produce different GeoJSON properties - auto track_expansion = [&dom, &opp_edges, &gen_factor, &skip_opps, - &exp_props](baldr::GraphReader& reader, baldr::GraphId edgeid, - const char* algorithm = nullptr, const char* status = nullptr, - const float duration = 0.f, const uint32_t distance = 0, - const float cost = 0.f) { - auto tile = reader.GetGraphTile(edgeid); - if (tile == nullptr) { - LOG_ERROR("thor_worker_t::expansion error, tile no longer available" + - std::to_string(edgeid.Tile_Base())); - return; - } - const auto* edge = tile->directededge(edgeid); - // unfortunately we have to call this before checking if we can skip - // else the tile could change underneath us when we get the opposing - auto shape = tile->edgeinfo(edge).shape(); - auto names = tile->edgeinfo(edge).GetNames(); - auto is_forward = edge->forward(); - - // if requested, skip this edge in case its opposite edge has been added - // before (i.e. lower cost) else add this edge's id to the lookup container - if (skip_opps) { - auto opp_edgeid = reader.GetOpposingEdgeId(edgeid, tile); - if (opp_edgeid && opp_edges.count(opp_edgeid)) - return; - opp_edges.insert(edgeid); - } - - if (!edge->forward()) - std::reverse(shape.begin(), shape.end()); - Polyline2::Generalize(shape, gen_factor, {}, false); - - // make the geom - auto& a = dom.GetAllocator(); - auto* coords = GetValueByPointer(dom, "/features/0/geometry/coordinates"); - coords->GetArray().PushBack(Value(kArrayType), a); - auto& linestring = (*coords)[coords->Size() - 1]; - for (const auto& p : shape) { - linestring.GetArray().PushBack(Value(kArrayType), a); - auto point = linestring[linestring.Size() - 1].GetArray(); - point.PushBack(p.first, a); - point.PushBack(p.second, a); - } - - // no properties asked for, don't collect any - if (!exp_props.size()) { - return; - } - - // make the properties - SetValueByPointer(dom, "/properties/algorithm", algorithm); - if (exp_props.count(Options_ExpansionProperties_durations)) { - Pointer(kPropPaths[Options_ExpansionProperties_durations]) - .Get(dom) - ->GetArray() - .PushBack(Value{}.SetUint(static_cast(duration)), a); - } - if (exp_props.count(Options_ExpansionProperties_distances)) { - Pointer(kPropPaths[Options_ExpansionProperties_distances]) - .Get(dom) - ->GetArray() - .PushBack(Value{}.SetUint(distance), a); - } - if (exp_props.count(Options_ExpansionProperties_costs)) { - Pointer(kPropPaths[Options_ExpansionProperties_costs]) - .Get(dom) - ->GetArray() - .PushBack(Value{}.SetUint(static_cast(cost)), a); - } - if (exp_props.count(Options_ExpansionProperties_statuses)) - Pointer(kPropPaths[Options_ExpansionProperties_statuses]) - .Get(dom) - ->GetArray() - .PushBack(Value{}.SetString(status, a), a); - if (exp_props.count(Options_ExpansionProperties_edge_ids)) - Pointer(kPropPaths[Options_ExpansionProperties_edge_ids]) - .Get(dom) - ->GetArray() - .PushBack(Value{}.SetUint64(static_cast(edgeid)), a); - }; + std::string algo = ""; + auto track_expansion = + [&](baldr::GraphReader& reader, baldr::GraphId edgeid, baldr::GraphId prev_edgeid, + const char* algorithm = nullptr, + const Expansion_EdgeStatus status = Expansion_EdgeStatus_reached, + const float duration = 0.f, const uint32_t distance = 0, const float cost = 0.f, + const Expansion_ExpansionType expansion_type = Expansion_ExpansionType_forward) { + algo = algorithm; + + auto tile = reader.GetGraphTile(edgeid); + if (tile == nullptr) { + LOG_ERROR("thor_worker_t::expansion error, tile no longer available" + + std::to_string(edgeid.Tile_Base())); + return; + } + + // if requested, skip this edge in case its opposite edge has been added + // before (i.e. lower cost) else add this edge's id to the lookup container + if (skip_opps) { + auto opp_tile = tile; + auto opp_edgeid = reader.GetOpposingEdgeId(edgeid, opp_tile); + if (opp_edgeid && opp_edges.count(opp_edgeid)) + return; + opp_edges.insert(edgeid); + } + + const auto* edge = tile->directededge(edgeid); + auto shape = tile->edgeinfo(edge).shape(); + + if (!edge->forward()) + std::reverse(shape.begin(), shape.end()); + Polyline2::Generalize(shape, gen_factor, {}, false); + if (dedupe) { + if (edge_state.contains(edgeid)) { + // Keep only properties of last/highest status of edge + if (!expansion_properties_t::is_latest_status(edge_state.at(edgeid).status, status)) { + return; + } + } + edge_state[edgeid] = expansion_properties_t(prev_edgeid, status, duration, distance, + std::move(shape), cost, expansion_type); + } else { + writeExpansionProgress(expansion, edgeid, prev_edgeid, shape, exp_props, status, duration, + distance, cost, expansion_type); + } + }; // tell all the algorithms how to track expansion for (auto* alg : std::vector{ @@ -139,6 +166,10 @@ std::string thor_worker_t::expansion(Api& request) { }) { alg->set_track_expansion(track_expansion); } + for (auto* alg : std::vector{&costmatrix_, &time_distance_matrix_, + &time_distance_bss_matrix_}) { + alg->set_track_expansion(track_expansion); + } isochrone_gen.SetInnerExpansionCallback(track_expansion); try { @@ -147,21 +178,33 @@ std::string thor_worker_t::expansion(Api& request) { route(request); } else if (exp_action == Options::isochrone) { isochrones(request); + } else if (exp_action == Options::sources_to_targets) { + matrix(request); } } catch (...) { // we swallow exceptions because we actually want to see what the heck the expansion did // anyway } + // assemble the properties from latest/highest stages it went through + if (dedupe) { + for (const auto& e : edge_state) { + writeExpansionProgress(expansion, e.first, e.second.prev_edgeid, e.second.shape, exp_props, + e.second.status, e.second.duration, e.second.distance, e.second.cost, + e.second.expansion_type); + } + } + // tell all the algorithms to stop tracking the expansion for (auto* alg : std::vector{&multi_modal_astar, &timedep_forward, &timedep_reverse, &bidir_astar, &bss_astar}) { alg->set_track_expansion(nullptr); } + costmatrix_.set_track_expansion(nullptr); isochrone_gen.SetInnerExpansionCallback(nullptr); // serialize it - return to_string(dom, 5); + return tyr::serializeExpansion(request, algo); } } // namespace thor diff --git a/src/thor/isochrone.cc b/src/thor/isochrone.cc index 4550f60d0b..9865689f72 100644 --- a/src/thor/isochrone.cc +++ b/src/thor/isochrone.cc @@ -3,8 +3,6 @@ #include "midgard/distanceapproximator.h" #include "midgard/logging.h" #include -#include // TODO remove if not needed -#include using namespace valhalla::midgard; using namespace valhalla::baldr; @@ -17,10 +15,18 @@ constexpr float METRIC_PADDING = 10.f; template std::vector> OriginEdgeShape(const std::vector>& pts, double distance_along) { + // just the endpoint really + if (distance_along == 0) + return {pts.back(), pts.back()}; + + // consume shape until we reach the desired distance double suffix_len = 0; for (auto from = std::next(pts.rbegin()), to = pts.rbegin(); from != pts.rend(); ++from, ++to) { + // add whatever this segment of shape contributes to the overall distance PrecisionT len = from->Distance(*to); suffix_len += len; + + // we have enough distance now, lets find the exact stopping point along the geom if (suffix_len >= distance_along) { auto interpolated = from->PointAlongSegment(*to, (suffix_len - distance_along) / len); std::vector> res(pts.rbegin(), from); @@ -29,6 +35,8 @@ std::vector> OriginEdgeShape(const std::vector max_seconds_ && dist > max_meters_) - return ExpansionRecommendation::prune_expansion; + + ExpansionRecommendation recommendation = (time > max_seconds_ && dist > max_meters_) + ? ExpansionRecommendation::prune_expansion + : ExpansionRecommendation::continue_expansion; // track expansion if (inner_expansion_callback_ && (time <= (max_seconds_ - METRIC_PADDING * kSecondsPerMinute) || @@ -324,8 +334,7 @@ ExpansionRecommendation Isochrone::ShouldExpand(baldr::GraphReader& /*graphreade } else if (expansion_callback_) { expansion_callback_ = nullptr; } - - return ExpansionRecommendation::continue_expansion; + return recommendation; }; void Isochrone::GetExpansionHints(uint32_t& bucket_count, uint32_t& edge_label_reservation) const { diff --git a/src/thor/isochrone_action.cc b/src/thor/isochrone_action.cc index 4c031e071e..6faf094e06 100644 --- a/src/thor/isochrone_action.cc +++ b/src/thor/isochrone_action.cc @@ -17,13 +17,13 @@ std::string thor_worker_t::isochrones(Api& request) { auto costing = parse_costing(request); // name of the metric (time/distance, value, color) - std::vector::contour_interval_t> contours; + std::vector::contour_interval_t> intervals; for (const auto& contour : options.contours()) { if (contour.has_time_case()) { - contours.emplace_back(0, contour.time(), "time", contour.color()); + intervals.emplace_back(0, contour.time(), "time", contour.color()); } if (contour.has_distance_case()) { - contours.emplace_back(1, contour.distance(), "distance", contour.color()); + intervals.emplace_back(1, contour.distance(), "distance", contour.color()); } } @@ -43,15 +43,8 @@ std::string thor_worker_t::isochrones(Api& request) { if (options.action() == Options_Action_expansion) return ""; - // we have parallel vectors of contour properties and the actual geojson features - // this method sorts the contour specifications by metric (time or distance) and then by value - // with the largest values coming first. eg (60min, 30min, 10min, 40km, 10km) - auto isolines = - grid->GenerateContours(contours, options.polygons(), options.denoise(), options.generalize()); - - // make the final json - std::string ret = tyr::serializeIsochrones(request, contours, isolines, options.polygons(), - options.show_locations()); + // make the final output (pbf, json or geotiff) + std::string ret = tyr::serializeIsochrones(request, intervals, grid); return ret; } diff --git a/src/thor/map_matcher.cc b/src/thor/map_matcher.cc index b52073957b..96c4ce3df5 100644 --- a/src/thor/map_matcher.cc +++ b/src/thor/map_matcher.cc @@ -1,6 +1,5 @@ #include "midgard/logging.h" #include -#include #include #include "baldr/datetime.h" @@ -283,14 +282,14 @@ MapMatcher::FormPath(meili::MapMatcher* matcher, directededge, elapsed, 0, - 0, mode, 0, - {}, baldr::kInvalidRestriction, true, static_cast(flow_sources & kDefaultFlowMask), - turn}; + turn, + 0, + directededge->destonly() || (costing->is_hgv() && directededge->destonly_hgv())}; paths.back().first.emplace_back( PathInfo{mode, elapsed, edge_id, 0, 0, edge_segment.restriction_idx, transition_cost}); paths.back().second.emplace_back(&edge_segment); diff --git a/src/thor/matrix_action.cc b/src/thor/matrix_action.cc index 0fb0b1b219..48838592a9 100644 --- a/src/thor/matrix_action.cc +++ b/src/thor/matrix_action.cc @@ -14,46 +14,32 @@ using namespace valhalla::baldr; using namespace valhalla::sif; using namespace valhalla::thor; -namespace valhalla { -namespace thor { - -constexpr uint32_t kCostMatrixThreshold = 5; - -std::string thor_worker_t::matrix(Api& request) { - // time this whole method and save that statistic - auto _ = measure_scope_time(request); +namespace { +const std::string get_unfound_indices(const google::protobuf::RepeatedField& result) { + std::string indices; + for (int i = 0; i != result.size(); ++i) { + if (result[i]) { + indices += std::to_string(i) + ","; + } + } + indices.pop_back(); - auto& options = *request.mutable_options(); - adjust_scores(options); - auto costing = parse_costing(request); + return indices; +} - // Distance scaling (miles or km) - double distance_scale = (options.units() == Options::miles) ? kMilePerMeter : kKmPerMeter; +constexpr uint32_t kCostMatrixThreshold = 5; +} // namespace - // lambdas to do the real work - auto costmatrix = [&](const bool has_time) { - return costmatrix_.SourceToTarget(*options.mutable_sources(), *options.mutable_targets(), *reader, - mode_costing, mode, max_matrix_distance.find(costing)->second, - has_time, options.date_time_type() == Options::invariant); - }; - auto timedistancematrix = [&]() { - return time_distance_matrix_.SourceToTarget(*options.mutable_sources(), - *options.mutable_targets(), *reader, mode_costing, - mode, max_matrix_distance.find(costing)->second, - options.matrix_locations(), - options.date_time_type() == Options::invariant); - }; +namespace valhalla { +namespace thor { +MatrixAlgorithm* +thor_worker_t::get_matrix_algorithm(Api& request, const bool has_time, const std::string& costing) { if (costing == "bikeshare") { - const auto& time_distances = - time_distance_bss_matrix_.SourceToTarget(options.sources(), options.targets(), *reader, - mode_costing, mode, - max_matrix_distance.find(costing)->second, - options.matrix_locations()); - return tyr::serializeMatrix(request, time_distances, distance_scale, MatrixType::TimeDist); + return &time_distance_bss_matrix_; } - MatrixType matrix_type = MatrixType::Cost; + Matrix::Algorithm config_algo = Matrix::CostMatrix; switch (source_to_target_algorithm) { case SELECT_OPTIMAL: // TODO - Do further performance testing to pick the best algorithm for the job @@ -62,13 +48,13 @@ std::string thor_worker_t::matrix(Api& request) { case travel_mode_t::kBicycle: // Use CostMatrix if number of sources and number of targets // exceeds some threshold - if (options.sources().size() <= kCostMatrixThreshold || - options.targets().size() <= kCostMatrixThreshold) { - matrix_type = MatrixType::TimeDist; + if (static_cast(request.options().sources().size()) <= kCostMatrixThreshold || + static_cast(request.options().targets().size()) <= kCostMatrixThreshold) { + config_algo = Matrix::TimeDistanceMatrix; } break; case travel_mode_t::kPublicTransit: - matrix_type = MatrixType::TimeDist; + config_algo = Matrix::TimeDistanceMatrix; break; default: break; @@ -77,32 +63,89 @@ std::string thor_worker_t::matrix(Api& request) { case COST_MATRIX: break; case TIME_DISTANCE_MATRIX: - matrix_type = MatrixType::TimeDist; + config_algo = Matrix::TimeDistanceMatrix; break; } // similar to routing: prefer the exact unidirectional algo if not requested otherwise // don't use matrix_type, we only need it to set the right warnings for what will be used - bool has_time = - check_matrix_time(request, - options.prioritize_bidirectional() ? MatrixType::Cost : MatrixType::TimeDist); - if (has_time && !options.prioritize_bidirectional() && source_to_target_algorithm != COST_MATRIX) { - return tyr::serializeMatrix(request, timedistancematrix(), distance_scale, MatrixType::TimeDist); - } else if (has_time && options.prioritize_bidirectional() && + if (has_time && !request.options().prioritize_bidirectional() && + source_to_target_algorithm != COST_MATRIX) { + return &time_distance_matrix_; + } else if (has_time && request.options().prioritize_bidirectional() && source_to_target_algorithm != TIME_DISTANCE_MATRIX) { - return tyr::serializeMatrix(request, costmatrix(has_time), distance_scale, MatrixType::Cost); - } else if (matrix_type == MatrixType::Cost) { - // if this happens, the server config only allows for timedist matrix - if (has_time && !options.prioritize_bidirectional()) { + return &costmatrix_; + } else if (config_algo == Matrix::CostMatrix) { + if (has_time && !request.options().prioritize_bidirectional()) { add_warning(request, 301); } - return tyr::serializeMatrix(request, costmatrix(has_time), distance_scale, MatrixType::Cost); + return &costmatrix_; } else { - if (has_time && options.prioritize_bidirectional()) { + // if this happens, the server config only allows for timedist matrix + if (has_time && request.options().prioritize_bidirectional()) { add_warning(request, 300); } - return tyr::serializeMatrix(request, timedistancematrix(), distance_scale, MatrixType::TimeDist); + return &time_distance_matrix_; } } + +std::string thor_worker_t::matrix(Api& request) { + // time this whole method and save that statistic + auto _ = measure_scope_time(request); + + auto& options = *request.mutable_options(); + adjust_scores(options); + auto costing = parse_costing(request); + + bool has_time = + check_matrix_time(request, options.prioritize_bidirectional() ? Matrix::CostMatrix + : Matrix::TimeDistanceMatrix); + + // allow all algos to be cancelled + for (auto* alg : std::vector{ + &costmatrix_, + &time_distance_matrix_, + &time_distance_bss_matrix_, + }) { + alg->set_interrupt(interrupt); + alg->set_has_time(has_time); + } + + auto* algo = get_matrix_algorithm(request, has_time, costing); + LOG_INFO("matrix::" + std::string(algo->name())); + + // TODO(nils): TDMatrix doesn't care about either destonly or no_thru + if (algo->name() != "costmatrix") { + algo->SourceToTarget(request, *reader, mode_costing, mode, + max_matrix_distance.find(costing)->second); + return tyr::serializeMatrix(request); + } + + // for costmatrix try a second pass if the first didn't work out + valhalla::sif::cost_ptr_t cost = mode_costing[static_cast(mode)]; + cost->set_allow_destination_only(false); + cost->set_pass(0); + + if (!algo->SourceToTarget(request, *reader, mode_costing, mode, + max_matrix_distance.find(costing)->second) && + cost->AllowMultiPass() && costmatrix_allow_second_pass) { + // NOTE: we only look for unfound connections in a second pass; but + // if A -> B wasn't found and B -> A was, we still expand both for bidirectional efficiency + // TODO(nils): probably add filtered edges here too? + algo->Clear(); + cost->set_pass(1); + cost->RelaxHierarchyLimits(true); + cost->set_allow_destination_only(true); + cost->set_allow_conditional_destination(true); + algo->set_not_thru_pruning(false); + algo->SourceToTarget(request, *reader, mode_costing, mode, + max_matrix_distance.find(costing)->second); + + // add a warning that we needed to open destonly etc + add_warning(request, 400, get_unfound_indices(request.matrix().second_pass())); + }; + + return tyr::serializeMatrix(request); +} } // namespace thor } // namespace valhalla diff --git a/src/thor/multimodal.cc b/src/thor/multimodal.cc index 2f11a34f47..04eff8549d 100644 --- a/src/thor/multimodal.cc +++ b/src/thor/multimodal.cc @@ -3,7 +3,6 @@ #include "midgard/logging.h" #include "worker.h" #include -#include using namespace valhalla::baldr; using namespace valhalla::sif; @@ -43,8 +42,7 @@ MultiModalPathAlgorithm::MultiModalPathAlgorithm(const boost::property_tree::ptr : PathAlgorithm(config.get("max_reserved_labels_count_astar", kInitialEdgeLabelCountAstar), config.get("clear_reserved_memory", false)), - max_walking_dist_(0), max_label_count_(std::numeric_limits::max()), - mode_(travel_mode_t::kPedestrian), travel_type_(0) { + max_walking_dist_(0), mode_(travel_mode_t::kPedestrian), travel_type_(0) { } // Destructor @@ -741,21 +739,19 @@ bool MultiModalPathAlgorithm::ExpandFromNode(baldr::GraphReader& graphreader, // Check if lower cost path if (es->set() == EdgeSet::kTemporary) { - EdgeLabel& lab = edgelabels[es->index()]; + auto& lab = edgelabels[es->index()]; if (newcost.cost < lab.cost().cost) { - float newsortcost = lab.sortcost() - (lab.cost().cost - newcost.cost); - adjlist.decrease(es->index(), newsortcost); - lab.Update(pred_idx, newcost, newsortcost, walking_distance, transition_cost, - restriction_idx); + adjlist.decrease(es->index(), newcost.cost); + lab.Update(pred_idx, newcost, newcost.cost, walking_distance, restriction_idx); } continue; } // Add edge label, add to the adjacency list and set edge status uint32_t idx = edgelabels.size(); - edgelabels.emplace_back(pred_idx, edgeid, directededge, newcost, newcost.cost, 0.0f, mode_, - walking_distance, transition_cost, baldr::kInvalidRestriction, true, - false, InternalTurn::kNoTurn); + edgelabels.emplace_back(pred_idx, edgeid, directededge, newcost, newcost.cost, mode_, + walking_distance, baldr::kInvalidRestriction, false, false, + InternalTurn::kNoTurn); *es = {EdgeSet::kTemporary, idx}; adjlist.add(idx); } @@ -811,8 +807,8 @@ bool MultiModalPathAlgorithm::CanReachDestination(const valhalla::Location& dest Cost cost = costing->EdgeCost(diredge, tile) * ratio; // we cannot do transition_cost on this label yet because we have no predecessor, but when we find // it, we will do an update on it and set the real transition cost based on the path to it - edgelabels.emplace_back(kInvalidLabel, oppedge, diredge, cost, cost.cost, 0.0f, mode_, length, - Cost{}, baldr::kInvalidRestriction, true, false, InternalTurn::kNoTurn); + edgelabels.emplace_back(kInvalidLabel, oppedge, diredge, cost, cost.cost, mode_, length, + baldr::kInvalidRestriction, false, false, InternalTurn::kNoTurn); adjlist.add(label_idx); edgestatus.Set(oppedge, EdgeSet::kTemporary, label_idx, tile); label_idx++; diff --git a/src/thor/optimized_route_action.cc b/src/thor/optimized_route_action.cc index 2525a24c39..f80b080d57 100644 --- a/src/thor/optimized_route_action.cc +++ b/src/thor/optimized_route_action.cc @@ -28,11 +28,9 @@ void thor_worker_t::optimized_route(Api& request) { // Use CostMatrix to find costs from each location to every other location CostMatrix costmatrix; - std::vector td = - costmatrix.SourceToTarget(*options.mutable_sources(), *options.mutable_targets(), *reader, - mode_costing, mode, max_matrix_distance.find(costing)->second, - check_matrix_time(request, MatrixType::Cost), - options.date_time_type() == Options::invariant); + costmatrix.set_has_time(check_matrix_time(request, Matrix::CostMatrix)); + costmatrix.SourceToTarget(request, *reader, mode_costing, mode, + max_matrix_distance.find(costing)->second); // Return an error if any locations are totally unreachable const auto& correlated = @@ -41,7 +39,8 @@ void thor_worker_t::optimized_route(Api& request) { // Set time costs to send to Optimizer. std::vector time_costs; bool reachable = true; - for (size_t i = 0; i < td.size(); ++i) { + const auto tds = request.matrix().times(); + for (size_t i = 0; i < tds.size(); ++i) { // If any location is completely unreachable then we cant have a connected path if (i % correlated.size() == 0) { if (!reachable) { @@ -49,9 +48,9 @@ void thor_worker_t::optimized_route(Api& request) { }; reachable = false; } - reachable = reachable || td[i].time != kMaxCost; + reachable = reachable || tds.Get(i) != kMaxCost; // Keep the times for the reordering - time_costs.emplace_back(static_cast(td[i].time)); + time_costs.emplace_back(static_cast(tds.Get(i))); } Optimizer optimizer; diff --git a/src/thor/route_action.cc b/src/thor/route_action.cc index 1431fcc979..9d048ee216 100644 --- a/src/thor/route_action.cc +++ b/src/thor/route_action.cc @@ -2,6 +2,7 @@ #include #include "baldr/attributes_controller.h" +#include "baldr/datetime.h" #include "baldr/json.h" #include "baldr/rapidjson_utils.h" #include "midgard/constants.h" @@ -107,8 +108,8 @@ inline bool is_break_point(const valhalla::Location& l) { } inline bool is_highly_reachable(const valhalla::Location& loc, const valhalla::PathEdge& edge) { - return edge.inbound_reach() >= loc.minimum_reachability() && - edge.outbound_reach() >= loc.minimum_reachability(); + return static_cast(edge.inbound_reach()) >= loc.minimum_reachability() && + static_cast(edge.outbound_reach()) >= loc.minimum_reachability(); } template inline void remove_path_edges(valhalla::Location& loc, Predicate pred) { @@ -304,6 +305,7 @@ std::vector> thor_worker_t::get_path(PathAlgorithm* // If bidirectional A* disable use of destination-only edges on the // first pass. If there is a failure, we allow them on the second pass. // Other path algorithms can use destination-only edges on the first pass. + // TODO(nils): why not others with destonly pruning? it gets a 2nd pass as well cost->set_allow_destination_only(path_algorithm == &bidir_astar ? false : true); cost->set_pass(0); @@ -311,6 +313,7 @@ std::vector> thor_worker_t::get_path(PathAlgorithm* // Check if we should run a second pass pedestrian route with different A* // (to look for better routes where a ferry is taken) + // TODO(nils): how would a second pass find a better route, if it changes nothing ferry-related? bool ped_second_pass = false; if (!paths.empty() && (costing == "pedestrian" && path_algorithm->has_ferry())) { // DO NOT run a second pass on long routes due to performance issues @@ -359,6 +362,7 @@ void thor_worker_t::path_arrive_by(Api& api, const std::string& costing) { valhalla::Trip& trip = *api.mutable_trip(); trip.mutable_routes()->Reserve(options.alternates() + 1); + graph_tile_ptr tile = nullptr; auto route_two_locations = [&](auto& origin, auto& destination) -> bool { // Get the algorithm type for this location pair thor::PathAlgorithm* path_algorithm = @@ -378,22 +382,30 @@ void thor_worker_t::path_arrive_by(Api& api, const std::string& costing) { auto temp_paths = this->get_path(path_algorithm, *origin, *destination, costing, options); if (temp_paths.empty()) return false; - for (auto& temp_path : temp_paths) { - // back propagate time information - if (!destination->date_time().empty() && - options.date_time_type() != valhalla::Options::invariant) { - auto origin_dt = offset_date(*reader, destination->date_time(), temp_path.back().edgeid, - -temp_path.back().elapsed_cost.secs, temp_path.front().edgeid); - origin->set_date_time(origin_dt); + auto out_tz = reader->GetTimezoneFromEdge(temp_path.back().edgeid, tile); + auto in_tz = reader->GetTimezoneFromEdge(temp_path.front().edgeid, tile); + + // we add the timezone info if destination is the last location + // and add waiting_secs again from the final destination's datetime, so we output the departing + // time at intermediate locations, not the arrival time + if ((destination->correlation().original_index() == + static_cast((options.locations().size() - 1)) && + (in_tz || out_tz))) { + auto destination_dt = DateTime::offset_date(destination->date_time(), out_tz, out_tz, + destination->waiting_secs()); + destination->set_date_time(destination_dt.date_time); + destination->set_time_zone_offset(destination_dt.time_zone_offset); + destination->set_time_zone_name(destination_dt.time_zone_name); } - // add waiting_secs again from the final destination's datetime, so we output the departing time - // at intermediate locations, not the arrival time - if (destination->waiting_secs() && !destination->date_time().empty()) { - auto dest_dt = offset_date(*reader, destination->date_time(), temp_path.back().edgeid, - destination->waiting_secs(), temp_path.back().edgeid); - destination->set_date_time(dest_dt); + // back propagate time information + if (!destination->date_time().empty()) { + auto origin_dt = DateTime::offset_date(destination->date_time(), out_tz, in_tz, + -temp_path.back().elapsed_cost.secs); + origin->set_date_time(origin_dt.date_time); + origin->set_time_zone_offset(origin_dt.time_zone_offset); + origin->set_time_zone_name(origin_dt.time_zone_name); } first_edge = temp_path.front().edgeid; @@ -457,10 +469,13 @@ void thor_worker_t::path_arrive_by(Api& api, const std::string& costing) { // advance the time for the next destination (i.e. algo origin) by the waiting_secs // of this origin (i.e. algo destination) - if (origin->waiting_secs()) { - auto origin_dt = offset_date(*reader, origin->date_time(), path.front().edgeid, - -origin->waiting_secs(), path.front().edgeid); - origin->set_date_time(origin_dt); + // TODO(nils): why do we do this twice? above we also do it for a destination.. + if (origin->waiting_secs() && !origin->date_time().empty()) { + auto origin_dt = + DateTime::offset_date(origin->date_time(), in_tz, in_tz, -origin->waiting_secs()); + origin->set_date_time(origin_dt.date_time); + origin->set_time_zone_offset(origin_dt.time_zone_offset); + origin->set_time_zone_name(origin_dt.time_zone_name); } path.clear(); edge_trimming.clear(); @@ -532,6 +547,7 @@ void thor_worker_t::path_depart_at(Api& api, const std::string& costing) { valhalla::Trip& trip = *api.mutable_trip(); trip.mutable_routes()->Reserve(options.alternates() + 1); + graph_tile_ptr tile = nullptr; auto route_two_locations = [&, this](auto& origin, auto& destination) -> bool { // Get the algorithm type for this location pair thor::PathAlgorithm* path_algorithm = @@ -552,13 +568,25 @@ void thor_worker_t::path_depart_at(Api& api, const std::string& costing) { return false; for (auto& temp_path : temp_paths) { + + auto in_tz = reader->GetTimezoneFromEdge(temp_path.front().edgeid, tile); + auto out_tz = reader->GetTimezoneFromEdge(temp_path.back().edgeid, tile); + if ((origin->correlation().original_index() == 0) && (in_tz || out_tz)) { + auto origin_dt = DateTime::offset_date(origin->date_time(), in_tz, in_tz, 0); + + origin->set_date_time(origin_dt.date_time); + origin->set_time_zone_offset(origin_dt.time_zone_offset); + origin->set_time_zone_name(origin_dt.time_zone_name); + } // forward propagate time information - if (!origin->date_time().empty() && options.date_time_type() != valhalla::Options::invariant) { - auto destination_dt = - offset_date(*reader, origin->date_time(), temp_path.front().edgeid, - temp_path.back().elapsed_cost.secs + destination->waiting_secs(), - temp_path.back().edgeid); - destination->set_date_time(destination_dt); + if (!origin->date_time().empty() && (in_tz || out_tz)) { + float offset = (options.date_time_type() != valhalla::Options::invariant) + ? (temp_path.back().elapsed_cost.secs + destination->waiting_secs()) + : 0.0f; + auto destination_dt = DateTime::offset_date(origin->date_time(), in_tz, out_tz, offset); + destination->set_date_time(destination_dt.date_time); + destination->set_time_zone_offset(destination_dt.time_zone_offset); + destination->set_time_zone_name(destination_dt.time_zone_name); } last_edge = temp_path.back().edgeid; @@ -664,46 +692,5 @@ void thor_worker_t::path_depart_at(Api& api, const std::string& costing) { // assign changed locations *api.mutable_options()->mutable_locations() = std::move(correlated); } - -/** - * Offset a time by some number of seconds, optionally taking into account timezones at the origin & - * destination. - * - * @param reader graphreader for tile/edge/node access - * @param in_dt the input date time string - * @param in_edge the input edgeid (used for timezone lookup) - * @param offset the offset in seconds from the input date time string - * @param out_edge the output edgeid (used for timezone lookup) - * @return out_dt the time at the out_edge in local time after the offset is applied to the in_dt - */ -std::string thor_worker_t::offset_date(GraphReader& reader, - const std::string& in_dt, - const GraphId& in_edge, - float offset, - const GraphId& out_edge) { - uint32_t in_tz = 0; - uint32_t out_tz = 0; - // get the timezone of the input location - graph_tile_ptr tile = nullptr; - auto in_nodes = reader.GetDirectedEdgeNodes(in_edge, tile); - if (const auto* node = reader.nodeinfo(in_nodes.first, tile)) - in_tz = node->timezone(); - else if (const auto* node = reader.nodeinfo(in_nodes.second, tile)) - in_tz = node->timezone(); - - // get the timezone of the output location - auto out_nodes = reader.GetDirectedEdgeNodes(out_edge, tile); - if (const auto* node = reader.nodeinfo(out_nodes.first, tile)) - out_tz = node->timezone(); - else if (const auto* node = reader.nodeinfo(out_nodes.second, tile)) - out_tz = node->timezone(); - - // offset the time - uint64_t in_epoch = DateTime::seconds_since_epoch(in_dt, DateTime::get_tz_db().from_index(in_tz)); - double out_epoch = static_cast(in_epoch) + offset; - auto out_dt = DateTime::seconds_to_date(static_cast(out_epoch + .5), - DateTime::get_tz_db().from_index(out_tz), false); - return out_dt; -} } // namespace thor } // namespace valhalla diff --git a/src/thor/route_matcher.cc b/src/thor/route_matcher.cc index 904089896e..23fb1c5c23 100644 --- a/src/thor/route_matcher.cc +++ b/src/thor/route_matcher.cc @@ -205,10 +205,8 @@ bool expand_from_node(const mode_costing_t& mode_costing, de, {}, 0, - 0, mode, 0, - {}, kInvalidRestriction, true, static_cast(flow_sources & kDefaultFlowMask), @@ -411,10 +409,8 @@ bool RouteMatcher::FormPath(const sif::mode_costing_t& mode_costing, de, {}, 0, - 0, mode, 0, - {}, baldr::kInvalidRestriction, true, static_cast(flow_sources & kDefaultFlowMask), diff --git a/src/thor/status_action.cc b/src/thor/status_action.cc index 8602b9cfdb..a96b30eb2a 100644 --- a/src/thor/status_action.cc +++ b/src/thor/status_action.cc @@ -3,7 +3,7 @@ namespace valhalla { namespace thor { void thor_worker_t::status(Api&) const { -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES // if we are in the process of shutting down we signal that here // should react by draining traffic (though they are likely doing this as they are usually the ones // who sent us the request to shutdown) diff --git a/src/thor/timedistancebssmatrix.cc b/src/thor/timedistancebssmatrix.cc index 75fdf40a3a..b5493c8b11 100644 --- a/src/thor/timedistancebssmatrix.cc +++ b/src/thor/timedistancebssmatrix.cc @@ -7,22 +7,6 @@ using namespace valhalla::baldr; using namespace valhalla::sif; namespace { -static bool IsTrivial(const uint64_t& edgeid, - const valhalla::Location& origin, - const valhalla::Location& destination) { - for (const auto& destination_edge : destination.correlation().edges()) { - if (destination_edge.graph_id() == edgeid) { - for (const auto& origin_edge : origin.correlation().edges()) { - if (origin_edge.graph_id() == edgeid && - origin_edge.percent_along() <= destination_edge.percent_along()) { - return true; - } - } - } - } - return false; -} - static travel_mode_t get_other_travel_mode(const travel_mode_t current_mode) { static const auto bss_modes = std::vector{travel_mode_t::kPedestrian, travel_mode_t::kBicycle}; @@ -35,7 +19,7 @@ namespace thor { // Constructor with cost threshold. TimeDistanceBSSMatrix::TimeDistanceBSSMatrix(const boost::property_tree::ptree& config) - : settled_count_(0), current_cost_threshold_(0), + : MatrixAlgorithm(config), settled_count_(0), current_cost_threshold_(0), max_reserved_labels_count_(config.get("max_reserved_labels_count_dijkstras", kInitialEdgeLabelCountDijkstras)), clear_reserved_memory_(config.get("clear_reserved_memory", false)) { @@ -110,7 +94,7 @@ void TimeDistanceBSSMatrix::Expand(GraphReader& graphreader, // Skip this edge if permanently labeled (best path already found to this // directed edge), if no access is allowed to this edge (based on costing // method), or if a complex restriction prevents this path. - uint8_t restriction_idx = -1; + uint8_t restriction_idx = kInvalidRestriction; const bool is_dest = dest_edges_.find(edgeid.value) != dest_edges_.cend(); if (FORWARD) { if (!current_costing->Allowed(directededge, is_dest, pred, tile, edgeid, 0, 0, @@ -138,26 +122,23 @@ void TimeDistanceBSSMatrix::Expand(GraphReader& graphreader, // Compute the cost to the end of this edge Cost newcost = pred.cost() + normalized_edge_cost + transition_cost; - uint32_t distance = pred.path_distance() + directededge->length(); + uint32_t path_distance = pred.path_distance() + directededge->length(); // Check if edge is temporarily labeled and this path has less cost. If - // less cost the predecessor is updated and the sort cost is decremented - // by the difference in real cost (A* heuristic doesn't change) + // less cost the cost and predecessor are updated. if (es->set() == EdgeSet::kTemporary) { - EdgeLabel& lab = edgelabels_[es->index()]; + auto& lab = edgelabels_[es->index()]; if (newcost.cost < lab.cost().cost) { - float newsortcost = lab.sortcost() - (lab.cost().cost - newcost.cost); - adjacencylist_.decrease(es->index(), newsortcost); - lab.Update(pred_idx, newcost, newsortcost, transition_cost, restriction_idx); + adjacencylist_.decrease(es->index(), newcost.cost); + lab.Update(pred_idx, newcost, newcost.cost, path_distance, restriction_idx); } continue; } // Add to the adjacency list and edge labels. uint32_t idx = edgelabels_.size(); - edgelabels_.emplace_back(pred_idx, edgeid, directededge, newcost, newcost.cost, 0.0f, mode, - distance, transition_cost, restriction_idx, true, false, - InternalTurn::kNoTurn); + edgelabels_.emplace_back(pred_idx, edgeid, directededge, newcost, newcost.cost, mode, + path_distance, restriction_idx, false, false, InternalTurn::kNoTurn); *es = {EdgeSet::kTemporary, idx}; adjacencylist_.add(idx); } @@ -181,15 +162,16 @@ void TimeDistanceBSSMatrix::Expand(GraphReader& graphreader, // Calculate time and distance from one origin location to many destination // locations. template -std::vector TimeDistanceBSSMatrix::ComputeMatrix( - const google::protobuf::RepeatedPtrField& source_location_list, - const google::protobuf::RepeatedPtrField& target_location_list, - baldr::GraphReader& graphreader, - const float max_matrix_distance, - const uint32_t matrix_locations) { +bool TimeDistanceBSSMatrix::ComputeMatrix(Api& request, + baldr::GraphReader& graphreader, + const float max_matrix_distance) { + uint32_t matrix_locations = request.options().matrix_locations(); + // Run a series of one to many calls and concatenate the results. - const auto& origins = FORWARD ? source_location_list : target_location_list; - const auto& destinations = FORWARD ? target_location_list : source_location_list; + auto& origins = FORWARD ? *request.mutable_options()->mutable_sources() + : *request.mutable_options()->mutable_targets(); + auto& destinations = FORWARD ? *request.mutable_options()->mutable_targets() + : *request.mutable_options()->mutable_sources(); // Construct adjacency list, edge status, and done set. Set bucket size and // cost range based on DynamicCost. @@ -197,12 +179,12 @@ std::vector TimeDistanceBSSMatrix::ComputeMatrix( // Initialize destinations once for all origins InitDestinations(graphreader, destinations); + // reserve the PBF vectors + reserve_pbf_arrays(*request.mutable_matrix(), origins.size() * destinations.size()); - std::vector many_to_many(origins.size() * destinations.size()); - for (size_t origin_index = 0; origin_index < origins.size(); ++origin_index) { + for (int origin_index = 0; origin_index < origins.size(); ++origin_index) { edgelabels_.reserve(max_reserved_labels_count_); const auto& origin = origins.Get(origin_index); - std::vector one_to_many; current_cost_threshold_ = GetCostThreshold(max_matrix_distance); adjacencylist_.reuse(0.0f, current_cost_threshold_, bucketsize, &edgelabels_); @@ -212,6 +194,7 @@ std::vector TimeDistanceBSSMatrix::ComputeMatrix( SetOrigin(graphreader, origin); SetDestinationEdges(); + uint32_t n = 0; // Find shortest path graph_tile_ptr tile; while (true) { @@ -220,7 +203,7 @@ std::vector TimeDistanceBSSMatrix::ComputeMatrix( uint32_t predindex = adjacencylist_.pop(); if (predindex == kInvalidLabel) { // Can not expand any further... - one_to_many = FormTimeDistanceMatrix(); + FormTimeDistanceMatrix(request, FORWARD, origin_index); break; } @@ -247,52 +230,41 @@ std::vector TimeDistanceBSSMatrix::ComputeMatrix( const DirectedEdge* edge = tile->directededge(pred.edgeid()); if (UpdateDestinations(origin, destinations, destedge->second, edge, tile, pred, matrix_locations)) { - one_to_many = FormTimeDistanceMatrix(); + FormTimeDistanceMatrix(request, FORWARD, origin_index); break; } } // Terminate when we are beyond the cost threshold if (pred.cost().cost > current_cost_threshold_) { - one_to_many = FormTimeDistanceMatrix(); + FormTimeDistanceMatrix(request, FORWARD, origin_index); break; } // Expand forward from the end node of the predecessor edge. Expand(graphreader, pred.endnode(), pred, predindex, false, false, pred.mode()); - } - // Insert one-to-many into many-to-many - if (FORWARD) { - for (size_t target_index = 0; target_index < destinations.size(); target_index++) { - size_t index = origin_index * origins.size() + target_index; - many_to_many[index] = one_to_many[target_index]; - } - } else { - for (size_t source_index = 0; source_index < destinations.size(); source_index++) { - size_t index = source_index * origins.size() + origin_index; - many_to_many[index] = one_to_many[source_index]; + // Allow this process to be aborted + if (interrupt_ && (n++ % kInterruptIterationsInterval) == 0) { + (*interrupt_)(); } } reset(); } - return many_to_many; + + // TODO(nils): not sure a second pass would make for BSS + return true; } -template std::vector TimeDistanceBSSMatrix::ComputeMatrix( - const google::protobuf::RepeatedPtrField& source_location_list, - const google::protobuf::RepeatedPtrField& target_location_list, - baldr::GraphReader& graphreader, - const float max_matrix_distance, - const uint32_t matrix_locations); -template std::vector -TimeDistanceBSSMatrix::ComputeMatrix( - const google::protobuf::RepeatedPtrField& source_location_list, - const google::protobuf::RepeatedPtrField& target_location_list, - baldr::GraphReader& graphreader, - const float max_matrix_distance, - const uint32_t matrix_locations); +template bool +TimeDistanceBSSMatrix::ComputeMatrix(Api& request, + baldr::GraphReader& graphreader, + const float max_matrix_distance); +template bool +TimeDistanceBSSMatrix::ComputeMatrix(Api& request, + baldr::GraphReader& graphreader, + const float max_matrix_distance); // Add edges at the origin to the adjacency list template @@ -343,11 +315,11 @@ void TimeDistanceBSSMatrix::SetOrigin(GraphReader& graphreader, const valhalla:: dist = static_cast(directededge->length() * percent_along); } else { - opp_edge_id = graphreader.GetOpposingEdgeId(edgeid); + opp_edge_id = graphreader.GetOpposingEdgeId(edgeid, endtile); if (!opp_edge_id.Is_Valid()) { continue; } - opp_dir_edge = graphreader.GetOpposingEdge(edgeid); + opp_dir_edge = graphreader.GetOpposingEdge(edgeid, endtile); cost = pedestrian_costing_->EdgeCost(opp_dir_edge, endtile, time_info, flow_sources) * edge.percent_along(); dist = static_cast(directededge->length() * edge.percent_along()); @@ -362,13 +334,13 @@ void TimeDistanceBSSMatrix::SetOrigin(GraphReader& graphreader, const valhalla:: // Set the predecessor edge index to invalid to indicate the origin // of the path. Set the origin flag if (FORWARD) { - edgelabels_.emplace_back(kInvalidLabel, edgeid, directededge, cost, cost.cost, 0.0f, - travel_mode_t::kPedestrian, dist, Cost{}, baldr::kInvalidRestriction, - true, false, InternalTurn::kNoTurn); + edgelabels_.emplace_back(kInvalidLabel, edgeid, directededge, cost, cost.cost, + travel_mode_t::kPedestrian, dist, baldr::kInvalidRestriction, false, + false, InternalTurn::kNoTurn); } else { - edgelabels_.emplace_back(kInvalidLabel, opp_edge_id, opp_dir_edge, cost, cost.cost, 0.0f, - travel_mode_t::kPedestrian, dist, Cost{}, baldr::kInvalidRestriction, - true, false, InternalTurn::kNoTurn); + edgelabels_.emplace_back(kInvalidLabel, opp_edge_id, opp_dir_edge, cost, cost.cost, + travel_mode_t::kPedestrian, dist, baldr::kInvalidRestriction, false, + false, InternalTurn::kNoTurn); } edgelabels_.back().set_origin(); adjacencylist_.add(edgelabels_.size() - 1); @@ -537,12 +509,29 @@ bool TimeDistanceBSSMatrix::UpdateDestinations( } // Form the time, distance matrix from the destinations list -std::vector TimeDistanceBSSMatrix::FormTimeDistanceMatrix() { - std::vector td; - for (auto& dest : destinations_) { - td.emplace_back(dest.best_cost.secs, dest.distance); +void TimeDistanceBSSMatrix::FormTimeDistanceMatrix(Api& request, + const bool forward, + const uint32_t origin_index) { + valhalla::Matrix& matrix = *request.mutable_matrix(); + for (uint32_t i = 0; i < destinations_.size(); i++) { + auto& dest = destinations_[i]; + float time = dest.best_cost.secs + .5f; + auto pbf_idx = forward ? (origin_index * request.options().targets().size()) + i + : (i * request.options().targets().size()) + origin_index; + matrix.mutable_from_indices()->Set(pbf_idx, forward ? origin_index : i); + matrix.mutable_to_indices()->Set(pbf_idx, forward ? i : origin_index); + matrix.mutable_distances()->Set(pbf_idx, dest.distance); + matrix.mutable_times()->Set(pbf_idx, time); + + // TODO - support date_time and time zones as in timedistancematrix. + // For now, add empty strings (serializer requires this) to prevent crashing + auto* pbf_dt = matrix.mutable_date_times()->Add(); + *pbf_dt = ""; + auto* pbf_tz_offset = matrix.mutable_time_zone_offsets()->Add(); + *pbf_tz_offset = ""; + auto* pbf_tz_names = matrix.mutable_time_zone_names()->Add(); + *pbf_tz_names = ""; } - return td; } } // namespace thor diff --git a/src/thor/timedistancematrix.cc b/src/thor/timedistancematrix.cc index f3deb25e52..e6176b8a6e 100644 --- a/src/thor/timedistancematrix.cc +++ b/src/thor/timedistancematrix.cc @@ -1,38 +1,22 @@ -#include "thor/timedistancematrix.h" -#include "midgard/logging.h" #include #include +#include "baldr/datetime.h" +#include "midgard/logging.h" +#include "thor/timedistancematrix.h" + using namespace valhalla::baldr; using namespace valhalla::sif; -namespace { -static bool IsTrivial(const uint64_t& edgeid, - const valhalla::Location& origin, - const valhalla::Location& destination) { - for (const auto& destination_edge : destination.correlation().edges()) { - if (destination_edge.graph_id() == edgeid) { - for (const auto& origin_edge : origin.correlation().edges()) { - if (origin_edge.graph_id() == edgeid && - origin_edge.percent_along() <= destination_edge.percent_along()) { - return true; - } - } - } - } - return false; -} -} // namespace - namespace valhalla { namespace thor { // Constructor with cost threshold. TimeDistanceMatrix::TimeDistanceMatrix(const boost::property_tree::ptree& config) - : mode_(travel_mode_t::kDrive), settled_count_(0), current_cost_threshold_(0), + : MatrixAlgorithm(config), settled_count_(0), current_cost_threshold_(0), max_reserved_labels_count_(config.get("max_reserved_labels_count_dijkstras", kInitialEdgeLabelCountDijkstras)), - clear_reserved_memory_(config.get("clear_reserved_memory", false)) { + mode_(travel_mode_t::kDrive) { } // Compute a cost threshold in seconds based on average speed for the travel mode. @@ -72,6 +56,7 @@ void TimeDistanceMatrix::Expand(GraphReader& graphreader, return; } const NodeInfo* nodeinfo = tile->node(node); + // TODO(nils): handle deadends in this algo, this should be flagged as one too if (!costing_->Allowed(nodeinfo)) { return; } @@ -120,7 +105,7 @@ void TimeDistanceMatrix::Expand(GraphReader& graphreader, // Skip this edge if permanently labeled (best path already found to this // directed edge), if no access is allowed to this edge (based on costing // method), or if a complex restriction prevents this path. - uint8_t restriction_idx = -1; + uint8_t restriction_idx = kInvalidRestriction; const bool is_dest = dest_edges_.find(edgeid) != dest_edges_.cend(); if (FORWARD) { if (!costing_->Allowed(directededge, is_dest, pred, tile, edgeid, offset_time.local_time, @@ -149,31 +134,43 @@ void TimeDistanceMatrix::Expand(GraphReader& graphreader, static_cast(flow_sources & kDefaultFlowMask), pred.internal_turn()); newcost += pred.cost() + transition_cost; - uint32_t distance = pred.path_distance() + directededge->length(); + uint32_t path_distance = pred.path_distance() + directededge->length(); // Check if edge is temporarily labeled and this path has less cost. If - // less cost the predecessor is updated and the sort cost is decremented - // by the difference in real cost (A* heuristic doesn't change) + // less cost the cost and predecessor are updated. if (es->set() == EdgeSet::kTemporary) { - EdgeLabel& lab = edgelabels_[es->index()]; + auto& lab = edgelabels_[es->index()]; if (newcost.cost < lab.cost().cost) { - float newsortcost = lab.sortcost() - (lab.cost().cost - newcost.cost); - adjacencylist_.decrease(es->index(), newsortcost); - lab.Update(pred_idx, newcost, newsortcost, distance, transition_cost, restriction_idx); + adjacencylist_.decrease(es->index(), newcost.cost); + lab.Update(pred_idx, newcost, newcost.cost, path_distance, restriction_idx); } continue; } // Add to the adjacency list and edge labels. uint32_t idx = edgelabels_.size(); - sif::InternalTurn turn_type = - FORWARD ? turn_type = costing_->TurnType(pred.opp_local_idx(), nodeinfo, directededge) - : costing_->TurnType(directededge->localedgeidx(), nodeinfo, opp_edge, opp_pred_edge); - - edgelabels_.emplace_back(pred_idx, edgeid, directededge, newcost, newcost.cost, 0.0f, mode_, - distance, transition_cost, restriction_idx, - (pred.closure_pruning() || !costing_->IsClosed(directededge, tile)), - static_cast(flow_sources & kDefaultFlowMask), turn_type); + if (FORWARD) { + edgelabels_.emplace_back(pred_idx, edgeid, directededge, newcost, newcost.cost, mode_, + path_distance, restriction_idx, + (pred.closure_pruning() || !(costing_->IsClosed(directededge, tile))), + 0 != (flow_sources & kDefaultFlowMask), + costing_->TurnType(pred.opp_local_idx(), nodeinfo, directededge), 0, + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); + } else { + edgelabels_.emplace_back(pred_idx, edgeid, directededge, newcost, newcost.cost, mode_, + path_distance, restriction_idx, + (pred.closure_pruning() || !(costing_->IsClosed(opp_edge, t2))), + 0 != (flow_sources & kDefaultFlowMask), + costing_->TurnType(directededge->localedgeidx(), nodeinfo, opp_edge, + opp_pred_edge), + 0, + opp_edge->destonly() || + (costing_->is_hgv() && opp_edge->destonly_hgv()), + opp_edge->forwardaccess() & kTruckAccess); + } + *es = {EdgeSet::kTemporary, idx}; adjacencylist_.add(idx); } @@ -188,27 +185,33 @@ void TimeDistanceMatrix::Expand(GraphReader& graphreader, } template -std::vector TimeDistanceMatrix::ComputeMatrix( - google::protobuf::RepeatedPtrField& origins, - google::protobuf::RepeatedPtrField& destinations, - baldr::GraphReader& graphreader, - const float max_matrix_distance, - const uint32_t matrix_locations, - const bool invariant) { +bool TimeDistanceMatrix::ComputeMatrix(Api& request, + baldr::GraphReader& graphreader, + const float max_matrix_distance) { + bool invariant = request.options().date_time_type() == Options::invariant; + uint32_t matrix_locations = request.options().matrix_locations(); + uint32_t bucketsize = costing_->UnitSize(); + + auto& origins = FORWARD ? *request.mutable_options()->mutable_sources() + : *request.mutable_options()->mutable_targets(); + auto& destinations = FORWARD ? *request.mutable_options()->mutable_targets() + : *request.mutable_options()->mutable_sources(); + + size_t num_elements = origins.size() * destinations.size(); auto time_infos = SetTime(origins, graphreader); // Initialize destinations once for all origins InitDestinations(graphreader, destinations); + // reserve the PBF vectors + reserve_pbf_arrays(*request.mutable_matrix(), num_elements, costing_->pass()); - std::vector many_to_many(origins.size() * destinations.size()); - for (size_t origin_index = 0; origin_index < origins.size(); ++origin_index) { + for (int origin_index = 0; origin_index < origins.size(); ++origin_index) { // reserve some space for the next dijkstras (will be cleared at the end of the loop) edgelabels_.reserve(max_reserved_labels_count_); auto& origin = origins.Get(origin_index); const auto& time_info = time_infos[origin_index]; - std::vector one_to_many; current_cost_threshold_ = GetCostThreshold(max_matrix_distance); // Construct adjacency list. Set bucket size and cost range based on DynamicCost. @@ -219,6 +222,11 @@ std::vector TimeDistanceMatrix::ComputeMatrix( SetOrigin(graphreader, origin, time_info); SetDestinationEdges(); + uint32_t n = 0; + // Collect edge_ids used for settling a location to determine its time zone + std::unordered_map dest_edge_ids; + dest_edge_ids.reserve(destinations.size()); + // Find shortest path graph_tile_ptr tile; while (true) { @@ -227,8 +235,8 @@ std::vector TimeDistanceMatrix::ComputeMatrix( uint32_t predindex = adjacencylist_.pop(); if (predindex == kInvalidLabel) { // Can not expand any further... - one_to_many = FormTimeDistanceMatrix(graphreader, origin.date_time(), - time_info.timezone_index, GraphId{}); + FormTimeDistanceMatrix(request, graphreader, FORWARD, origin_index, origin.date_time(), + time_info.timezone_index, dest_edge_ids); break; } @@ -250,58 +258,50 @@ std::vector TimeDistanceMatrix::ComputeMatrix( // have been settled or the requested amount of destinations has been found tile = graphreader.GetGraphTile(pred.edgeid()); const DirectedEdge* edge = tile->directededge(pred.edgeid()); + + for (auto& dest_id : destedge->second) { + dest_edge_ids[dest_id] = pred.edgeid(); + } if (UpdateDestinations(origin, destinations, destedge->second, edge, tile, pred, time_info, matrix_locations)) { - one_to_many = FormTimeDistanceMatrix(graphreader, origin.date_time(), - time_info.timezone_index, pred.edgeid()); + FormTimeDistanceMatrix(request, graphreader, FORWARD, origin_index, origin.date_time(), + time_info.timezone_index, dest_edge_ids); break; } } // Terminate when we are beyond the cost threshold if (pred.cost().cost > current_cost_threshold_) { - one_to_many = FormTimeDistanceMatrix(graphreader, origin.date_time(), - time_info.timezone_index, pred.edgeid()); + FormTimeDistanceMatrix(request, graphreader, FORWARD, origin_index, origin.date_time(), + time_info.timezone_index, dest_edge_ids); break; } // Expand forward from the end node of the predecessor edge. Expand(graphreader, pred.endnode(), pred, predindex, false, time_info, invariant); - } - // Insert one-to-many into many-to-many - if (FORWARD) { - for (size_t target_index = 0; target_index < destinations.size(); target_index++) { - size_t index = origin_index * origins.size() + target_index; - many_to_many[index] = one_to_many[target_index]; - } - } else { - for (size_t source_index = 0; source_index < destinations.size(); source_index++) { - size_t index = source_index * origins.size() + origin_index; - many_to_many[index] = one_to_many[source_index]; + // Allow this process to be aborted + if (interrupt_ && (n++ % kInterruptIterationsInterval) == 0) { + (*interrupt_)(); } } + reset(); } - return many_to_many; + // TODO(nils): implement second pass here too + return true; } -template std::vector TimeDistanceMatrix::ComputeMatrix( - google::protobuf::RepeatedPtrField& origins, - google::protobuf::RepeatedPtrField& destinations, - baldr::GraphReader& graphreader, - const float max_matrix_distance, - const uint32_t matrix_locations, - const bool invariant); -template std::vector TimeDistanceMatrix::ComputeMatrix( - google::protobuf::RepeatedPtrField& origins, - google::protobuf::RepeatedPtrField& destinations, - baldr::GraphReader& graphreader, - const float max_matrix_distance, - const uint32_t matrix_locations, - const bool invariant); +template bool +TimeDistanceMatrix::ComputeMatrix(Api& request, + baldr::GraphReader& graphreader, + const float max_matrix_distance); +template bool +TimeDistanceMatrix::ComputeMatrix(Api& request, + baldr::GraphReader& graphreader, + const float max_matrix_distance); // Add edges at the origin to the adjacency list template @@ -371,17 +371,21 @@ void TimeDistanceMatrix::SetOrigin(GraphReader& graphreader, // Set the predecessor edge index to invalid to indicate the origin // of the path. Set the origin flag if (FORWARD) { - edgelabels_.emplace_back(kInvalidLabel, edgeid, directededge, cost, cost.cost, 0.0f, mode_, - dist, Cost{}, baldr::kInvalidRestriction, - !costing_->IsClosed(directededge, tile), + edgelabels_.emplace_back(kInvalidLabel, edgeid, directededge, cost, cost.cost, mode_, dist, + baldr::kInvalidRestriction, !costing_->IsClosed(directededge, tile), static_cast(flow_sources & kDefaultFlowMask), - InternalTurn::kNoTurn); + InternalTurn::kNoTurn, 0, + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); } else { - edgelabels_.emplace_back(kInvalidLabel, opp_edge_id, opp_dir_edge, cost, cost.cost, 0.0f, mode_, - dist, Cost{}, baldr::kInvalidRestriction, - !costing_->IsClosed(directededge, tile), + edgelabels_.emplace_back(kInvalidLabel, opp_edge_id, opp_dir_edge, cost, cost.cost, mode_, dist, + baldr::kInvalidRestriction, !costing_->IsClosed(directededge, tile), static_cast(flow_sources & kDefaultFlowMask), - InternalTurn::kNoTurn); + InternalTurn::kNoTurn, 0, + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); } edgelabels_.back().set_origin(); adjacencylist_.add(edgelabels_.size() - 1); @@ -415,7 +419,6 @@ void TimeDistanceMatrix::InitDestinations( // Form a threshold cost (the total cost to traverse the edge), also based on forward path for // REVERSE - GraphId id(static_cast(edge.graph_id())); graph_tile_ptr tile = graphreader.GetGraphTile(edgeid); const DirectedEdge* directededge = tile->directededge(edgeid); float c = costing_->EdgeCost(directededge, tile).cost; @@ -556,17 +559,33 @@ bool TimeDistanceMatrix::UpdateDestinations( } // Form the time, distance matrix from the destinations list -std::vector TimeDistanceMatrix::FormTimeDistanceMatrix(GraphReader& reader, - const std::string& origin_dt, - const uint64_t& origin_tz, - const GraphId& pred_id) { - std::vector td; - for (auto& dest : destinations_) { - auto date_time = get_date_time(origin_dt, origin_tz, pred_id, reader, - static_cast(dest.best_cost.secs + .5f)); - td.emplace_back(dest.best_cost.secs, dest.distance, date_time); +void TimeDistanceMatrix::FormTimeDistanceMatrix(Api& request, + GraphReader& reader, + const bool forward, + const uint32_t origin_index, + const std::string& origin_dt, + const uint64_t& origin_tz, + std::unordered_map& edge_ids) { + // when it's forward, origin_index will be the source_index + // when it's reverse, origin_index will be the target_index + valhalla::Matrix& matrix = *request.mutable_matrix(); + graph_tile_ptr tile; + for (uint32_t i = 0; i < destinations_.size(); i++) { + auto& dest = destinations_[i]; + auto pbf_idx = forward ? (origin_index * request.options().targets().size()) + i + : (i * request.options().targets().size()) + origin_index; + matrix.mutable_from_indices()->Set(pbf_idx, forward ? origin_index : i); + matrix.mutable_to_indices()->Set(pbf_idx, forward ? i : origin_index); + matrix.mutable_distances()->Set(pbf_idx, dest.distance); + matrix.mutable_times()->Set(pbf_idx, dest.best_cost.secs); + + auto dt_info = + DateTime::offset_date(origin_dt, origin_tz, reader.GetTimezoneFromEdge(edge_ids[i], tile), + static_cast(dest.best_cost.secs)); + *matrix.mutable_date_times(pbf_idx) = dt_info.date_time; + *matrix.mutable_time_zone_names(pbf_idx) = dt_info.time_zone_name; + *matrix.mutable_time_zone_offsets(pbf_idx) = dt_info.time_zone_offset; } - return td; } } // namespace thor diff --git a/src/thor/trace_attributes_action.cc b/src/thor/trace_attributes_action.cc index c0140368bd..14a79d859c 100644 --- a/src/thor/trace_attributes_action.cc +++ b/src/thor/trace_attributes_action.cc @@ -1,5 +1,4 @@ #include -#include #include #include #include diff --git a/src/thor/trace_route_action.cc b/src/thor/trace_route_action.cc index 8d466eec1f..a8ebd7a6f4 100644 --- a/src/thor/trace_route_action.cc +++ b/src/thor/trace_route_action.cc @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -263,20 +262,22 @@ void thor_worker_t::build_trace( // here we enumerate the discontinuities and set the edge index of each input trace point std::unordered_map> edge_trimming; baldr::GraphId last_id; - size_t edge_index = 0; + size_t edge_index = -1; for (const auto& path : paths) { // remember the global edge index of every input point for (const auto* segment : path.second) { + if (last_id != segment->edgeid) { + ++edge_index; + } // they can be -1,-1 or l,-1 or -1,h or l,h for (int low = (segment->first_match_idx >= 0 ? segment->first_match_idx : segment->last_match_idx), high = (segment->last_match_idx >= 0 ? segment->last_match_idx : segment->first_match_idx); low >= 0 && low <= high; ++low) { - match_results[low].edge_index = edge_index; - } - if (last_id != segment->edgeid) { - ++edge_index; + // assign edge_index if edgeid is correct + if (match_results[low].edgeid == segment->edgeid) + match_results[low].edge_index = edge_index; } last_id = segment->edgeid; } diff --git a/src/thor/triplegbuilder.cc b/src/thor/triplegbuilder.cc index d09212034f..742504f905 100644 --- a/src/thor/triplegbuilder.cc +++ b/src/thor/triplegbuilder.cc @@ -1,8 +1,6 @@ #include #include #include -#include -#include #include #include #include @@ -11,10 +9,12 @@ #include "baldr/datetime.h" #include "baldr/edgeinfo.h" #include "baldr/graphconstants.h" +#include "baldr/landmark.h" #include "baldr/signinfo.h" #include "baldr/tilehierarchy.h" #include "baldr/time_info.h" #include "meili/match_result.h" +#include "midgard/elevation_encoding.h" #include "midgard/encoded.h" #include "midgard/logging.h" #include "midgard/pointll.h" @@ -33,6 +33,12 @@ using namespace valhalla::thor; namespace { +using LinguisticMap = std::unordered_map>; + +constexpr uint8_t kNotTagged = 0; +constexpr uint8_t kTunnelTag = static_cast(baldr::TaggedValue::kTunnel); +constexpr uint8_t kBridgeTag = static_cast(baldr::TaggedValue::kBridge); + uint32_t GetAdminIndex(const AdminInfo& admin_info, std::unordered_map& admin_info_map, @@ -481,24 +487,92 @@ void SetHeadings(TripLeg_Edge* trip_edge, } } +/** + * Add landmarks in the directed edge to trip edge. + * @param edgeinfo Edge info of the directed edge. + * @param trip_edge Trip path edge to add landmarks. + * @param controller Controller specifying whether we want landmarks in the graph to come out the + * other side. + * @param edge Directed edge where the landmarks are stored. + * @param shape Trip shape. + */ +void AddLandmarks(const EdgeInfo& edgeinfo, + TripLeg_Edge* trip_edge, + const AttributesController& controller, + const DirectedEdge* edge, + const std::vector& shape, + const uint32_t begin_index) { + if (!controller(kEdgeLandmarks)) { + return; + } + + for (const auto& tag : edgeinfo.GetTags()) { + // get landmarks from tagged values in the edge info + if (tag.first == baldr::TaggedValue::kLandmark) { + Landmark lan(tag.second); + PointLL landmark_point = {lan.lng, lan.lat}; + + // find the closed point on edge to the landmark + auto closest = landmark_point.ClosestPoint(shape, begin_index); + // TODO: in the future maybe we could allow a request option to have a tighter threshold on + // how far landmarks should be away from an edge + + // add the landmark to trip leg + auto* landmark = trip_edge->mutable_landmarks()->Add(); + landmark->set_name(lan.name); + landmark->set_type(static_cast(lan.type)); + landmark->mutable_lat_lng()->set_lng(lan.lng); + landmark->mutable_lat_lng()->set_lat(lan.lat); + + // calculate the landmark's distance along the edge + // that is to accumulate distance from the begin point to the closest point to it on the edge + int closest_idx = std::get<2>(closest); + double distance_along_edge = 0; + for (int idx = begin_index + 1; idx <= closest_idx; ++idx) { + distance_along_edge += shape[idx].Distance(shape[idx - 1]); + } + distance_along_edge += shape[closest_idx].Distance(std::get<0>(closest)); + // the overall distance shouldn't be larger than edge length + distance_along_edge = std::min(distance_along_edge, static_cast(edge->length())); + landmark->set_distance(distance_along_edge); + // check which side of the edge the landmark is on + // quirks of the ClosestPoint function + bool is_right = closest_idx == (int)shape.size() - 1 + ? landmark_point.IsLeft(shape[closest_idx - 1], shape[closest_idx]) < 0 + : landmark_point.IsLeft(shape[closest_idx], shape[closest_idx + 1]) < 0; + landmark->set_right(is_right); + } + } +} + // Populate the specified sign element with the specified sign attributes including pronunciation // attributes if they exist -void PopulateSignElement( - uint32_t sign_index, - const SignInfo& sign, - const std::unordered_map>& pronunciations, - valhalla::TripSignElement* sign_element) { +void PopulateSignElement(uint32_t sign_index, + const SignInfo& sign, + const LinguisticMap& linguistics, + valhalla::TripSignElement* sign_element) { sign_element->set_text(sign.text()); sign_element->set_is_route_number(sign.is_route_num()); // Assign pronunciation alphabet and value if they exist - std::unordered_map>::const_iterator iter = - pronunciations.find(sign_index); - if (iter != pronunciations.end()) { - auto* pronunciation = sign_element->mutable_pronunciation(); - pronunciation->set_alphabet(GetTripPronunciationAlphabet( - static_cast((iter->second).first))); - pronunciation->set_value((iter->second).second); + const auto iter = linguistics.find(sign_index); + if (iter != linguistics.end()) { + + // Lang saved with pronunciation + auto lang = static_cast(std::get(iter->second)); + if (lang != Language::kNone) { + sign_element->set_language_tag(GetTripLanguageTag(lang)); + } + + auto alphabet = static_cast( + std::get(iter->second)); + + if (alphabet != PronunciationAlphabet::kNone) { + + auto* pronunciation = sign_element->mutable_pronunciation(); + pronunciation->set_alphabet(GetTripPronunciationAlphabet(alphabet)); + pronunciation->set_value(std::get(iter->second)); + } } } @@ -506,7 +580,7 @@ void PopulateSignElement( // add per the attributes-controller. void AddSignInfo(const AttributesController& controller, const std::vector& edge_signs, - const std::unordered_map>& pronunciations, + const LinguisticMap& linguistics, valhalla::TripSign* trip_sign) { if (!edge_signs.empty()) { @@ -514,57 +588,57 @@ void AddSignInfo(const AttributesController& controller, for (const auto& sign : edge_signs) { switch (sign.type()) { case valhalla::baldr::Sign::Type::kExitNumber: { - if (controller(kEdgeSignExitNumber)) { - PopulateSignElement(sign_index, sign, pronunciations, + if (controller.attributes.at(kEdgeSignExitNumber)) { + PopulateSignElement(sign_index, sign, linguistics, trip_sign->mutable_exit_numbers()->Add()); } break; } case valhalla::baldr::Sign::Type::kExitBranch: { - if (controller(kEdgeSignExitBranch)) { - PopulateSignElement(sign_index, sign, pronunciations, + if (controller.attributes.at(kEdgeSignExitBranch)) { + PopulateSignElement(sign_index, sign, linguistics, trip_sign->mutable_exit_onto_streets()->Add()); } break; } case valhalla::baldr::Sign::Type::kExitToward: { - if (controller(kEdgeSignExitToward)) { - PopulateSignElement(sign_index, sign, pronunciations, + if (controller.attributes.at(kEdgeSignExitToward)) { + PopulateSignElement(sign_index, sign, linguistics, trip_sign->mutable_exit_toward_locations()->Add()); } break; } case valhalla::baldr::Sign::Type::kExitName: { - if (controller(kEdgeSignExitName)) { - PopulateSignElement(sign_index, sign, pronunciations, + if (controller.attributes.at(kEdgeSignExitName)) { + PopulateSignElement(sign_index, sign, linguistics, trip_sign->mutable_exit_names()->Add()); } break; } case valhalla::baldr::Sign::Type::kGuideBranch: { - if (controller(kEdgeSignGuideBranch)) { - PopulateSignElement(sign_index, sign, pronunciations, + if (controller.attributes.at(kEdgeSignGuideBranch)) { + PopulateSignElement(sign_index, sign, linguistics, trip_sign->mutable_guide_onto_streets()->Add()); } break; } case valhalla::baldr::Sign::Type::kGuideToward: { - if (controller(kEdgeSignGuideToward)) { - PopulateSignElement(sign_index, sign, pronunciations, + if (controller.attributes.at(kEdgeSignGuideToward)) { + PopulateSignElement(sign_index, sign, linguistics, trip_sign->mutable_guide_toward_locations()->Add()); } break; } case valhalla::baldr::Sign::Type::kGuidanceViewJunction: { - if (controller(kEdgeSignGuidanceViewJunction)) { - PopulateSignElement(sign_index, sign, pronunciations, + if (controller.attributes.at(kEdgeSignGuidanceViewJunction)) { + PopulateSignElement(sign_index, sign, linguistics, trip_sign->mutable_guidance_view_junctions()->Add()); } break; } case valhalla::baldr::Sign::Type::kGuidanceViewSignboard: { - if (controller(kEdgeSignGuidanceViewSignboard)) { - PopulateSignElement(sign_index, sign, pronunciations, + if (controller.attributes.at(kEdgeSignGuidanceViewSignboard)) { + PopulateSignElement(sign_index, sign, linguistics, trip_sign->mutable_guidance_view_signboards()->Add()); } break; @@ -578,6 +652,130 @@ void AddSignInfo(const AttributesController& controller, } } +void FilterUnneededStreetNumbers( + std::vector>& names_and_types) { + if (names_and_types.size() < 2) { + return; + } + auto it = names_and_types.begin(); + while (it != names_and_types.end()) { + if (std::get<1>(*it) == true && names_and_types.size() > 1) { + it = names_and_types.erase(it); + } else { + it++; + } + } +} + +/** + * Set elevation along the edge if requested. + * @param trip_edge Trip path edge to add elevation. + * @param start_pct Start percent + * @param end_pct End percent + * @param start_node Nodeinfo for start of the edge. + * @param edge Directed edge + * @param tile Graph tile of the edge. + * @param graphreader Graphreader in case end node is in a different tile. + */ +void SetElevation(TripLeg_Edge* trip_edge, + const double start_pct, + const double end_pct, + const NodeInfo* start_node, + const DirectedEdge* edge, + const graph_tile_ptr& tile, + GraphReader& graphreader) { + + // Lambda to get elevation at specified distance + double interval = 0.0; + const auto find_elevation = [&interval](const std::vector elevation, const double d) { + // Find index based on the stored interval and the desired distance + uint32_t index = static_cast(d / interval); + if (index >= elevation.size() - 1) { + return elevation.back(); + } + + // Interpolate between this vertex and the next + double pct = (d / interval) - index; + return static_cast((elevation[index] * (1.0 - pct)) + (elevation[index + 1] * pct)); + }; + + // Add the elevation at the start node to the elevation vector + float h1 = start_node->elevation(); + + // Get encoded elevation from EdgeInfo edge + auto encoded = tile->edgeinfo(edge).encoded_elevation(edge->length(), interval); + + // Get the end node and its elevation (if end tile is null just set elevation at + // the end node to be same as at start node - this should be rare) + float h2 = h1; + auto end_tile = graphreader.GetGraphTile(edge->endnode()); + if (end_tile != nullptr) { + h2 = end_tile->node(edge->endnode())->elevation(); + } + + // Decode elevation and reverse if edge is not forward direction + std::vector elevation = decode_elevation(encoded, h1, h2, edge->forward()); + + // Add to trip edge (protect against both start and end percent == 0.0) + if (((0.0 < start_pct && start_pct < 1.0) || (0.0 < end_pct && end_pct < 1.0)) && + start_pct != end_pct) { + // Trim elevation - find a new sampling interval based on the partial length + double partial_length = static_cast(edge->length()) * (end_pct - start_pct); + double new_interval = sampling_interval(partial_length); + trip_edge->set_elevation_sampling_interval(new_interval); + + // Add elevation along this portion of the edge + double d = edge->length() * start_pct; + double end_distance = edge->length() * end_pct; + while (d < end_distance + kEpsilon) { + trip_edge->mutable_elevation()->Add(find_elevation(elevation, d)); + d += new_interval; + } + + // Validate new size + if (trip_edge->elevation_size() - 1 != static_cast(partial_length / new_interval)) { + LOG_ERROR("TRIMMED elevation is wrong size"); + } + } else { + // Set trip_edge sampling interval and elevation vector + trip_edge->set_elevation_sampling_interval(interval); + for (auto e : elevation) { + trip_edge->mutable_elevation()->Add(e); + } + } +} + +void ProcessNonTaggedValue(valhalla::StreetName* trip_edge_name, + const LinguisticMap& linguistics, + const std::tuple& name_and_type, + const uint8_t name_index) { + // Assign name and type + trip_edge_name->set_value(std::get<0>(name_and_type)); + trip_edge_name->set_is_route_number(std::get<1>(name_and_type)); + + const auto iter = linguistics.find(name_index); + + if (iter != linguistics.end()) { + + auto lang = static_cast(std::get(iter->second)); + if (lang != Language::kNone) { + trip_edge_name->set_language_tag(GetTripLanguageTag(lang)); + } + + auto alphabet = static_cast( + std::get(iter->second)); + + if (alphabet != PronunciationAlphabet::kNone) { + + auto* pronunciation = trip_edge_name->mutable_pronunciation(); + pronunciation->set_alphabet( + GetTripPronunciationAlphabet(static_cast( + std::get(iter->second)))); + pronunciation->set_value(std::get(iter->second)); + } + } +} + /** * Add trip intersecting edge. * @param controller Controller to determine which attributes to set. @@ -596,7 +794,8 @@ void AddTripIntersectingEdge(const AttributesController& controller, uint32_t local_edge_index, const NodeInfo* nodeinfo, TripLeg_Node* trip_node, - const DirectedEdge* intersecting_de) { + const DirectedEdge* intersecting_de, + bool blind_instructions) { TripLeg_IntersectingEdge* intersecting_edge = trip_node->add_intersecting_edge(); // Set the heading for the intersecting edge if requested @@ -652,6 +851,35 @@ void AddTripIntersectingEdge(const AttributesController& controller, intersecting_edge->set_curr_name_consistency(directededge->name_consistency(local_edge_index)); } + // Add names to edge if requested + if (controller.attributes.at(kEdgeNames)) { + + auto edgeinfo = graphtile->edgeinfo(intersecting_de); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + if (blind_instructions) { + FilterUnneededStreetNumbers(names_and_types); + } + intersecting_edge->mutable_name()->Reserve(names_and_types.size()); + const auto linguistics = edgeinfo.GetLinguisticMap(); + + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + switch (std::get<2>(name_and_type)) { + case kNotTagged: { + ProcessNonTaggedValue(intersecting_edge->mutable_name()->Add(), linguistics, name_and_type, + name_index); + break; + } + default: + // Skip the rest tagged names + LOG_TRACE(std::string("skipped tagged value= ") + + std::to_string(std::get<2>(name_and_type))); + break; + } + ++name_index; + } + } + // Set the use for the intersecting edge if requested if (controller(kNodeIntersectingEdgeUse)) { intersecting_edge->set_use(GetTripLegUse(intersecting_de->use())); @@ -670,12 +898,12 @@ void AddTripIntersectingEdge(const AttributesController& controller, // Set the sign info for the intersecting edge if requested if (controller(kNodeIntersectingEdgeSignInfo)) { if (intersecting_de->sign()) { - std::unordered_map> pronunciations; + LinguisticMap linguistics; std::vector edge_signs = - graphtile->GetSigns(intersecting_de - graphtile->directededge(0), pronunciations); + graphtile->GetSigns(intersecting_de - graphtile->directededge(0), linguistics); if (!edge_signs.empty()) { valhalla::TripSign* sign = intersecting_edge->mutable_sign(); - AddSignInfo(controller, edge_signs, pronunciations, sign); + AddSignInfo(controller, edge_signs, linguistics, sign); } } } @@ -692,6 +920,7 @@ void AddTripIntersectingEdge(const AttributesController& controller, * @param prior_opp_local_index opposing edge local index of previous edge in the path * @param graphreader graph reader for graph access * @param trip_node pbf node in the pbf structure we are building + * @param blind_instructions whether instructions for blind users are requested */ void AddIntersectingEdges(const AttributesController& controller, const graph_tile_ptr& start_tile, @@ -700,7 +929,8 @@ void AddIntersectingEdges(const AttributesController& controller, const DirectedEdge* prev_de, uint32_t prior_opp_local_index, GraphReader& graphreader, - valhalla::TripLeg::Node* trip_node) { + valhalla::TripLeg::Node* trip_node, + const bool blind_instructions) { /* Add connected edges from the start node. Do this after the first trip edge is added @@ -731,7 +961,7 @@ void AddIntersectingEdges(const AttributesController& controller, for (uint32_t idx1 = 0; idx1 < node->edge_count(); ++idx1, intersecting_edge++) { // Skip shortcut edges AND the opposing edge of the previous edge in the path AND - // the current edge in the path AND the superceded edge of the current edge in the path + // the current edge in the path AND the superseded edge of the current edge in the path // if the current edge in the path is a shortcut if (intersecting_edge->is_shortcut() || intersecting_edge->localedgeidx() == prior_opp_local_index || @@ -743,7 +973,8 @@ void AddIntersectingEdges(const AttributesController& controller, // Add intersecting edges on the same hierarchy level and not on the path AddTripIntersectingEdge(controller, start_tile, directededge, prev_de, - intersecting_edge->localedgeidx(), node, trip_node, intersecting_edge); + intersecting_edge->localedgeidx(), node, trip_node, intersecting_edge, + blind_instructions); } // Add intersecting edges on different levels (follow NodeTransitions) @@ -769,12 +1000,42 @@ void AddIntersectingEdges(const AttributesController& controller, AddTripIntersectingEdge(controller, endtile, directededge, prev_de, intersecting_edge2->localedgeidx(), nodeinfo2, trip_node, - intersecting_edge2); + intersecting_edge2, blind_instructions); } } } } +void ProcessTunnelBridgeTaggedValue(valhalla::StreetName* trip_edge_name, + const LinguisticMap& linguistics, + const std::tuple& name_and_type, + const uint8_t name_index) { + + trip_edge_name->set_value(std::get<0>(name_and_type)); + trip_edge_name->set_is_route_number(std::get<1>(name_and_type)); + + const auto iter = linguistics.find(name_index); + if (iter != linguistics.end()) { + + auto lang = static_cast(std::get(iter->second)); + if (lang != Language::kNone) { + trip_edge_name->set_language_tag(GetTripLanguageTag(lang)); + } + + auto alphabet = static_cast( + std::get(iter->second)); + + if (alphabet != PronunciationAlphabet::kNone) { + + auto* pronunciation = trip_edge_name->mutable_pronunciation(); + pronunciation->set_alphabet( + GetTripPronunciationAlphabet(static_cast( + std::get(iter->second)))); + pronunciation->set_value(std::get(iter->second)); + } + } +} + /** * Add trip edge. (TODO more comments) * @param controller Controller to determine which attributes to set. @@ -791,6 +1052,7 @@ void AddIntersectingEdges(const AttributesController& controller, * @param start_node_idx The start node index * @param has_junction_name True if named junction exists, false otherwise * @param start_tile The start tile of the start node + * @param blind_instructions Whether instructions should be generated for blind users * */ TripLeg_Edge* AddTripEdge(const AttributesController& controller, @@ -809,11 +1071,11 @@ TripLeg_Edge* AddTripEdge(const AttributesController& controller, const bool has_junction_name, const graph_tile_ptr& start_tile, const uint8_t restrictions_idx, - float elapsed_secs) { + float elapsed_secs, + bool blind_instructions) { // Index of the directed edge within the tile uint32_t idx = edge.id(); - TripLeg_Edge* trip_edge = trip_node->mutable_edge(); // Get the edgeinfo @@ -822,32 +1084,32 @@ TripLeg_Edge* AddTripEdge(const AttributesController& controller, // Add names to edge if requested if (controller(kEdgeNames)) { auto names_and_types = edgeinfo.GetNamesAndTypes(true); + if (blind_instructions) + FilterUnneededStreetNumbers(names_and_types); trip_edge->mutable_name()->Reserve(names_and_types.size()); - std::unordered_map> pronunciations = - edgeinfo.GetPronunciationsMap(); + const auto linguistics = edgeinfo.GetLinguisticMap(); + uint8_t name_index = 0; for (const auto& name_and_type : names_and_types) { - if (std::get<2>(name_and_type) != 0) { - // Skip the tagged names - continue; - } - - auto* trip_edge_name = trip_edge->mutable_name()->Add(); - // Assign name and type - trip_edge_name->set_value(std::get<0>(name_and_type)); - trip_edge_name->set_is_route_number(std::get<1>(name_and_type)); - std::unordered_map>::const_iterator iter = - pronunciations.find(name_index); - - // Assign pronunciation alphabet and value if one exists - if (iter != pronunciations.end()) { - auto* pronunciation = trip_edge_name->mutable_pronunciation(); - pronunciation->set_alphabet(GetTripPronunciationAlphabet( - static_cast((iter->second).first))); - pronunciation->set_value((iter->second).second); + switch (std::get<2>(name_and_type)) { + case kNotTagged: { + ProcessNonTaggedValue(trip_edge->mutable_name()->Add(), linguistics, name_and_type, + name_index); + break; + } + case kTunnelTag: + case kBridgeTag: { + ProcessTunnelBridgeTaggedValue(trip_edge->mutable_tunnel_name()->Add(), linguistics, + name_and_type, name_index); + break; + } + default: + // Skip the rest tagged names + LOG_TRACE(std::string("skipped tagged value= ") + + std::to_string(std::get<2>(name_and_type))); + break; } - - name_index++; + ++name_index; } } @@ -870,27 +1132,27 @@ TripLeg_Edge* AddTripEdge(const AttributesController& controller, // Set the signs (if the directed edge has sign information) and if requested if (directededge->sign()) { // Add the edge signs - std::unordered_map> pronunciations; - std::vector edge_signs = graphtile->GetSigns(idx, pronunciations); + LinguisticMap linguistics; + std::vector edge_signs = graphtile->GetSigns(idx, linguistics); if (!edge_signs.empty()) { valhalla::TripSign* sign = trip_edge->mutable_sign(); - AddSignInfo(controller, edge_signs, pronunciations, sign); + AddSignInfo(controller, edge_signs, linguistics, sign); } } // Process the named junctions at nodes if (has_junction_name && start_tile) { // Add the node signs - std::unordered_map> pronunciations; - std::vector node_signs = start_tile->GetSigns(start_node_idx, pronunciations, true); + LinguisticMap linguistics; + std::vector node_signs = start_tile->GetSigns(start_node_idx, linguistics, true); if (!node_signs.empty()) { valhalla::TripSign* trip_sign = trip_edge->mutable_sign(); uint32_t sign_index = 0; for (const auto& sign : node_signs) { switch (sign.type()) { case valhalla::baldr::Sign::Type::kJunctionName: { - if (controller(kEdgeSignJunctionName)) { - PopulateSignElement(sign_index, sign, pronunciations, + if (controller.attributes.at(kEdgeSignJunctionName)) { + PopulateSignElement(sign_index, sign, linguistics, trip_sign->mutable_junction_names()->Add()); } break; @@ -939,6 +1201,11 @@ TripLeg_Edge* AddTripEdge(const AttributesController& controller, trip_edge->set_speed(speed); } + // Set country crossing if requested + if (controller(kEdgeCountryCrossing)) { + trip_edge->set_country_crossing(directededge->ctry_crossing()); + } + uint8_t kAccess = 0; if (mode == sif::TravelMode::kBicycle) { kAccess = kBicycleAccess; @@ -1314,7 +1581,7 @@ void AccumulateRecostingInfoForward(const valhalla::Options& options, // setup a callback for the recosting to tell us about the new label each made auto out_itr = leg.mutable_node()->begin(); - sif::LabelCallback label_cb = [&out_itr](const sif::EdgeLabel& label) -> void { + sif::LabelCallback label_cb = [&out_itr](const sif::PathEdgeLabel& label) -> void { // get the turn cost at this node out_itr->mutable_recosts()->rbegin()->mutable_transition_cost()->set_seconds( label.transition_cost().secs); @@ -1490,6 +1757,9 @@ void TripLegBuilder::Build( // from the edge index that we assigned to them earlier in route_action auto intermediate_itr = trip_path.mutable_location()->begin() + 1; double total_distance = 0; + bool has_toll = false; + bool has_ferry = false; + bool has_highway = false; // loop over the edges to build the trip leg for (auto edge_itr = path_begin; edge_itr != path_end; ++edge_itr, ++edge_index) { @@ -1503,6 +1773,16 @@ void TripLegBuilder::Build( const uint8_t travel_type = travel_types[static_cast(mode)]; const auto& costing = mode_costing[static_cast(mode)]; + if (directededge->toll()) { + has_toll = true; + } + if (directededge->use() == Use::kFerry) { + has_ferry = true; + } + if (directededge->classification() == baldr::RoadClass::kMotorway) { + has_highway = true; + } + // Set node attributes - only set if they are true since they are optional graph_tile_ptr start_tile = graphtile; graphreader.GetGraphTile(startnode, start_tile); @@ -1529,6 +1809,8 @@ void TripLegBuilder::Build( if (controller(kNodeType)) { trip_node->set_type(GetTripLegNodeType(node->type())); + if (node->traffic_signal()) + trip_node->set_traffic_signal(true); } if (node->intersection() == IntersectionType::kFork) { @@ -1578,7 +1860,8 @@ void TripLegBuilder::Build( AddTripEdge(controller, edge, edge_itr->trip_id, multimodal_builder.block_id, mode, travel_type, costing, directededge, node->drive_on_right(), trip_node, graphtile, time_info, startnode.id(), node->named_intersection(), start_tile, - edge_itr->restriction_index, edge_itr->elapsed_cost.secs); + edge_itr->restriction_index, edge_itr->elapsed_cost.secs, + travel_type == PedestrianType::kBlind && mode == sif::TravelMode::kPedestrian); // some information regarding shape/length trimming float trim_start_pct = is_first_edge ? start_pct : 0; @@ -1727,11 +2010,22 @@ void TripLegBuilder::Build( // must be done after the edge's shape has been added. SetHeadings(trip_edge, controller, directededge, trip_shape, begin_index); + // Add elevation along the edge if requested + if (controller(kEdgeElevation)) { + SetElevation(trip_edge, trim_start_pct, trim_end_pct, node, directededge, graphtile, + graphreader); + } + + // Add landmarks in the directededge to the trip leg + AddLandmarks(edgeinfo, trip_edge, controller, directededge, trip_shape, begin_index); + // Add the intersecting edges at the node. Skip it if the node was an inner node (excluding start // node and end node) of a shortcut that was recovered. if (startnode.Is_Valid() && !edge_itr->start_node_is_recovered) { AddIntersectingEdges(controller, start_tile, node, directededge, prev_de, prior_opp_local_index, - graphreader, trip_node); + graphreader, trip_node, + travel_type == PedestrianType::kBlind && + mode == sif::TravelMode::kPedestrian); } ////////////// Prepare for the next iteration @@ -1799,6 +2093,11 @@ void TripLegBuilder::Build( trip_path.set_osm_changeset(osmchangeset); } + Summary* summary = trip_path.mutable_summary(); + summary->set_has_toll(has_toll); + summary->set_has_ferry(has_ferry); + summary->set_has_highway(has_highway); + // Add that extra costing information if requested AccumulateRecostingInfoForward(options, start_pct, end_pct, forward_time_info, invariant, graphreader, trip_path); diff --git a/src/thor/unidirectional_astar.cc b/src/thor/unidirectional_astar.cc index 6e4f82961d..036f9c2bea 100644 --- a/src/thor/unidirectional_astar.cc +++ b/src/thor/unidirectional_astar.cc @@ -19,8 +19,7 @@ UnidirectionalAStar::UnidirectionalAStar( : PathAlgorithm(config.get("max_reserved_labels_count_astar", kInitialEdgeLabelCountAstar), config.get("clear_reserved_memory", false)), - max_label_count_(std::numeric_limits::max()), mode_(travel_mode_t::kDrive), - travel_type_(0), access_mode_{kAutoAccess} { + mode_(travel_mode_t::kDrive), travel_type_(0), access_mode_(kAutoAccess) { } // Default constructor @@ -80,7 +79,7 @@ bool UnidirectionalAStar::Expand(GraphReader& grap : time_info.reverse(pred.cost().secs, static_cast(nodeinfo->timezone())); if (!costing_->Allowed(nodeinfo)) { - const DirectedEdge* opp_edge; + const DirectedEdge* opp_edge = nullptr; const GraphId opp_edge_id = graphreader.GetOpposingEdgeId(pred.edgeid(), opp_edge, tile); // Check if edge is null before using it (can happen with regional data sets) pred.set_deadend(true); @@ -275,16 +274,22 @@ inline bool UnidirectionalAStar::ExpandInner( (pred.closure_pruning() || !(costing_->IsClosed(meta.edge, tile))), 0 != (flow_sources & kDefaultFlowMask), costing_->TurnType(pred.opp_local_idx(), nodeinfo, meta.edge), - restriction_idx); + restriction_idx, 0, + meta.edge->destonly() || + (costing_->is_hgv() && meta.edge->destonly_hgv()), + meta.edge->forwardaccess() & kTruckAccess); } else { edgelabels_.emplace_back(pred_idx, meta.edge_id, opp_edge_id, meta.edge, cost, sortcost, dist, mode_, transition_cost, (pred.not_thru_pruning() || !meta.edge->not_thru()), - (pred.closure_pruning() || !(costing_->IsClosed(meta.edge, tile))), + (pred.closure_pruning() || !(costing_->IsClosed(opp_edge, endtile))), 0 != (flow_sources & kDefaultFlowMask), costing_->TurnType(meta.edge->localedgeidx(), nodeinfo, opp_edge, opp_pred_edge), - restriction_idx); + restriction_idx, 0, + opp_edge->destonly() || + (costing_->is_hgv() && opp_edge->destonly_hgv()), + opp_edge->forwardaccess() & kTruckAccess); } auto& edge_label = edgelabels_.back(); @@ -368,7 +373,7 @@ std::vector UnidirectionalAStar::FormPath(cons std::vector path; for (auto edgelabel_index = dest; edgelabel_index != kInvalidLabel; edgelabel_index = edgelabels_[edgelabel_index].predecessor()) { - const EdgeLabel& edgelabel = edgelabels_[edgelabel_index]; + const auto& edgelabel = edgelabels_[edgelabel_index]; path.emplace_back(edgelabel.mode(), edgelabel.cost(), edgelabel.edgeid(), 0, edgelabel.path_distance(), edgelabel.restriction_idx(), edgelabel.transition_cost()); @@ -463,7 +468,7 @@ std::vector> UnidirectionalAStar> UnidirectionalAStar best_path = std::make_pair(-1, 0.0f); - size_t total_labels = 0; + size_t n = 0; while (true) { // Allow this process to be aborted - size_t current_labels = edgelabels_.size(); - if (interrupt && - total_labels / kInterruptIterationsInterval < current_labels / kInterruptIterationsInterval) { + if (interrupt && (++n % kInterruptIterationsInterval) == 0) { (*interrupt)(); } - total_labels = current_labels; - - // Abort if max label count is exceeded - if (total_labels > max_label_count_) { - return {}; - } // Get next element from adjacency list. Check that it is valid. An // invalid label indicates there are no edges that can be expanded. @@ -761,13 +758,19 @@ void UnidirectionalAStar::SetOrigin( edgelabels_.emplace_back(kInvalidLabel, edgeid, GraphId(), directededge, cost, sortcost, dist, mode_, Cost{}, false, !(costing_->IsClosed(directededge, tile)), 0 != (flow_sources & kDefaultFlowMask), sif::InternalTurn::kNoTurn, - kInvalidRestriction); + kInvalidRestriction, 0, + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); } else { edgelabels_.emplace_back(kInvalidLabel, opp_edge_id, edgeid, opp_dir_edge, cost, sortcost, dist, mode_, Cost{}, false, !(costing_->IsClosed(directededge, tile)), 0 != (flow_sources & kDefaultFlowMask), sif::InternalTurn::kNoTurn, - kInvalidRestriction); + kInvalidRestriction, 0, + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); } auto& edge_label = edgelabels_.back(); diff --git a/src/thor/worker.cc b/src/thor/worker.cc index 92d0a92573..0b6ddaf7f2 100644 --- a/src/thor/worker.cc +++ b/src/thor/worker.cc @@ -1,10 +1,7 @@ -#include #include -#include #include #include #include -#include #include "midgard/constants.h" #include "midgard/logging.h" @@ -48,7 +45,7 @@ const std::unordered_map kMaxDistances = { // a scale factor to apply to the score so that we bias towards closer results more constexpr float kDistanceScale = 10.f; -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES std::string serialize_to_pbf(Api& request) { std::string buf; if (!request.SerializeToString(&buf)) { @@ -101,6 +98,8 @@ thor_worker_t::thor_worker_t(const boost::property_tree::ptree& config, source_to_target_algorithm = SELECT_OPTIMAL; } + costmatrix_allow_second_pass = config.get("thor.costmatrix_allow_second_pass", false); + max_timedep_distance = config.get("service_limits.max_timedep_distance", kDefaultMaxTimeDependentDistance); @@ -111,7 +110,7 @@ thor_worker_t::thor_worker_t(const boost::property_tree::ptree& config, thor_worker_t::~thor_worker_t() { } -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES prime_server::worker_t::result_t thor_worker_t::work(const std::list& job, void* request_info, @@ -313,9 +312,9 @@ void thor_worker_t::cleanup() { multi_modal_astar.Clear(); bss_astar.Clear(); trace.clear(); - costmatrix_.clear(); - time_distance_matrix_.clear(); - time_distance_bss_matrix_.clear(); + costmatrix_.Clear(); + time_distance_matrix_.Clear(); + time_distance_bss_matrix_.Clear(); isochrone_gen.Clear(); centroid_gen.Clear(); matcher_factory.ClearFullCache(); diff --git a/src/tile_server.cc b/src/tile_server.cc index 9d1984a1d9..61b0b9550e 100644 --- a/src/tile_server.cc +++ b/src/tile_server.cc @@ -10,7 +10,6 @@ #include #include -#include #include #include diff --git a/src/tyr/CMakeLists.txt b/src/tyr/CMakeLists.txt index 2c2e3a22d7..38c669f996 100644 --- a/src/tyr/CMakeLists.txt +++ b/src/tyr/CMakeLists.txt @@ -4,22 +4,22 @@ set(sources actor.cc height_serializer.cc isochrone_serializer.cc + matrix_serializer.cc + route_serializer_osrm.cc + route_summary_cache.cc serializers.cc - transit_available_serializer.cc) + transit_available_serializer.cc + expansion_serializer.cc) set(sources_with_warnings locate_serializer.cc - matrix_serializer.cc route_serializer.cc - route_serializer_osrm.cc route_serializer_valhalla.cc trace_serializer.cc) -# treat date library as system -set(system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include) -if(APPLE) - list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) -endif() +set(system_includes + ${date_include_dir} + $<$:${dirent_include_dir}>) valhalla_module(NAME tyr SOURCES @@ -32,17 +32,19 @@ valhalla_module(NAME tyr PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include ${CMAKE_BINARY_DIR} SYSTEM_INCLUDE_DIRECTORIES PUBLIC ${system_includes} + PRIVATE + ${rapidjson_include_dir} DEPENDS valhalla::loki valhalla::thor valhalla::odin valhalla::proto ${valhalla_protobuf_targets} - Boost::boost) + Boost::boost + ${GDAL_TARGET} + ) diff --git a/src/tyr/actor.cc b/src/tyr/actor.cc index e02ffe39aa..9a4335dd60 100644 --- a/src/tyr/actor.cc +++ b/src/tyr/actor.cc @@ -143,12 +143,12 @@ actor_t::matrix(const std::string& request_str, const std::function* int // check the request and locate the locations in the graph pimpl->loki_worker.matrix(*api); // compute the matrix - auto json = pimpl->thor_worker.matrix(*api); + auto bytes = pimpl->thor_worker.matrix(*api); // if they want you do to do the cleanup automatically if (auto_cleanup) { cleanup(); } - return json; + return bytes; } std::string actor_t::optimized_route(const std::string& request_str, @@ -301,8 +301,10 @@ actor_t::expansion(const std::string& request_str, const std::function* // check the request and locate the locations in the graph if (api->options().expansion_action() == Options::route) { pimpl->loki_worker.route(*api); - } else { + } else if (api->options().expansion_action() == Options::isochrone) { pimpl->loki_worker.isochrones(*api); + } else { + pimpl->loki_worker.matrix(*api); } // route between the locations in the graph to find the best path auto json = pimpl->thor_worker.expansion(*api); diff --git a/src/tyr/expansion_serializer.cc b/src/tyr/expansion_serializer.cc new file mode 100644 index 0000000000..ee6032d497 --- /dev/null +++ b/src/tyr/expansion_serializer.cc @@ -0,0 +1,88 @@ + +#include "baldr/rapidjson_utils.h" +#include "tyr/serializers.h" + +using namespace valhalla; +using namespace rapidjson; + +namespace valhalla { +namespace tyr { +std::string serializeExpansion(Api& request, const std::string& algo) { + if (request.options().format() == Options::Format::Options_Format_pbf) + return serializePbf(request); + + // form GeoJSON + writer_wrapper_t writer(1024 * 1024); + writer.start_object(); + writer("type", "FeatureCollection"); + writer.start_array("features"); + writer.set_precision(6); + + std::unordered_set exp_props; + for (const auto& prop : request.options().expansion_properties()) { + exp_props.insert(static_cast(prop)); + } + auto expansion = request.expansion(); + for (int i = 0; i < expansion.geometries().size(); ++i) { + // create features + writer.start_object(); // feature object + writer("type", "Feature"); + + writer.start_object("geometry"); + writer("type", "LineString"); + writer.start_array("coordinates"); + + // make the geom + const auto& geom = expansion.geometries(i); + for (int j = 0; j < geom.coords().size() - 1; j += 2) { + writer.start_array(); + writer(static_cast((geom.coords(j) / 1e6))); + writer(static_cast((geom.coords(j + 1) / 1e6))); + writer.end_array(); + } + + writer.end_array(); // coordinates + writer.end_object(); // geometry + + writer.start_object("properties"); + // no properties asked for, don't collect any + if (!exp_props.size()) { + writer.end_object(); // properties + writer.end_object(); // feature + continue; + } + + // make the properties + if (exp_props.count(Options_ExpansionProperties_duration)) { + writer("duration", static_cast(expansion.durations(i))); + } + if (exp_props.count(Options_ExpansionProperties_distance)) { + writer("distance", static_cast(expansion.distances(i))); + } + if (exp_props.count(Options_ExpansionProperties_cost)) { + writer("cost", static_cast(expansion.costs(i))); + } + if (exp_props.count(Options_ExpansionProperties_edge_status)) + writer("edge_status", Expansion_EdgeStatus_Enum_Name(expansion.edge_status(i))); + if (exp_props.count(Options_ExpansionProperties_edge_id)) + writer("edge_id", static_cast(expansion.edge_id(i))); + if (exp_props.count(Options_ExpansionProperties_pred_edge_id)) + writer("pred_edge_id", static_cast(expansion.pred_edge_id(i))); + if (exp_props.count(Options_ExpansionProperties_expansion_type)) + writer("expansion_type", static_cast(expansion.expansion_type(i))); + + writer.end_object(); // properties + writer.end_object(); // feature + } + + // close the GeoJSON + writer.end_array(); // features + writer.start_object("properties"); + writer("algorithm", algo); + writer.end_object(); + writer.end_object(); // object + + return writer.get_buffer(); +} +} // namespace tyr +} // namespace valhalla \ No newline at end of file diff --git a/src/tyr/isochrone_serializer.cc b/src/tyr/isochrone_serializer.cc index 24c8768038..22943370e4 100644 --- a/src/tyr/isochrone_serializer.cc +++ b/src/tyr/isochrone_serializer.cc @@ -2,83 +2,300 @@ #include "baldr/json.h" #include "midgard/point2.h" #include "midgard/pointll.h" +#include "thor/worker.h" #include "tyr/serializers.h" #include #include #include +#ifdef ENABLE_GDAL +#include +#endif + using namespace valhalla::baldr::json; namespace { + +// allows us to only ever register the driver once per process without having to put it +// in every executable that might call into this code +struct gdal_singleton_t { + static const gdal_singleton_t& get() { + static const gdal_singleton_t instance; + return instance; + } + +private: + gdal_singleton_t() { +#ifdef ENABLE_GDAL + GDALRegister_GTiff(); +#endif + } +}; + using rgba_t = std::tuple; + +using namespace valhalla; +using namespace tyr; +using namespace midgard; +using contour_t = std::list; // single ring +using feature_t = std::list; // rings per interval +using contours_t = std::vector>; // all rings +using contour_group_t = std::vector; +using grouped_contours_t = std::vector; +// dimension, value (seconds/meters), name (time/distance), color +using contour_interval_t = std::tuple; + +grouped_contours_t GroupContours(const bool polygons, const feature_t& contours) { + grouped_contours_t results; + + // if the user requested linestrings, we'll give them linestrings + if (!polygons || contours.size() < 2) { + std::for_each(contours.begin(), contours.end(), + [&results](const contour_t& c) { results.push_back({&c}); }); + return results; + } + + // inner rings have negative area + auto isExteriorRing = [](const contour_t& c) -> bool { return polygon_area(c) > 0; }; + + std::vector inner_ptrs; + std::for_each(contours.begin(), contours.end(), + [&results, &inner_ptrs, isExteriorRing](const contour_t& c) { + if (isExteriorRing(c)) { + results.push_back({&c}); + } else { + inner_ptrs.push_back(&c); + } + }); + + // exactly one exterior ring, so all inners go in one group + if (results.size() == 1) { + std::for_each(inner_ptrs.begin(), inner_ptrs.end(), + [&results](const contour_t* c) { results[0].push_back(c); }); + return results; + } + + // iterate over outer rings and for each inner ring check if the inner ring is within the exterior + // ring + for (const auto* inner : inner_ptrs) { + + // get the first point of the ring (could be any though) + const PointLL& inner_pt = inner->front(); + bool found_exterior = false; + // go over exterior rings from smallest to largest + for (size_t i = results.size(); i > 0; --i) { + const contour_t& ext = *results[i - 1][0]; + + // inner is within exterior ring if any of its points lies within the exterior ring + // if (inner_pt.WithinPolygon(ext)) { + if (point_in_poly(inner_pt, ext)) { + results[i - 1].push_back(inner); + found_exterior = true; + break; + } + } + + // TODO: reverse winding and make it an exterior ring + if (!found_exterior) { + LOG_WARN("No exterior ring contour found for inner contour."); + } + } + + return results; } -namespace valhalla { -namespace tyr { +std::string getIntervalColor(std::vector& intervals, size_t interval_idx) { + std::stringstream hex; + // color was supplied + if (!std::get<3>(intervals[interval_idx]).empty()) { + hex << "#" << std::get<3>(intervals[interval_idx]); + } // or we computed it.. + else { + auto h = interval_idx * (150.f / intervals.size()); + auto c = .5f; + auto x = c * (1 - std::abs(std::fmod(h / 60.f, 2.f) - 1)); + auto m = .25f; + rgba_t color = h < 60 ? rgba_t{m + c, m + x, m} + : (h < 120 ? rgba_t{m + x, m + c, m} : rgba_t{m, m + c, m + x}); + hex << "#" << std::hex << static_cast(std::get<0>(color) * 255 + .5f) << std::hex + << static_cast(std::get<1>(color) * 255 + .5f) << std::hex + << static_cast(std::get<2>(color) * 255 + .5f); + } + return hex.str(); +} -std::string serializeIsochrones(const Api& request, - std::vector::contour_interval_t>& intervals, - midgard::GriddedData<2>::contours_t& contours, - bool polygons, - bool show_locations) { +void addLocations(Api& request, valhalla::baldr::json::ArrayPtr& features) { + int idx = 0; + for (const auto& location : request.options().locations()) { + // first add all snapped points as MultiPoint feature per origin point + auto snapped_points_array = array({}); + std::unordered_set snapped_points; + for (const auto& path_edge : location.correlation().edges()) { + const PointLL& snapped_current = PointLL(path_edge.ll().lng(), path_edge.ll().lat()); + // remove duplicates of path_edges in case the snapped object is a node + if (snapped_points.insert(snapped_current).second) { + snapped_points_array->push_back( + array({fixed_t{snapped_current.lng(), 6}, fixed_t{snapped_current.lat(), 6}})); + } + }; + features->emplace_back(map({{"type", std::string("Feature")}, + {"properties", map({{"type", std::string("snapped")}, + {"location_index", static_cast(idx)}})}, + {"geometry", map({{"type", std::string("MultiPoint")}, + {"coordinates", snapped_points_array}})}})); + + // then each user input point as separate Point feature + const valhalla::LatLng& input_latlng = location.ll(); + const auto input_array = array({fixed_t{input_latlng.lng(), 6}, fixed_t{input_latlng.lat(), 6}}); + features->emplace_back( + map({{"type", std::string("Feature")}, + {"properties", + map({{"type", std::string("input")}, {"location_index", static_cast(idx)}})}, + {"geometry", map({{"type", std::string("Point")}, {"coordinates", input_array}})}})); + idx++; + } +} + +#ifdef ENABLE_GDAL +// get a temporary file name suffix for GDAL's virtual file system +std::string GenerateTmpFName() { + std::stringstream ss; + ss << "/vsimem/" << std::this_thread::get_id() << "_" + << std::chrono::high_resolution_clock::now().time_since_epoch().count(); + return ss.str(); +} + +std::string serializeGeoTIFF(Api& request, const std::shared_ptr>& isogrid) { + + // time, distance + std::vector metrics{false, false}; + for (auto& contour : request.options().contours()) { + metrics[0] = metrics[0] || contour.has_time_case(); + metrics[1] = metrics[1] || contour.has_distance_case(); + } + + auto box = isogrid->MinExtent(); + int32_t ext_x = box[2] - box[0]; + int32_t ext_y = box[3] - box[1]; + + // for GDALs virtual fs + std::string name = GenerateTmpFName(); + + auto nbands = std::count(metrics.begin(), metrics.end(), true); + char** geotiff_options = NULL; + geotiff_options = CSLSetNameValue(geotiff_options, "COMPRESS", "PACKBITS"); + + gdal_singleton_t::get(); + auto driver_manager = GetGDALDriverManager(); + auto geotiff_driver = driver_manager->GetDriverByName("GTiff"); + auto geotiff_dataset = + geotiff_driver->Create(name.c_str(), ext_x, ext_y, nbands, GDT_UInt16, geotiff_options); + + OGRSpatialReference spatial_ref; + spatial_ref.SetWellKnownGeogCS("EPSG:4326"); + double geo_transform[6] = {isogrid->TileBounds(isogrid->TileId(box[0], box[1])).minx(), // minx + isogrid->TileSize(), + 0, + isogrid->TileBounds(isogrid->TileId(box[0], box[1])).miny(), // miny + 0, + isogrid->TileSize()}; + + geotiff_dataset->SetGeoTransform(geo_transform); + geotiff_dataset->SetSpatialRef(const_cast(&spatial_ref)); + + for (size_t metric_idx = 0; metric_idx < metrics.size(); ++metric_idx) { + if (!metrics[metric_idx]) + continue; // only create bands for requested metrics + uint16_t* data = new uint16_t[ext_x * ext_y]; + + // seconds or 10 meter steps + float scale_factor = metric_idx == 0 ? 60 : 100; + for (int32_t i = 0; i < ext_y; ++i) { + for (int32_t j = 0; j < ext_x; ++j) { + auto tileid = isogrid->TileId(j + box[0], i + box[1]); + data[i * ext_x + j] = + static_cast(isogrid->DataAt(tileid, metric_idx) * scale_factor); + } + } + auto band = geotiff_dataset->GetRasterBand(nbands == 2 ? (metric_idx + 1) : 1); + band->SetNoDataValue(std::numeric_limits::max()); + band->SetDescription(metric_idx == 0 ? "Time (seconds)" : "Distance (10m)"); + + CPLErr err = band->RasterIO(GF_Write, 0, 0, ext_x, ext_y, data, ext_x, ext_y, GDT_UInt16, 0, 0); + + delete[] data; + + if (err != CE_None) { + throw valhalla_exception_t{599, "Unknown error when writing GeoTIFF."}; + } + } + + GDALClose(geotiff_dataset); + vsi_l_offset bufferlength; + GByte* bytes = VSIGetMemFileBuffer(name.c_str(), &bufferlength, TRUE); + + // TODO: there's gotta be way to do this without copying + std::string data(reinterpret_cast(bytes), bufferlength); + + return data; +}; +#endif + +std::string serializeIsochroneJson(Api& request, + std::vector& intervals, + contours_t& contours, + bool show_locations, + bool polygons) { // for each contour interval int i = 0; auto features = array({}); assert(intervals.size() == contours.size()); for (size_t contour_index = 0; contour_index < intervals.size(); ++contour_index) { const auto& interval = intervals[contour_index]; - const auto& feature_collection = contours[contour_index]; - - // color was supplied - std::stringstream hex; - if (!std::get<3>(interval).empty()) { - hex << "#" << std::get<3>(interval); - } // or we computed it.. - else { - auto h = i * (150.f / intervals.size()); - auto c = .5f; - auto x = c * (1 - std::abs(std::fmod(h / 60.f, 2.f) - 1)); - auto m = .25f; - rgba_t color = h < 60 ? rgba_t{m + c, m + x, m} - : (h < 120 ? rgba_t{m + x, m + c, m} : rgba_t{m, m + c, m + x}); - hex << "#" << std::hex << static_cast(std::get<0>(color) * 255 + .5f) << std::hex - << static_cast(std::get<1>(color) * 255 + .5f) << std::hex - << static_cast(std::get<2>(color) * 255 + .5f); - } + const auto& interval_contours = contours[contour_index]; + + std::string hex = getIntervalColor(intervals, i); ++i; // for each feature on that interval - for (const auto& feature : feature_collection) { - // for each contour in that feature + for (const auto& feature : interval_contours) { + grouped_contours_t groups = GroupContours(polygons, feature); auto geom = array({}); - for (const auto& contour : feature) { - // make some geometry - auto coords = array({}); - for (const auto& coord : contour) { - coords->push_back(array({fixed_t{coord.first, 6}, fixed_t{coord.second, 6}})); - } - // its either a ring - if (polygons) { - geom->emplace_back(coords); - // or a single line, if someone has more than one contour per feature they messed up - } else { - geom = coords; + // each group is a polygon consisting of an exterior ring and possibly inner rings + for (const auto& group : groups) { + auto poly = array({}); + for (const auto& ring : group) { + auto ring_coords = array({}); + for (const auto& pair : *ring) { + ring_coords->push_back(array({fixed_t{pair.lng(), 6}, fixed_t{pair.lat(), 6}})); + } + if (polygons) { + poly->emplace_back(ring_coords); + } else { + poly = ring_coords; + } } + geom->emplace_back(poly); } + // add a feature features->emplace_back(map({ {"type", std::string("Feature")}, - {"geometry", map({ - {"type", std::string(polygons ? "Polygon" : "LineString")}, - {"coordinates", geom}, - })}, + {"geometry", + map({ + {"type", std::string(polygons ? groups.size() > 1 ? "MultiPolygon" : "Polygon" + : "LineString")}, + {"coordinates", + polygons && geom->size() > 1 ? geom : geom->at(0)}, // unwrap linestring, or polygon + // if there's only one + })}, {"properties", map({ {"metric", std::get<2>(interval)}, {"contour", baldr::json::float_t{std::get<1>(interval)}}, - {"color", hex.str()}, // lines - {"fill", hex.str()}, // geojson.io polys - {"fillColor", hex.str()}, // leaflet polys + {"color", hex}, // lines + {"fill", hex}, // geojson.io polys + {"fillColor", hex}, // leaflet polys {"opacity", fixed_t{.33f, 2}}, // lines {"fill-opacity", fixed_t{.33f, 2}}, // geojson.io polys {"fillOpacity", fixed_t{.33f, 2}}, // leaflet polys @@ -86,43 +303,10 @@ std::string serializeIsochrones(const Api& request, })); } } - // Add input and snapped locations to the geojson - if (show_locations) { - int idx = 0; - for (const auto& location : request.options().locations()) { - // first add all snapped points as MultiPoint feature per origin point - auto snapped_points_array = array({}); - std::unordered_set snapped_points; - for (const auto& path_edge : location.correlation().edges()) { - const midgard::PointLL& snapped_current = - midgard::PointLL(path_edge.ll().lng(), path_edge.ll().lat()); - // remove duplicates of path_edges in case the snapped object is a node - if (snapped_points.insert(snapped_current).second) { - snapped_points_array->push_back( - array({fixed_t{snapped_current.lng(), 6}, fixed_t{snapped_current.lat(), 6}})); - } - }; - features->emplace_back(map( - {{"type", std::string("Feature")}, - {"properties", - map({{"type", std::string("snapped")}, {"location_index", static_cast(idx)}})}, - {"geometry", - map({{"type", std::string("MultiPoint")}, {"coordinates", snapped_points_array}})}})); - - // then each user input point as separate Point feature - const valhalla::LatLng& input_latlng = location.ll(); - const auto input_array = - array({fixed_t{input_latlng.lng(), 6}, fixed_t{input_latlng.lat(), 6}}); - features->emplace_back(map( - {{"type", std::string("Feature")}, - {"properties", - map({{"type", std::string("input")}, {"location_index", static_cast(idx)}})}, - {"geometry", map({{"type", std::string("Point")}, {"coordinates", input_array}})}})); - idx++; - } - } - // make the collection + if (show_locations) + addLocations(request, features); + auto feature_collection = map({ {"type", std::string("FeatureCollection")}, {"features", features}, @@ -142,5 +326,82 @@ std::string serializeIsochrones(const Api& request, return ss.str(); } + +std::string serializeIsochronePbf(Api& request, + std::vector& intervals, + const contours_t& contours) { + // construct pbf output + Isochrone& isochrone = *request.mutable_isochrone(); + + // construct contours + for (size_t isoline_index = 0; isoline_index < contours.size(); ++isoline_index) { + const auto& contour = contours[isoline_index]; + const auto& interval = intervals[isoline_index]; + + auto* interval_pbf = isochrone.mutable_intervals()->Add(); + interval_pbf->set_metric(std::get<2>(interval) == "time" ? Isochrone::time : Isochrone::distance); + + interval_pbf->set_metric_value(std::get<1>(interval)); + + // for each feature + for (const auto& feature : contour) { + grouped_contours_t groups = GroupContours(true, feature); + + // for each group of rings (first is outer, rest is inner) + for (const std::vector& group_ptr : groups) { + auto* contour_pbf = interval_pbf->mutable_contours()->Add(); + + // construct a geometry + for (const std::list* ring : group_ptr) { + std::cerr << "Rings: " << ring->size() << std::endl; + + auto* geom = contour_pbf->mutable_geometries()->Add(); + for (PointLL pair : *ring) { + geom->add_coords(round(pair.lng() * 1e6)); + geom->add_coords(round(pair.lat() * 1e6)); + } + } + } + } + } + + return serializePbf(request); +} + +} // namespace + +namespace valhalla { +namespace tyr { + +std::string serializeIsochrones(Api& request, + std::vector::contour_interval_t>& intervals, + const std::shared_ptr>& isogrid) { + + // only generate if json or pbf output is requested + contours_t contours; + + switch (request.options().format()) { + case Options_Format_pbf: + case Options_Format_json: + // we have parallel vectors of contour properties and the actual geojson features + // this method sorts the contour specifications by metric (time or distance) and then by value + // with the largest values coming first. eg (60min, 30min, 10min, 40km, 10km) + contours = + isogrid->GenerateContours(intervals, request.options().polygons(), + request.options().denoise(), request.options().generalize()); + return request.options().format() == Options_Format_json + ? serializeIsochroneJson(request, intervals, contours, + request.options().show_locations(), + request.options().polygons()) + : serializeIsochronePbf(request, intervals, contours); + +#ifdef ENABLE_GDAL + case Options_Format_geotiff: + return serializeGeoTIFF(request, isogrid); +#endif + default: + throw; + } +} } // namespace tyr } // namespace valhalla diff --git a/src/tyr/locate_serializer.cc b/src/tyr/locate_serializer.cc index 8216dca614..a40e9cd7b3 100644 --- a/src/tyr/locate_serializer.cc +++ b/src/tyr/locate_serializer.cc @@ -25,6 +25,15 @@ OpenLR::LocationReferencePoint::FormOfWay get_fow(const baldr::DirectedEdge* de) return OpenLR::LocationReferencePoint::OTHER; } +json::ArrayPtr get_access_restrictions(const graph_tile_ptr& tile, uint32_t edge_idx) { + auto arr = json::array({}); + for (const auto& res : tile->GetAccessRestrictions(edge_idx, kAllAccess)) { + arr->emplace_back(res.json()); + }; + + return arr; +} + std::string linear_reference(const baldr::DirectedEdge* de, float percent_along, const EdgeInfo& edgeinfo) { const auto fow = get_fow(de); @@ -99,6 +108,7 @@ json::ArrayPtr serialize_edges(const PathLocation& location, GraphReader& reader {"linear_reference", linear_reference(directed_edge, edge.percent_along, edge_info)}, {"predicted_speeds", predicted_speeds}, {"live_speed", live_speed}, + {"access_restrictions", get_access_restrictions(tile, edge.id.id())}, })); } // they want it lean and mean else { diff --git a/src/tyr/matrix_serializer.cc b/src/tyr/matrix_serializer.cc index 53abe98c0f..73cffd2465 100644 --- a/src/tyr/matrix_serializer.cc +++ b/src/tyr/matrix_serializer.cc @@ -2,7 +2,7 @@ #include "baldr/json.h" #include "proto_conversions.h" -#include "thor/matrix_common.h" +#include "thor/matrixalgorithm.h" #include "tyr/serializers.h" using namespace valhalla; @@ -13,12 +13,12 @@ using namespace valhalla::thor; namespace { json::ArrayPtr -serialize_duration(const std::vector& tds, size_t start_td, const size_t td_count) { +serialize_duration(const valhalla::Matrix& matrix, size_t start_td, const size_t td_count) { auto time = json::array({}); for (size_t i = start_td; i < start_td + td_count; ++i) { // check to make sure a route was found; if not, return null for time in matrix result - if (tds[i].time != kMaxCost) { - time->emplace_back(static_cast(tds[i].time)); + if (matrix.times()[i] != kMaxCost) { + time->emplace_back(static_cast(matrix.times()[i])); } else { time->emplace_back(static_cast(nullptr)); } @@ -26,8 +26,8 @@ serialize_duration(const std::vector& tds, size_t start_td, const return time; } -json::ArrayPtr serialize_distance(const std::vector& tds, - size_t start_td, +json::ArrayPtr serialize_distance(const valhalla::Matrix& matrix, + const size_t start_td, const size_t td_count, const size_t /* source_index */, const size_t /* target_index */, @@ -35,23 +35,46 @@ json::ArrayPtr serialize_distance(const std::vector& tds, auto distance = json::array({}); for (size_t i = start_td; i < start_td + td_count; ++i) { // check to make sure a route was found; if not, return null for distance in matrix result - if (tds[i].time != kMaxCost) { - distance->emplace_back(json::fixed_t{tds[i].dist * distance_scale, 3}); + if (matrix.times()[i] != kMaxCost) { + distance->emplace_back(json::fixed_t{matrix.distances()[i] * distance_scale, 3}); } else { distance->emplace_back(static_cast(nullptr)); } } return distance; } + +json::ArrayPtr serialize_shape(const valhalla::Matrix& matrix, + const size_t start_td, + const size_t td_count, + const ShapeFormat shape_format) { + // TODO(nils): shapes aren't implemented yet in TDMatrix + auto shapes = json::array({}); + if (shape_format == no_shape || (matrix.algorithm() != Matrix::CostMatrix)) + return shapes; + + for (size_t i = start_td; i < start_td + td_count; ++i) { + switch (shape_format) { + // even if it source == target or no route found, we want to emplace an element + case geojson: + if (!matrix.shapes()[i].empty()) + shapes->emplace_back(tyr::geojson_shape(decode>(matrix.shapes()[i]))); + else + shapes->emplace_back(nullptr); + break; + default: + // this covers the polylines + shapes->emplace_back(matrix.shapes()[i]); + } + } + return shapes; +} } // namespace namespace osrm_serializers { // Serialize route response in OSRM compatible format. -json::MapPtr serialize(const Api& request, - const std::vector& time_distances, - double distance_scale, - MatrixType matrix_type) { +std::string serialize(const Api& request) { auto json = json::map({}); auto time = json::array({}); auto distance = json::array({}); @@ -63,18 +86,19 @@ json::MapPtr serialize(const Api& request, json->emplace("sources", osrm::waypoints(options.sources())); json->emplace("destinations", osrm::waypoints(options.targets())); - for (size_t source_index = 0; source_index < options.sources_size(); ++source_index) { - time->emplace_back(serialize_duration(time_distances, source_index * options.targets_size(), + for (int source_index = 0; source_index < options.sources_size(); ++source_index) { + time->emplace_back(serialize_duration(request.matrix(), source_index * options.targets_size(), options.targets_size())); - distance->emplace_back(serialize_distance(time_distances, source_index * options.targets_size(), - options.targets_size(), source_index, 0, - distance_scale)); + distance->emplace_back(serialize_distance(request.matrix(), source_index * options.targets_size(), + options.targets_size(), source_index, 0, 1.0)); } json->emplace("durations", time); json->emplace("distances", distance); - json->emplace("algorithm", - std::string(matrix_type == MatrixType::Cost ? "costmatrix" : "timedistancematrix")); - return json; + json->emplace("algorithm", MatrixAlgoToString(request.matrix().algorithm())); + + std::stringstream ss; + ss << *json; + return ss.str(); } } // namespace osrm_serializers @@ -85,33 +109,65 @@ valhalla output looks like this: */ -json::ArrayPtr locations(const google::protobuf::RepeatedPtrField& correlated) { +json::ArrayPtr locations(const google::protobuf::RepeatedPtrField& locations) { auto input_locs = json::array({}); - for (size_t i = 0; i < correlated.size(); i++) { - input_locs->emplace_back(json::map({{"lat", json::fixed_t{correlated.Get(i).ll().lat(), 6}}, - {"lon", json::fixed_t{correlated.Get(i).ll().lng(), 6}}})); + for (const auto& location : locations) { + if (location.correlation().edges().size() == 0) { + input_locs->emplace_back(nullptr); + } else { + auto& corr_ll = location.correlation().edges(0).ll(); + input_locs->emplace_back(json::map( + {{"lat", json::fixed_t{corr_ll.lat(), 6}}, {"lon", json::fixed_t{corr_ll.lng(), 6}}})); + } } return input_locs; } -json::ArrayPtr serialize_row(const std::vector& tds, +json::ArrayPtr serialize_row(const valhalla::Matrix& matrix, size_t start_td, const size_t td_count, const size_t source_index, const size_t target_index, - double distance_scale) { + const double distance_scale, + const ShapeFormat shape_format) { auto row = json::array({}); for (size_t i = start_td; i < start_td + td_count; ++i) { // check to make sure a route was found; if not, return null for distance & time in matrix // result json::MapPtr map; - if (tds[i].time != kMaxCost) { + const auto time = matrix.times()[i]; + const auto& date_time = matrix.date_times()[i]; + const auto& time_zone_offset = matrix.time_zone_offsets()[i]; + const auto& time_zone_name = matrix.time_zone_names()[i]; + if (time != kMaxCost) { map = json::map({{"from_index", static_cast(source_index)}, {"to_index", static_cast(target_index + (i - start_td))}, - {"time", static_cast(tds[i].time)}, - {"distance", json::fixed_t{tds[i].dist * distance_scale, 3}}}); - if (!tds[i].date_time.empty()) { - map->emplace("date_time", tds[i].date_time); + {"time", static_cast(time)}, + {"distance", json::fixed_t{matrix.distances()[i] * distance_scale, 3}}}); + if (!date_time.empty()) { + map->emplace("date_time", date_time); + } + + if (!time_zone_offset.empty()) { + map->emplace("time_zone_offset", time_zone_offset); + } + + if (!time_zone_name.empty()) { + map->emplace("time_zone_name", time_zone_name); + } + + if (matrix.shapes().size() && shape_format != no_shape) { + // TODO(nils): tdmatrices don't have "shape" support yet + if (!matrix.shapes()[i].empty()) { + switch (shape_format) { + case geojson: + map->emplace("shape", + tyr::geojson_shape(decode>(matrix.shapes()[i]))); + break; + default: + map->emplace("shape", matrix.shapes()[i]); + } + } } } else { map = json::map({{"from_index", static_cast(source_index)}, @@ -124,46 +180,47 @@ json::ArrayPtr serialize_row(const std::vector& tds, return row; } -json::MapPtr serialize(const Api& request, - const std::vector& time_distances, - double distance_scale, - MatrixType matrix_type) { +std::string serialize(const Api& request, double distance_scale) { auto json = json::map({}); const auto& options = request.options(); if (options.verbose()) { json::ArrayPtr matrix = json::array({}); - for (size_t source_index = 0; source_index < options.sources_size(); ++source_index) { - matrix->emplace_back(serialize_row(time_distances, source_index * options.targets_size(), - options.targets_size(), source_index, 0, distance_scale)); + for (int source_index = 0; source_index < options.sources_size(); ++source_index) { + matrix->emplace_back(serialize_row(request.matrix(), source_index * options.targets_size(), + options.targets_size(), source_index, 0, distance_scale, + options.shape_format())); } json->emplace("sources_to_targets", matrix); - json->emplace("targets", json::array({locations(options.targets())})); - json->emplace("sources", json::array({locations(options.sources())})); + json->emplace("targets", locations(options.targets())); + json->emplace("sources", locations(options.sources())); } // slim it down else { auto matrix = json::map({}); auto time = json::array({}); auto distance = json::array({}); + auto shapes = json::array({}); - for (size_t source_index = 0; source_index < options.sources_size(); ++source_index) { - time->emplace_back(serialize_duration(time_distances, source_index * options.targets_size(), - options.targets_size())); - distance->emplace_back(serialize_distance(time_distances, source_index * options.targets_size(), - options.targets_size(), source_index, 0, - distance_scale)); + for (int source_index = 0; source_index < options.sources_size(); ++source_index) { + const auto first_td = source_index * options.targets_size(); + time->emplace_back(serialize_duration(request.matrix(), first_td, options.targets_size())); + distance->emplace_back(serialize_distance(request.matrix(), first_td, options.targets_size(), + source_index, 0, distance_scale)); + shapes->emplace_back(serialize_shape(request.matrix(), first_td, options.targets_size(), + options.shape_format())); } matrix->emplace("distances", distance); matrix->emplace("durations", time); + if (!(options.shape_format() == no_shape) && (request.matrix().algorithm() == Matrix::CostMatrix)) + matrix->emplace("shapes", shapes); json->emplace("sources_to_targets", matrix); } json->emplace("units", Options_Units_Enum_Name(options.units())); - json->emplace("algorithm", - std::string(matrix_type == MatrixType::Cost ? "costmatrix" : "timedistancematrix")); + json->emplace("algorithm", MatrixAlgoToString(request.matrix().algorithm())); if (options.has_id_case()) { json->emplace("id", options.id()); @@ -174,26 +231,37 @@ json::MapPtr serialize(const Api& request, json->emplace("warnings", valhalla::tyr::serializeWarnings(request)); } - return json; + std::stringstream ss; + ss << *json; + return ss.str(); } } // namespace valhalla_serializers namespace valhalla { namespace tyr { -std::string serializeMatrix(const Api& request, - const std::vector& time_distances, - double distance_scale, - MatrixType matrix_type) { +std::string serializeMatrix(Api& request) { + double distance_scale = (request.options().units() == Options::miles) ? kMilePerMeter : kKmPerMeter; - auto json = - request.options().format() == Options::osrm - ? osrm_serializers::serialize(request, time_distances, distance_scale, matrix_type) - : valhalla_serializers::serialize(request, time_distances, distance_scale, matrix_type); + // error if we failed finding any connection + // dont bother serializing in case of /expansion request + if (std::all_of(request.matrix().times().begin(), request.matrix().times().end(), + [](const float& time) { return time == kMaxCost; })) { + throw valhalla_exception_t(442); + } else if (request.options().action() == Options_Action_expansion) { + return ""; + } - std::stringstream ss; - ss << *json; - return ss.str(); + switch (request.options().format()) { + case Options_Format_osrm: + return osrm_serializers::serialize(request); + case Options_Format_json: + return valhalla_serializers::serialize(request, distance_scale); + case Options_Format_pbf: + return serializePbf(request); + default: + throw; + } } } // namespace tyr diff --git a/src/tyr/route_serializer.cc b/src/tyr/route_serializer.cc index 76df5efe63..90050cf9cd 100644 --- a/src/tyr/route_serializer.cc +++ b/src/tyr/route_serializer.cc @@ -1,11 +1,10 @@ #include -#include #include #include #include "midgard/encoded.h" #include "midgard/util.h" -#include "route_serializer_osrm.cc" +#include "route_serializer_osrm.h" #include "route_serializer_valhalla.cc" #include "tyr/serializers.h" diff --git a/src/tyr/route_serializer_osrm.cc b/src/tyr/route_serializer_osrm.cc index dfc7c99258..62f4abd7b5 100644 --- a/src/tyr/route_serializer_osrm.cc +++ b/src/tyr/route_serializer_osrm.cc @@ -1,3 +1,4 @@ +#include #include #include #include @@ -11,6 +12,7 @@ #include "midgard/util.h" #include "odin/enhancedtrippath.h" #include "odin/util.h" +#include "route_serializer_osrm.h" #include "route_summary_cache.h" #include "tyr/serializer_constants.h" #include "tyr/serializers.h" @@ -94,6 +96,10 @@ const constexpr PointLL::first_type DOUGLAS_PEUCKER_THRESHOLDS[19] = { 2.6, // z18 }; +const constexpr double SECONDS_BEFORE_VERBAL_TRANSITION_ALERT_INSTRUCTION = 15.0; +const constexpr double SECONDS_BEFORE_VERBAL_PRE_TRANSITION_INSTRUCTION = 5.0; +const constexpr double APPROXIMATE_VERBAL_POSTRANSITION_LENGTH = 110; + inline double clamp(const double lat) { return std::max(std::min(lat, double(EPSG3857_MAX_LATITUDE)), double(-EPSG3857_MAX_LATITUDE)); } @@ -169,34 +175,6 @@ std::unordered_map> speed_limit {"WS", {kSpeedLimitSignVienna, kSpeedLimitUnitsMph}}, }; -namespace osrm_serializers { -/* -OSRM output is described in: http://project-osrm.org/docs/v5.5.1/api/ -{ - "code":"Ok" - "waypoints": [{ }, { }...], - "routes": [ - { - "geometry":"....." - "distance":xxx.y - "duration":yyy.z - "legs":[ - { - "steps":[ - "intersections":[ - ] - "geometry":" " - "maneuver":{ - } - ] - } - ] - }, - ... - ] -} -*/ - std::string destinations(const valhalla::TripSign& sign); // Add OSRM route summary information: distance, duration @@ -247,20 +225,6 @@ void route_summary(json::MapPtr& route, const valhalla::Api& api, bool imperial, } } -// Generate leg shape in geojson format. -json::MapPtr geojson_shape(const std::vector shape) { - auto geojson = json::map({}); - auto coords = json::array({}); - coords->reserve(shape.size()); - for (const auto& p : shape) { - coords->emplace_back(json::array( - {json::fixed_t{p.lng(), DIGITS_PRECISION}, json::fixed_t{p.lat(), DIGITS_PRECISION}})); - } - geojson->emplace("type", std::string("LineString")); - geojson->emplace("coordinates", std::move(coords)); - return geojson; -} - // Generate full shape of the route. std::vector full_shape(const valhalla::DirectionsRoute& directions, const valhalla::Options& options) { @@ -316,6 +280,10 @@ std::vector simplified_shape(const valhalla::DirectionsRoute& direction void route_geometry(json::MapPtr& route, const valhalla::DirectionsRoute& directions, const valhalla::Options& options) { + if (options.shape_format() == no_shape) { + return; + } + std::vector shape; if (options.has_generalize_case() && options.generalize() == 0.0f) { shape = simplified_shape(directions); @@ -392,11 +360,8 @@ json::MapPtr serialize_annotations(const valhalla::TripLeg& trip_leg) { // the optimized sequence. json::ArrayPtr waypoints(google::protobuf::RepeatedPtrField& locs) { // Create a vector of indexes. - uint32_t i = 0; - std::vector indexes; - for (const auto& loc : locs) { - indexes.push_back(i++); - } + std::vector indexes(locs.size()); + std::iota(indexes.begin(), indexes.end(), 0); // Sort the the vector by the location's original index std::sort(indexes.begin(), indexes.end(), [&locs](const uint32_t a, const uint32_t b) -> bool { @@ -429,6 +394,51 @@ struct IntersectionEdges { } }; +// Process 'indications' array - add indications from left to right +json::ArrayPtr lane_indications(const bool drive_on_right, const uint16_t mask) { + auto indications = json::array({}); + + // TODO make map for lane mask to osrm indication string + + // reverse (left u-turn) + if (mask & kTurnLaneReverse && drive_on_right) { + indications->emplace_back(osrmconstants::kModifierUturn); + } + // sharp_left + if (mask & kTurnLaneSharpLeft) { + indications->emplace_back(osrmconstants::kModifierSharpLeft); + } + // left + if (mask & kTurnLaneLeft) { + indications->emplace_back(osrmconstants::kModifierLeft); + } + // slight_left + if (mask & kTurnLaneSlightLeft) { + indications->emplace_back(osrmconstants::kModifierSlightLeft); + } + // through + if (mask & kTurnLaneThrough) { + indications->emplace_back(osrmconstants::kModifierStraight); + } + // slight_right + if (mask & kTurnLaneSlightRight) { + indications->emplace_back(osrmconstants::kModifierSlightRight); + } + // right + if (mask & kTurnLaneRight) { + indications->emplace_back(osrmconstants::kModifierRight); + } + // sharp_right + if (mask & kTurnLaneSharpRight) { + indications->emplace_back(osrmconstants::kModifierSharpRight); + } + // reverse (right u-turn) + if (mask & kTurnLaneReverse && !drive_on_right) { + indications->emplace_back(osrmconstants::kModifierUturn); + } + return indications; +} + // Add intersections along a step/maneuver. json::ArrayPtr intersections(const valhalla::DirectionsLeg::Maneuver& maneuver, valhalla::odin::EnhancedTripLeg* etp, @@ -440,7 +450,6 @@ json::ArrayPtr intersections(const valhalla::DirectionsLeg::Maneuver& maneuver, count = 0; auto intersections = json::array({}); uint32_t n = arrive_maneuver ? maneuver.end_path_index() + 1 : maneuver.end_path_index(); - EnhancedTripLeg_Node* prev_node = nullptr; for (uint32_t i = maneuver.begin_path_index(); i < n; i++) { auto intersection = json::map({}); @@ -498,7 +507,7 @@ json::ArrayPtr intersections(const valhalla::DirectionsLeg::Maneuver& maneuver, // Add rest_stop when passing by a rest_area or service_area if (i > 0 && !arrive_maneuver) { auto rest_stop = json::map({}); - for (uint32_t m = 0; m < node->intersecting_edge_size(); m++) { + for (int m = 0; m < node->intersecting_edge_size(); m++) { auto intersecting_edge = node->GetIntersectingEdge(m); bool routeable = intersecting_edge->IsTraversableOutbound(curr_edge->travel_mode()); @@ -539,7 +548,7 @@ json::ArrayPtr intersections(const valhalla::DirectionsLeg::Maneuver& maneuver, if (!arrive_maneuver) { edges.emplace_back(curr_edge->begin_heading(), true, false, true); if (i > 0) { - for (uint32_t m = 0; m < node->intersecting_edge_size(); m++) { + for (int m = 0; m < node->intersecting_edge_size(); m++) { auto intersecting_edge = node->GetIntersectingEdge(m); bool routable = intersecting_edge->IsTraversableOutbound(curr_edge->travel_mode()); edges.emplace_back(intersecting_edge->begin_heading(), routable, false, false); @@ -562,7 +571,7 @@ json::ArrayPtr intersections(const valhalla::DirectionsLeg::Maneuver& maneuver, // Sort edges by increasing bearing and update the in/out edge indexes std::sort(edges.begin(), edges.end()); - uint32_t incoming_index, outgoing_index; + uint32_t incoming_index = 0, outgoing_index = 0; for (uint32_t n = 0; n < edges.size(); ++n) { if (edges[n].in_edge) { incoming_index = n; @@ -588,9 +597,9 @@ json::ArrayPtr intersections(const valhalla::DirectionsLeg::Maneuver& maneuver, // Add tunnel_name for tunnels if (!arrive_maneuver) { if (curr_edge->tunnel() && !curr_edge->tagged_value().empty()) { - for (uint32_t t = 0; t < curr_edge->tagged_value().size(); ++t) { - if (curr_edge->tagged_value().Get(t).type() == TaggedValue_Type_kTunnel) { - intersection->emplace("tunnel_name", curr_edge->tagged_value().Get(t).value()); + for (const auto& e : curr_edge->tagged_value()) { + if (e.type() == TaggedValue_Type_kTunnel) { + intersection->emplace("tunnel_name", e.value()); } } } @@ -643,50 +652,8 @@ json::ArrayPtr intersections(const valhalla::DirectionsLeg::Maneuver& maneuver, if (turn_lane.state() != TurnLane::kInvalid) { lane->emplace("valid_indication", turn_lane_direction(turn_lane.active_direction())); } - - // Process 'indications' array - add indications from left to right - auto indications = json::array({}); - uint16_t mask = turn_lane.directions_mask(); - - // TODO make map for lane mask to osrm indication string - - // reverse (left u-turn) - if (mask & kTurnLaneReverse && prev_edge->drive_on_right()) { - indications->emplace_back(osrmconstants::kModifierUturn); - } - // sharp_left - if (mask & kTurnLaneSharpLeft) { - indications->emplace_back(osrmconstants::kModifierSharpLeft); - } - // left - if (mask & kTurnLaneLeft) { - indications->emplace_back(osrmconstants::kModifierLeft); - } - // slight_left - if (mask & kTurnLaneSlightLeft) { - indications->emplace_back(osrmconstants::kModifierSlightLeft); - } - // through - if (mask & kTurnLaneThrough) { - indications->emplace_back(osrmconstants::kModifierStraight); - } - // slight_right - if (mask & kTurnLaneSlightRight) { - indications->emplace_back(osrmconstants::kModifierSlightRight); - } - // right - if (mask & kTurnLaneRight) { - indications->emplace_back(osrmconstants::kModifierRight); - } - // sharp_right - if (mask & kTurnLaneSharpRight) { - indications->emplace_back(osrmconstants::kModifierSharpRight); - } - // reverse (right u-turn) - if (mask & kTurnLaneReverse && !prev_edge->drive_on_right()) { - indications->emplace_back(osrmconstants::kModifierUturn); - } - lane->emplace("indications", std::move(indications)); + lane->emplace("indications", + lane_indications(prev_edge->drive_on_right(), turn_lane.directions_mask())); lanes->emplace_back(std::move(lane)); } intersection->emplace("lanes", std::move(lanes)); @@ -1052,46 +1019,17 @@ std::string ramp_type(const valhalla::DirectionsLeg::Maneuver& maneuver) { return ""; } -// Populate the OSRM maneuver record within a step. -json::MapPtr osrm_maneuver(const valhalla::DirectionsLeg::Maneuver& maneuver, - valhalla::odin::EnhancedTripLeg* etp, - const PointLL& man_ll, - const bool depart_maneuver, - const bool arrive_maneuver, - const uint32_t prev_intersection_count, - const std::string& mode, - const std::string& prev_mode, - const bool rotary, - const bool prev_rotary, - const valhalla::Options& options) { - auto osrm_man = json::map({}); - - // Set the location - auto loc = json::array({}); - loc->emplace_back(json::fixed_t{man_ll.lng(), 6}); - loc->emplace_back(json::fixed_t{man_ll.lat(), 6}); - osrm_man->emplace("location", loc); - - // Get incoming and outgoing bearing. For the incoming heading, use the - // prior edge from the TripLeg. Compute turn modifier. TODO - reconcile - // turn degrees between Valhalla and OSRM - uint32_t idx = maneuver.begin_path_index(); - uint32_t in_brg = (idx > 0) ? etp->GetPrevEdge(idx)->end_heading() : 0; - uint32_t out_brg = maneuver.begin_heading(); - osrm_man->emplace("bearing_before", static_cast(in_brg)); - osrm_man->emplace("bearing_after", static_cast(out_brg)); - - std::string modifier; - if (!depart_maneuver) { - modifier = turn_modifier(maneuver, in_brg, out_brg, arrive_maneuver); - if (!modifier.empty()) - osrm_man->emplace("modifier", modifier); - } - - if (options.directions_type() == DirectionsType::instructions) { - osrm_man->emplace("instruction", maneuver.text_instruction()); - } - +// Determine the OSRM type of a maneuver in a step and in bannerInstructions +std::string maneuver_type(const valhalla::DirectionsLeg::Maneuver& maneuver, + valhalla::odin::EnhancedTripLeg* etp, + const bool depart_maneuver, + const bool arrive_maneuver, + const std::string& modifier, + const uint32_t prev_intersection_count, + const std::string& mode, + const std::string& prev_mode, + const bool rotary, + const bool prev_rotary) { // TODO - logic to convert maneuver types from Valhalla into OSRM maneuver types. std::string maneuver_type; if (depart_maneuver) { @@ -1106,10 +1044,6 @@ json::MapPtr osrm_maneuver(const valhalla::DirectionsLeg::Maneuver& maneuver, } else { maneuver_type = "roundabout"; } - // Roundabout count - if (maneuver.roundabout_exit_count() > 0) { - osrm_man->emplace("exit", static_cast(maneuver.roundabout_exit_count())); - } } else if (maneuver.type() == DirectionsLeg_Maneuver_Type_kRoundaboutExit) { if (prev_rotary) { maneuver_type = "exit rotary"; @@ -1118,6 +1052,7 @@ json::MapPtr osrm_maneuver(const valhalla::DirectionsLeg::Maneuver& maneuver, } } else { // Special cases + uint32_t idx = maneuver.begin_path_index(); auto prev_edge = etp->GetPrevEdge(idx); auto curr_edge = etp->GetCurrEdge(idx); bool new_name = maneuver.type() == DirectionsLeg_Maneuver_Type_kContinue || @@ -1197,11 +1132,280 @@ json::MapPtr osrm_maneuver(const valhalla::DirectionsLeg::Maneuver& maneuver, } } } + return maneuver_type; +} + +// Populate the OSRM maneuver record within a step. +json::MapPtr osrm_maneuver(const valhalla::DirectionsLeg::Maneuver& maneuver, + const std::string& maneuver_type, + const std::string& modifier, + const uint32_t in_brg, + const uint32_t out_brg, + const PointLL& man_ll, + const bool emplace_instructions) { + auto osrm_man = json::map({}); + + // Set the location + auto loc = json::array({}); + loc->emplace_back(json::fixed_t{man_ll.lng(), 6}); + loc->emplace_back(json::fixed_t{man_ll.lat(), 6}); + osrm_man->emplace("location", loc); + + osrm_man->emplace("bearing_before", static_cast(in_brg)); + osrm_man->emplace("bearing_after", static_cast(out_brg)); osrm_man->emplace("type", maneuver_type); + if (emplace_instructions) { + osrm_man->emplace("instruction", maneuver.text_instruction()); + } + if (!modifier.empty()) { + osrm_man->emplace("modifier", modifier); + } + // Roundabout count + if (maneuver.type() == DirectionsLeg_Maneuver_Type_kRoundaboutEnter && + maneuver.roundabout_exit_count() > 0) { + osrm_man->emplace("exit", static_cast(maneuver.roundabout_exit_count())); + } + return osrm_man; } +// Return a banner component +json::MapPtr banner_component(const std::string& type, const std::string& text) { + json::MapPtr component = json::map({}); + component->emplace("type", type); + component->emplace("text", text); + return component; +} + +// Primary banners hold the most important information and supposed to be the large text in a +// navigation app. Mostly they are used to show the primary_banner of the upcoming road. +// TODO: Highway shield information could be added here as well. +json::MapPtr primary_banner_instruction(const std::string& primary_text, + const std::string& ref, + const std::string& exit, + const bool arrive_maneuver, + const std::string& maneuver_type, + const std::string& modifier, + const bool roundabout, + const uint32_t roundabout_turn_degrees, + const std::string& drive_side) { + json::MapPtr instruction = json::map({}); + json::ArrayPtr components = json::array({}); + + if (!exit.empty() && !arrive_maneuver) { + components->emplace_back(banner_component("exit", "Exit")); + components->emplace_back(banner_component("exit-number", exit)); + } + components->emplace_back(banner_component("text", primary_text)); + if (!ref.empty() && !arrive_maneuver) { + components->emplace_back(banner_component("delimiter", "/")); + components->emplace_back(banner_component("text", ref)); + } + instruction->emplace("components", std::move(components)); + instruction->emplace("text", primary_text); + if (!maneuver_type.empty()) { + instruction->emplace("type", maneuver_type); + } + if (!modifier.empty()) { + instruction->emplace("modifier", modifier); + } + if (roundabout) { + instruction->emplace("degrees", static_cast(roundabout_turn_degrees)); + instruction->emplace("driving_side", drive_side); + } + return instruction; +} + +// Secondary banners hold additional information which is displayed slightly smaller than the +// primary information. They are mostly used to show the destination names on street signs. +json::MapPtr secondary_banner_instruction(const std::string& secondary_text) { + json::MapPtr instruction = json::map({}); + json::ArrayPtr components = json::array({}); + components->emplace_back(banner_component("text", secondary_text)); + instruction->emplace("components", std::move(components)); + instruction->emplace("text", secondary_text); + return instruction; +} + +// Sub Banner Instructions are used to indicate which lane to use when multiple lanes are +// available. The lane information can be retrieved much like in the maneuver's intersections. +// The new bannerInstruction object's distanceAlongGeometry is determined by the first +// intersection which carries the lane information. +// +// This is very similar to the lane indication of the last intersection(s). +json::MapPtr sub_banner_instruction(const valhalla::DirectionsLeg::Maneuver* prev_maneuver, + valhalla::odin::EnhancedTripLeg* etp) { + json::MapPtr instruction = nullptr; + json::ArrayPtr lanes = nullptr; + + // We only care about the lanes directly before the end of the maneuver + auto edge = etp->GetPrevEdge(prev_maneuver->end_path_index()); + + // Process turn lanes - which are stored on the previous edge to the node + // Check if there is an active turn lane + // Verify that turn lanes are not non-directional + if (edge && (edge->turn_lanes_size() > 0) && edge->HasActiveTurnLane() && + !edge->HasNonDirectionalTurnLane()) { + lanes = json::array({}); + for (const auto& turn_lane : edge->turn_lanes()) { + auto lane = banner_component("lane", ""); + lane->emplace("active", turn_lane.state() == TurnLane::kActive); + // Add active_direction for a valid & active lanes + if (turn_lane.state() != TurnLane::kInvalid) { + lane->emplace("active_direction", turn_lane_direction(turn_lane.active_direction())); + } + lane->emplace("directions", + lane_indications(edge->drive_on_right(), turn_lane.directions_mask())); + lanes->emplace_back(std::move(lane)); + } + } + + if (lanes != nullptr) { + instruction = json::map({}); + instruction->emplace("components", std::move(lanes)); + instruction->emplace("text", std::string("")); + } + + return instruction; +} + +// The roundabout_turn_degrees is approximated by comparing the heading of the last edge +// before the roundabout with the heading of the first edge after the roundabout +uint32_t calc_roundabout_turn_degrees(const valhalla::DirectionsLeg::Maneuver* prev_maneuver, + const valhalla::DirectionsLeg::Maneuver& maneuver, + valhalla::odin::EnhancedTripLeg* etp) { + uint32_t roundabout_turn_degrees = 0; + uint32_t bearing_before = 0; + uint32_t bearing_after = 0; + + if (maneuver.type() == DirectionsLeg_Maneuver_Type_kRoundaboutEnter) { + uint32_t begin_index = maneuver.begin_path_index(); + if (begin_index > 0) { + // This is the normal case where a previous edge exists + bearing_before = etp->GetPrevEdge(begin_index)->end_heading(); + } else { + bearing_before = etp->GetCurrEdge(begin_index)->begin_heading(); + } + + uint32_t end_index = maneuver.end_path_index(); + if (etp->GetCurrEdge(end_index) != nullptr) { + bearing_after = etp->GetCurrEdge(end_index)->begin_heading(); + } + } + + if (prev_maneuver != nullptr && maneuver.type() == DirectionsLeg_Maneuver_Type_kRoundaboutExit) { + uint32_t begin_index = prev_maneuver->begin_path_index(); + if (begin_index > 0) { + // This is the normal case where a previous edge exists + bearing_before = etp->GetPrevEdge(begin_index)->end_heading(); + } else { + bearing_before = etp->GetCurrEdge(begin_index)->begin_heading(); + } + bearing_after = maneuver.begin_heading(); + } + + roundabout_turn_degrees = ((bearing_before - bearing_after - 180) + 720) % 360; + + if (!etp->GetCurrEdge(maneuver.begin_path_index())->drive_on_right()) { + roundabout_turn_degrees = 360 - roundabout_turn_degrees; + } + + return roundabout_turn_degrees; +} + +// Populate the bannerInstructions within a step. +// bannerInstructions are a unified object of maneuvers name, dest, ref and intersection.lanes +json::ArrayPtr banner_instructions(const std::string& name, + const std::string& dest, + const std::string& ref, + const valhalla::DirectionsLeg::Maneuver* prev_maneuver, + const valhalla::DirectionsLeg::Maneuver& maneuver, + const bool arrive_maneuver, + valhalla::odin::EnhancedTripLeg* etp, + const std::string& maneuver_type, + const std::string& modifier, + const std::string& exit, + const double distance, + const std::string& drive_side) { + // bannerInstructions is an array, because there may be multiple similar banner instruction + // objects. Mostly if the 'sub' attribute is to be added along the current step, a new + // instruction is created and the primary and secondary instructions are repeated with the + // additional 'sub' attribute and an updated 'distanceAlongGeometry', which is from where on + // this banner will be shown. + json::ArrayPtr banner_instructions_array = json::array({}); + json::MapPtr banner_instruction_main = json::map({}); + + std::string primary_text = name; + std::string secondary_text = dest; + std::string ref_ = ref; + + // Find a suitable primary_text and secondary_text + if (primary_text.empty() && !secondary_text.empty()) { + primary_text = secondary_text; + secondary_text = std::string(""); + } + if (primary_text.empty() && !ref_.empty()) { + primary_text = ref_; + ref_ = std::string(""); + } + if (arrive_maneuver || primary_text.empty()) { + primary_text = maneuver.text_instruction(); + } + + bool roundabout = maneuver.type() == DirectionsLeg_Maneuver_Type_kRoundaboutEnter || + maneuver.type() == DirectionsLeg_Maneuver_Type_kRoundaboutExit; + + // The roundabout_turn_degrees represents how far around the roundabout a circle the maneuver is. + // If it goes straight through, it's 180 degrees, if it's a quarter circle is 90 degrees, etc. + uint32_t roundabout_turn_degrees = + roundabout ? calc_roundabout_turn_degrees(prev_maneuver, maneuver, etp) : 0; + + // distanceAlongGeometry is the distance along the current step from where on this + // banner should be visible. The first banner starts at the beginning. + banner_instruction_main->emplace("distanceAlongGeometry", json::fixed_t{distance, 3}); + banner_instruction_main->emplace("primary", + primary_banner_instruction(primary_text, ref_, exit, + arrive_maneuver, maneuver_type, + modifier, roundabout, + roundabout_turn_degrees, drive_side)); + + if (!secondary_text.empty()) { + banner_instruction_main->emplace("secondary", secondary_banner_instruction(secondary_text)); + } + + json::MapPtr sub_banner = sub_banner_instruction(prev_maneuver, etp); + + json::MapPtr banner_instruction_with_sub = nullptr; + if (sub_banner != nullptr) { + if (distance > 400) { + banner_instruction_with_sub = json::map({}); + banner_instruction_with_sub->emplace("sub", std::move(sub_banner)); + if (!secondary_text.empty()) { + banner_instruction_with_sub->emplace("secondary", + secondary_banner_instruction(secondary_text)); + } + banner_instruction_with_sub->emplace("primary", + primary_banner_instruction(primary_text, ref_, exit, + arrive_maneuver, maneuver_type, + modifier, roundabout, + roundabout_turn_degrees, + drive_side)); + banner_instruction_with_sub->emplace("distanceAlongGeometry", json::fixed_t{400, 3}); + } else { + banner_instruction_main->emplace("sub", std::move(sub_banner)); + } + } + + banner_instructions_array->emplace_back(std::move(banner_instruction_main)); + + if (banner_instruction_with_sub != nullptr) { + banner_instructions_array->emplace_back(std::move(banner_instruction_with_sub)); + } + + return banner_instructions_array; +} + // Method to get the geometry string for a maneuver. void maneuver_geometry(json::MapPtr& step, const uint32_t begin_idx, @@ -1224,6 +1428,161 @@ void maneuver_geometry(json::MapPtr& step, } } +// The idea is that the instructions come a fixed amount of seconds before the maneuver takes place. +// For whatever reasons, a distance in meters from the end of the maneuver needs to be provided +// though. When different speeds are used on the road, they all need to be taken into account. This +// function calculates the distance before the end of the maneuver by checking the elapsed_cost +// seconds of each edges and accumulates their distances until the seconds threshold is passed. The +// speed of this last edge is then used to subtract the distance so that the the seconds until the end +// are exactly the provided amount of seconds. +float distance_along_geometry(const valhalla::DirectionsLeg::Maneuver* prev_maneuver, + valhalla::odin::EnhancedTripLeg* etp, + const double distance, + const uint32_t target_seconds) { + uint32_t node_index = prev_maneuver->end_path_index(); + double end_node_elapsed_seconds = etp->node(node_index).cost().elapsed_cost().seconds(); + double begin_node_elapsed_seconds = + etp->node(prev_maneuver->begin_path_index()).cost().elapsed_cost().seconds(); + + // If the maneuver is too short, simply return its distance. + if (end_node_elapsed_seconds - begin_node_elapsed_seconds < target_seconds) { + return distance; + } + + float accumulated_distance_km = 0; + float previous_accumulated_distance_km = 0; + double accumulated_seconds = 0; + double previous_accumulated_seconds = 0; + // Find the node after which the instructions should be heard: + while (accumulated_seconds < target_seconds && node_index >= prev_maneuver->begin_path_index()) { + node_index -= 1; + // not really accumulating seconds ourselves, but it happens elsewhere: + previous_accumulated_seconds = accumulated_seconds; + accumulated_seconds = + end_node_elapsed_seconds - etp->node(node_index).cost().elapsed_cost().seconds(); + previous_accumulated_distance_km = accumulated_distance_km; + accumulated_distance_km += etp->GetCurrEdge(node_index)->length_km(); + } + // The node_index now indicates the node AFTER which the target_seconds will be reached + // we now have to subtract the surplus distance (based on seconds) of this edge from the + // accumulated_distance_km + auto surplus_percentage = + (accumulated_seconds - target_seconds) / (accumulated_seconds - previous_accumulated_seconds); + accumulated_distance_km -= + (accumulated_distance_km - previous_accumulated_distance_km) * surplus_percentage; + if (accumulated_distance_km * 1000 > distance) { + return distance; + } else { + return accumulated_distance_km * 1000; // in meters + } +} + +// Populate the voiceInstructions within a step. +json::ArrayPtr voice_instructions(const valhalla::DirectionsLeg::Maneuver* prev_maneuver, + const valhalla::DirectionsLeg::Maneuver& maneuver, + const double distance, + const uint32_t maneuver_index, + valhalla::odin::EnhancedTripLeg* etp) { + // voiceInstructions is an array, because there may be similar voice instructions. + // When the step is long enough, there may be multiple voice instructions. + json::ArrayPtr voice_instructions_array = json::array({}); + + // distanceAlongGeometry is the distance along the current step from where on this + // voice instruction should be played. It is measured from the end of the maneuver. + // Using the maneuver length (distance) as the distanceAlongGeometry plays + // right at the beginning of the maneuver. A distanceAlongGeometry of 10 is + // shortly (10 meters at the given speed) after the maneuver has started. + // The voice_instruction_beginning starts shortly after the beginning of the step. + // The voice_instruction_end starts shortly before the end of the step. + float distance_before_verbal_transition_alert_instruction = -1; + float distance_before_verbal_pre_transition_instruction = -1; + if (prev_maneuver) { + distance_before_verbal_transition_alert_instruction = + distance_along_geometry(prev_maneuver, etp, distance, + SECONDS_BEFORE_VERBAL_TRANSITION_ALERT_INSTRUCTION); + distance_before_verbal_pre_transition_instruction = + distance_along_geometry(prev_maneuver, etp, distance, + SECONDS_BEFORE_VERBAL_PRE_TRANSITION_INSTRUCTION); + if (maneuver_index == 1 && !prev_maneuver->verbal_pre_transition_instruction().empty()) { + // For depart maneuver, we always want to hear the verbal_pre_transition_instruction + // right at the beginning of the navigation. This is something like: + // Drive West on XYZ Street. + // This voice_instruction_start is only created once. It is always played, even when + // the maneuver would otherwise be too short. + json::MapPtr voice_instruction_start = json::map({}); + voice_instruction_start->emplace("distanceAlongGeometry", json::fixed_t{distance, 1}); + voice_instruction_start->emplace("announcement", + prev_maneuver->verbal_pre_transition_instruction()); + voice_instruction_start->emplace("ssmlAnnouncement", + "" + + prev_maneuver->verbal_pre_transition_instruction() + + ""); + voice_instructions_array->emplace_back(std::move(voice_instruction_start)); + } else if (distance > distance_before_verbal_transition_alert_instruction + + APPROXIMATE_VERBAL_POSTRANSITION_LENGTH && + !prev_maneuver->verbal_post_transition_instruction().empty()) { + // In all other cases we want to play the verbal_post_transition_instruction shortly + // after the maneuver has started but only if there is sufficient time to play both + // the upcoming verbal_pre_transition_instruction and the verbal_post_transition_instruction + // itself. The approximation here is that the verbal_post_transition_instruction takes 100 + // meters to play + the 10 meters after the maneuver start which is added so that the + // instruction is not played directly on the intersection where the maneuver starts. + json::MapPtr voice_instruction_beginning = json::map({}); + voice_instruction_beginning->emplace("distanceAlongGeometry", json::fixed_t{distance - 10, 1}); + voice_instruction_beginning->emplace("announcement", + prev_maneuver->verbal_post_transition_instruction()); + voice_instruction_beginning->emplace("ssmlAnnouncement", + "" + + prev_maneuver->verbal_post_transition_instruction() + + ""); + voice_instructions_array->emplace_back(std::move(voice_instruction_beginning)); + } + } + + if (!maneuver.verbal_transition_alert_instruction().empty()) { + json::MapPtr voice_instruction_end = json::map({}); + if (maneuver_index == 1 && distance_before_verbal_transition_alert_instruction == distance) { + // For the depart maneuver we want to play both the verbal_post_transition_instruction and + // the verbal_transition_alert_instruction even if the maneuver is too short. + voice_instruction_end->emplace("distanceAlongGeometry", json::fixed_t{distance / 2, 1}); + } else { + // In all other cases we use distance_before_verbal_transition_alert_instruction value + // as it is capped to the maneuver length + voice_instruction_end + ->emplace("distanceAlongGeometry", + json::fixed_t{distance_before_verbal_transition_alert_instruction, 1}); + } + voice_instruction_end->emplace("announcement", maneuver.verbal_transition_alert_instruction()); + voice_instruction_end->emplace("ssmlAnnouncement", + "" + maneuver.verbal_transition_alert_instruction() + + ""); + voice_instructions_array->emplace_back(std::move(voice_instruction_end)); + } + + if (!maneuver.verbal_pre_transition_instruction().empty()) { + json::MapPtr voice_instruction_end = json::map({}); + if (maneuver_index == 1 && distance_before_verbal_pre_transition_instruction >= distance / 2) { + // For the depart maneuver we want to play the verbal_post_transition_instruction, + // the verbal_transition_alert_instruction and + // the verbal_pre_transition_instruction even if the maneuver is too short. + voice_instruction_end->emplace("distanceAlongGeometry", json::fixed_t{distance / 4, 1}); + } else { + // In all other cases we use distance_before_verbal_pre_transition_instruction value + // as it is capped to the maneuver length + voice_instruction_end->emplace("distanceAlongGeometry", + json::fixed_t{distance_before_verbal_pre_transition_instruction, + 1}); + } + voice_instruction_end->emplace("announcement", maneuver.verbal_pre_transition_instruction()); + voice_instruction_end->emplace("ssmlAnnouncement", + "" + maneuver.verbal_pre_transition_instruction() + + ""); + voice_instructions_array->emplace_back(std::move(voice_instruction_end)); + } + + return voice_instructions_array; +} + // Get the mode std::string get_mode(const valhalla::DirectionsLeg::Maneuver& maneuver, const bool arrive_maneuver, @@ -1338,10 +1697,11 @@ json::ArrayPtr serialize_legs(const google::protobuf::RepeatedPtrField>(leg->shape()); - //######################################################################### - // Iterate through maneuvers - convert to OSRM steps - uint32_t maneuver_index = 0; + // ######################################################################### + // Iterate through maneuvers - convert to OSRM steps + int maneuver_index = 0; uint32_t prev_intersection_count = 0; + double prev_distance = 0; std::string drive_side = "right"; std::string name = ""; std::string ref = ""; @@ -1442,11 +1802,27 @@ json::ArrayPtr serialize_legs(const google::protobuf::RepeatedPtrFieldemplace("rotary_name", maneuver.street_name(0).value()); } + // Get incoming and outgoing bearing. For the incoming heading, use the + // prior edge from the TripLeg. Compute turn modifier. TODO - reconcile + // turn degrees between Valhalla and OSRM + uint32_t idx = maneuver.begin_path_index(); + uint32_t in_brg = (idx > 0) ? etp.GetPrevEdge(idx)->end_heading() : 0; + uint32_t out_brg = maneuver.begin_heading(); + + std::string modifier; + if (!depart_maneuver) { + modifier = turn_modifier(maneuver, in_brg, out_brg, arrive_maneuver); + } + + std::string mnvr_type = + maneuver_type(maneuver, &etp, depart_maneuver, arrive_maneuver, modifier, + prev_intersection_count, mode, prev_mode, rotary, prev_rotary); + // Add OSRM maneuver step->emplace("maneuver", - osrm_maneuver(maneuver, &etp, shape[maneuver.begin_shape_index()], - depart_maneuver, arrive_maneuver, prev_intersection_count, mode, - prev_mode, rotary, prev_rotary, options)); + osrm_maneuver(maneuver, mnvr_type, modifier, in_brg, out_brg, + shape[maneuver.begin_shape_index()], + (options.directions_type() == DirectionsType::instructions))); // Add destinations const auto& sign = maneuver.sign(); @@ -1468,6 +1844,33 @@ json::ArrayPtr serialize_legs(const google::protobuf::RepeatedPtrFieldemplace("exits", ex); } + // Add banner instructions if the user requested them + if (options.banner_instructions()) { + if (prev_step) { + prev_step->emplace("bannerInstructions", + banner_instructions(name, dest, ref, prev_maneuver, maneuver, + arrive_maneuver, &etp, mnvr_type, modifier, ex, + prev_distance, drive_side)); + } + if (arrive_maneuver) { + // just add empty array for arrival maneuver + step->emplace("bannerInstructions", json::array({})); + } + } + + // Add voice instructions if the user requested them + if (options.voice_instructions()) { + if (prev_step) { + prev_step->emplace("voiceInstructions", + voice_instructions(prev_maneuver, maneuver, prev_distance, + maneuver_index, &etp)); + } + if (arrive_maneuver) { + // just add empty array for arrival maneuver + step->emplace("voiceInstructions", json::array({})); + } + } + // Add junction_name if not the start maneuver std::string junction_name = get_sign_elements(sign.junction_names()); if (!depart_maneuver && !junction_name.empty()) { @@ -1509,10 +1912,11 @@ json::ArrayPtr serialize_legs(const google::protobuf::RepeatedPtrFieldemplace_back(std::move(step)); } // end maneuver loop - //######################################################################### + // ######################################################################### // Add distance, duration, weight, and summary // Get a summary based on longest maneuvers. @@ -1593,7 +1997,7 @@ summarize_route_legs(const google::protobuf::RepeatedPtrField& // Find the simplest summary for every leg of every route. Important note: // each route should have the same number of legs. Hence, we only need to make // unique the same leg (leg_idx) between all routes. - for (size_t route_i = 0; route_i < routes.size(); route_i++) { + for (int route_i = 0; route_i < routes.size(); route_i++) { size_t num_legs_i = routes.Get(route_i).legs_size(); std::vector leg_summaries; @@ -1609,7 +2013,7 @@ summarize_route_legs(const google::protobuf::RepeatedPtrField& // Compare every jth route/leg summary vs the current ith route/leg summary. // We desire to compute num_named_segs_needed, which is the number of named // segments needed to uniquely identify the ith's summary. - for (size_t route_j = 0; route_j < routes.size(); route_j++) { + for (int route_j = 0; route_j < routes.size(); route_j++) { // avoid self if (route_i == route_j) @@ -1654,6 +2058,9 @@ summarize_route_legs(const google::protobuf::RepeatedPtrField& return all_summaries; } +} // namespace + +namespace osrm_serializers { // Serialize route response in OSRM compatible format. // Inputs are: // directions options @@ -1719,6 +2126,11 @@ std::string serialize(valhalla::Api& api) { *api.mutable_trip()->mutable_routes(i)->mutable_legs(), imperial, options, controller)); + // Add voice instructions if the user requested them + if (options.voice_instructions()) { + route->emplace("voiceLocale", options.language()); + } + routes->emplace_back(std::move(route)); } @@ -1742,9 +2154,10 @@ std::string serialize(valhalla::Api& api) { using namespace osrm_serializers; +namespace { /// Assert equality of two json documents // -// TODO Improve the diffed view of mis-matching documents +// TODO Improve the diffed view of mismatching documents void assert_json_equality(const rapidjson::Document& doc1, const rapidjson::Document& doc2) { if (doc1 != doc2) { ASSERT_STREQ(rapidjson::serialize(doc1).c_str(), rapidjson::serialize(doc2).c_str()); @@ -1766,7 +2179,7 @@ TEST(RouteSerializerOsrm, testserializeIncidents) { valhalla::IncidentsTile::Metadata meta; meta.set_id( - // Set a large id that excercises the uint64 serialization + // Set a large id that exercises the uint64 serialization 18446744073709551615u); uint64_t creation_time = 1597241829; meta.set_creation_time(creation_time); @@ -2020,15 +2433,25 @@ TEST(RouteSerializerOsrm, testserializeAnnotationsSpeedLimits) { assert_json_equality(serialized_to_json, expected_json); } +TEST(RouteSerializerOsrm, testlaneIndications) { + json::ArrayPtr indications_1 = lane_indications(true, kTurnLaneReverse | kTurnLaneSharpLeft); + json::ArrayPtr indications_2 = + lane_indications(true, kTurnLaneThrough | kTurnLaneRight | kTurnLaneSharpRight); + + ASSERT_EQ(indications_1->size(), 2); + ASSERT_STREQ(boost::get(indications_1->at(0)).c_str(), "uturn"); + ASSERT_STREQ(boost::get(indications_1->at(1)).c_str(), "sharp left"); + + ASSERT_EQ(indications_2->size(), 3); + ASSERT_STREQ(boost::get(indications_2->at(0)).c_str(), "straight"); + ASSERT_STREQ(boost::get(indications_2->at(1)).c_str(), "right"); + ASSERT_STREQ(boost::get(indications_2->at(2)).c_str(), "sharp right"); +} + } // namespace int main(int argc, char* argv[]) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } - -#else - -} // namespace - #endif diff --git a/src/tyr/route_serializer_osrm.h b/src/tyr/route_serializer_osrm.h new file mode 100644 index 0000000000..6187e38861 --- /dev/null +++ b/src/tyr/route_serializer_osrm.h @@ -0,0 +1,44 @@ +#ifndef VALHALLA_TYR_ROUTE_SERIALIZER_OSRM_ +#define VALHALLA_TYR_ROUTE_SERIALIZER_OSRM_ +#pragma once + +#include "proto_conversions.h" + +namespace osrm_serializers { +/* +OSRM output is described in: http://project-osrm.org/docs/v5.5.1/api/ +{ + "code":"Ok" + "waypoints": [{ }, { }...], + "routes": [ + { + "geometry":"....." + "distance":xxx.y + "duration":yyy.z + "legs":[ + { + "steps":[ + "intersections":[ + ] + "geometry":" " + "maneuver":{ + } + ] + } + ] + }, + ... + ] +} +*/ + +// Serialize route response in OSRM compatible format. +// Inputs are: +// directions options +// TripLeg protocol buffer +// DirectionsLeg protocol buffer +std::string serialize(valhalla::Api& api); + +} // namespace osrm_serializers + +#endif // VALHALLA_TYR_ROUTE_SERIALIZER_OSRM_ diff --git a/src/tyr/route_serializer_valhalla.cc b/src/tyr/route_serializer_valhalla.cc index 48b0bb7fa5..39d27d4c08 100644 --- a/src/tyr/route_serializer_valhalla.cc +++ b/src/tyr/route_serializer_valhalla.cc @@ -1,4 +1,3 @@ -#include #include #include "midgard/aabb2.h" @@ -193,6 +192,14 @@ void locations(const valhalla::Api& api, int route_index, rapidjson::writer_wrap writer("date_time", location->date_time()); } + if (!location->time_zone_offset().empty()) { + writer("time_zone_offset", location->time_zone_offset()); + } + + if (!location->time_zone_name().empty()) { + writer("time_zone_name", location->time_zone_name()); + } + if (location->waiting_secs()) { writer("waiting", static_cast(location->waiting_secs())); } @@ -497,10 +504,10 @@ void legs(const valhalla::Api& api, int route_index, rapidjson::writer_wrapper_t // man->emplace("hasGate", maneuver.); // man->emplace("hasFerry", maneuver.); - //“portionsTollNote” : “”, - //“portionsUnpavedNote” : “”, - //“gateAccessRequiredNote” : “”, - //“checkFerryInfoNote” : “” + // “portionsTollNote” : “”, + // “portionsUnpavedNote” : “”, + // “gateAccessRequiredNote” : “”, + // “checkFerryInfoNote” : “” writer.end_object(); // maneuver } @@ -508,6 +515,21 @@ void legs(const valhalla::Api& api, int route_index, rapidjson::writer_wrapper_t writer.end_array(); // maneuvers } + // Store elevation for the leg + if (api.options().elevation_interval() > 0.0f) { + writer.set_precision(1); + float unit_factor = api.options().units() == Options::miles ? kFeetPerMeter : 1.0f; + float interval = api.options().elevation_interval(); + writer("elevation_interval", interval * unit_factor); + auto elevation = tyr::get_elevation(*trip_leg_itr, interval); + + writer.start_array("elevation"); + for (const auto& h : elevation) { + writer(h * unit_factor); + } + writer.end_array(); // elevation + } + writer.start_object("summary"); writer("has_time_restrictions", has_time_restrictions); writer("has_toll", has_toll); diff --git a/src/tyr/route_summary_cache.cc b/src/tyr/route_summary_cache.cc new file mode 100644 index 0000000000..1e0a4c03f0 --- /dev/null +++ b/src/tyr/route_summary_cache.cc @@ -0,0 +1,154 @@ +#include "proto/directions.pb.h" +#include "proto/options.pb.h" +#include "proto/trip.pb.h" +#include "proto_conversions.h" + +#include "./route_summary_cache.h" + +namespace valhalla { +namespace tyr { + +NamedSegment& NamedSegment::operator=(const NamedSegment& ns) { + name = ns.name; + index = ns.index; + distance = ns.distance; + return *this; +} + +NamedSegment& NamedSegment::operator=(NamedSegment&& ns) noexcept { + name = std::move(ns.name); + index = ns.index; + distance = ns.distance; + return *this; +} + +route_summary_cache::route_summary_cache( + const google::protobuf::RepeatedPtrField& routes) { + // A route/leg/maneuver may go on/off the same named road many times. + // Combine the distances for same named roads - store in a NamedSegment. + route_leg_segs_by_dist.reserve(routes.size()); + for (const auto& route : routes) { + std::vector> leg_segs_by_dist; + leg_segs_by_dist.reserve(route.legs_size()); + for (int j = 0; j < route.legs_size(); j++) { + const DirectionsLeg& leg = route.legs(j); + std::unordered_map> maneuver_summary_map; + maneuver_summary_map.reserve(leg.maneuver_size()); + uint32_t maneuver_index = 0; + for (const auto& maneuver : leg.maneuver()) { + if (maneuver.street_name_size() > 0) { + const std::string& name = maneuver.street_name(0).value(); + auto maneuver_summary = maneuver_summary_map.find(name); + if (maneuver_summary == maneuver_summary_map.end()) { + maneuver_summary_map[name] = std::make_pair(maneuver_index, maneuver.length()); + } else { + maneuver_summary->second.second += maneuver.length(); + } + } + ++maneuver_index; + } + + // Create a list of named segments (maneuver name, maneuver index, distance) + // sorted by distance. + std::vector segs_by_dist; + segs_by_dist.reserve(maneuver_summary_map.size()); + for (const auto& map_item : maneuver_summary_map) { + segs_by_dist.emplace_back(map_item.first, map_item.second.first, map_item.second.second); + } + + // Sort list by descending maneuver distance + std::sort(segs_by_dist.begin(), segs_by_dist.end(), + [](const NamedSegment& a, const NamedSegment& b) { return b.distance < a.distance; }); + + leg_segs_by_dist.emplace_back(std::move(segs_by_dist)); + } + + route_leg_segs_by_dist.emplace_back(std::move(leg_segs_by_dist)); + } + + // fill the cache with empty results so we can safely index it with the same + // dimensionality as the route_leg_segs_by_dist object. + cache.reserve(route_leg_segs_by_dist.size()); + for (size_t i = 0; i < route_leg_segs_by_dist.size(); i++) { + cache.emplace_back(); + cache[i].reserve(route_leg_segs_by_dist[i].size()); + for (size_t j = 0; j < route_leg_segs_by_dist[i].size(); j++) { + cache[i].emplace_back(); + cache[i][j].reserve(route_leg_segs_by_dist[i][j].size()); + for (size_t k = 0; k < route_leg_segs_by_dist[i][j].size(); k++) { + cache[i][j].emplace_back(); + } + } + } +} + +size_t route_summary_cache::num_named_segments_for_route_leg(size_t route_idx, size_t leg_idx) { + if (route_idx >= route_leg_segs_by_dist.size()) { + return 0; + } + + if (leg_idx >= route_leg_segs_by_dist[route_idx].size()) { + return 0; + } + + return route_leg_segs_by_dist[route_idx][leg_idx].size(); +} + +// Return the n-part named-segment summary for the given route/leg. +// The summary returned is guaranteed to be comprised of as few named +// segments as possible, while also being unique among all route/same-leg +// summaries. +std::string +route_summary_cache::get_n_segment_summary(size_t route_idx, size_t leg_idx, size_t num_named_segs) { + if (route_idx >= cache.size()) { + return ""; + } + + if (leg_idx >= cache[route_idx].size()) { + return ""; + } + + if (num_named_segs == 0) { + return ""; + } + + // num_named_segs is the number of named segments you'd like the summary to + // be comprised of. The smallest requestable value is 1 - so we store all + // summaries offset by n = num_named_segs - 1. + size_t n = num_named_segs - 1; + if (n >= cache[route_idx][leg_idx].size()) { + return ""; + } + + // empty summary means cache miss + if (cache[route_idx][leg_idx][n].empty()) { + // go figure out the summary + std::vector segs_by_maneuver_index; + segs_by_maneuver_index.reserve(num_named_segs); + for (size_t i = 0; i < num_named_segs; i++) { + segs_by_maneuver_index.emplace_back(&route_leg_segs_by_dist[route_idx][leg_idx][i]); + } + + // sort by maneuver index + std::sort(segs_by_maneuver_index.begin(), segs_by_maneuver_index.end(), + [](const NamedSegment* a, const NamedSegment* b) { return a->index < b->index; }); + + std::string summary; + for (size_t i = 0; i < num_named_segs; i++) { + summary += segs_by_maneuver_index[i]->name; + if (i != num_named_segs - 1) + summary += ", "; + } + + misses++; + + cache[route_idx][leg_idx][n] = std::move(summary); + } else { + hits++; + } + + return cache[route_idx][leg_idx][n]; +} + +} // namespace tyr +} // namespace valhalla diff --git a/src/tyr/route_summary_cache.h b/src/tyr/route_summary_cache.h index 7ae7d85cd9..c3bd42b4a7 100644 --- a/src/tyr/route_summary_cache.h +++ b/src/tyr/route_summary_cache.h @@ -1,3 +1,5 @@ +#ifndef VALHALLA_TYR_ROUTE_SUMMARY_CACHE_H_ +#define VALHALLA_TYR_ROUTE_SUMMARY_CACHE_H_ #pragma once #include "proto/directions.pb.h" @@ -22,19 +24,9 @@ struct NamedSegment { NamedSegment(NamedSegment&& ns) : name(std::move(ns.name)), index(ns.index), distance(ns.distance) { } - NamedSegment& operator=(const NamedSegment& ns) { - name = ns.name; - index = ns.index; - distance = ns.distance; - return *this; - } + NamedSegment& operator=(const NamedSegment& ns); - NamedSegment& operator=(NamedSegment&& ns) { - name = std::move(ns.name); - index = ns.index; - distance = ns.distance; - return *this; - } + NamedSegment& operator=(NamedSegment&& ns) noexcept; }; //============================================================================= @@ -90,135 +82,16 @@ class route_summary_cache { int misses = 0; public: - route_summary_cache(const google::protobuf::RepeatedPtrField& routes) { - // A route/leg/maneuver may go on/off the same named road many times. - // Combine the distances for same named roads - store in a NamedSegment. - route_leg_segs_by_dist.reserve(routes.size()); - for (size_t i = 0; i < routes.size(); i++) { - const DirectionsRoute& route = routes.Get(i); - std::vector> leg_segs_by_dist; - leg_segs_by_dist.reserve(route.legs_size()); - for (size_t j = 0; j < route.legs_size(); j++) { - const DirectionsLeg& leg = route.legs(j); - std::unordered_map> maneuver_summary_map; - maneuver_summary_map.reserve(leg.maneuver_size()); - uint32_t maneuver_index = 0; - for (const auto& maneuver : leg.maneuver()) { - if (maneuver.street_name_size() > 0) { - const std::string& name = maneuver.street_name(0).value(); - auto maneuver_summary = maneuver_summary_map.find(name); - if (maneuver_summary == maneuver_summary_map.end()) { - maneuver_summary_map[name] = std::make_pair(maneuver_index, maneuver.length()); - } else { - maneuver_summary->second.second += maneuver.length(); - } - } - ++maneuver_index; - } - - // Create a list of named segments (maneuver name, maneuver index, distance) - // sorted by distance. - std::vector segs_by_dist; - segs_by_dist.reserve(maneuver_summary_map.size()); - for (const auto& map_item : maneuver_summary_map) { - segs_by_dist.emplace_back(map_item.first, map_item.second.first, map_item.second.second); - } - - // Sort list by descending maneuver distance - std::sort(segs_by_dist.begin(), segs_by_dist.end(), - [](const NamedSegment& a, const NamedSegment& b) { - return b.distance < a.distance; - }); - - leg_segs_by_dist.emplace_back(std::move(segs_by_dist)); - } - - route_leg_segs_by_dist.emplace_back(std::move(leg_segs_by_dist)); - } - - // fill the cache with empty results so we can safely index it with the same - // dimensionality as the route_leg_segs_by_dist object. - cache.reserve(route_leg_segs_by_dist.size()); - for (size_t i = 0; i < route_leg_segs_by_dist.size(); i++) { - cache.emplace_back(); - cache[i].reserve(route_leg_segs_by_dist[i].size()); - for (size_t j = 0; j < route_leg_segs_by_dist[i].size(); j++) { - cache[i].emplace_back(); - cache[i][j].reserve(route_leg_segs_by_dist[i][j].size()); - for (size_t k = 0; k < route_leg_segs_by_dist[i][j].size(); k++) { - cache[i][j].emplace_back(); - } - } - } - } - - size_t num_named_segments_for_route_leg(size_t route_idx, size_t leg_idx) { - if (route_idx >= route_leg_segs_by_dist.size()) { - return 0; - } - - if (leg_idx >= route_leg_segs_by_dist[route_idx].size()) { - return 0; - } - - return route_leg_segs_by_dist[route_idx][leg_idx].size(); - } + route_summary_cache(const google::protobuf::RepeatedPtrField& routes); + size_t num_named_segments_for_route_leg(size_t route_idx, size_t leg_idx); // Return the n-part named-segment summary for the given route/leg. // The summary returned is guaranteed to be comprised of as few named // segments as possible, while also being unique among all route/same-leg // summaries. - std::string get_n_segment_summary(size_t route_idx, size_t leg_idx, size_t num_named_segs) { - if (route_idx >= cache.size()) { - return ""; - } - - if (leg_idx >= cache[route_idx].size()) { - return ""; - } - - if (num_named_segs == 0) { - return ""; - } - - // num_named_segs is the number of named segments you'd like the summary to - // be comprised of. The smallest requestable value is 1 - so we store all - // summaries offset by n = num_named_segs - 1. - size_t n = num_named_segs - 1; - if (n >= cache[route_idx][leg_idx].size()) { - return ""; - } - - // empty summary means cache miss - if (cache[route_idx][leg_idx][n].empty()) { - // go figure out the summary - std::vector segs_by_maneuver_index; - segs_by_maneuver_index.reserve(num_named_segs); - for (size_t i = 0; i < num_named_segs; i++) { - segs_by_maneuver_index.emplace_back(&route_leg_segs_by_dist[route_idx][leg_idx][i]); - } - - // sort by maneuver index - std::sort(segs_by_maneuver_index.begin(), segs_by_maneuver_index.end(), - [](const NamedSegment* a, const NamedSegment* b) { return a->index < b->index; }); - - std::string summary; - for (size_t i = 0; i < num_named_segs; i++) { - summary += segs_by_maneuver_index[i]->name; - if (i != num_named_segs - 1) - summary += ", "; - } - - misses++; - - cache[route_idx][leg_idx][n] = std::move(summary); - } else { - hits++; - } - - return cache[route_idx][leg_idx][n]; - } + std::string get_n_segment_summary(size_t route_idx, size_t leg_idx, size_t num_named_segs); }; - } // namespace tyr -} // namespace valhalla \ No newline at end of file +} // namespace valhalla + +#endif // VALHALLA_TYR_ROUTE_SUMMARY_CACHE_H diff --git a/src/tyr/serializers.cc b/src/tyr/serializers.cc index 0f038c87be..6cc8404eaa 100644 --- a/src/tyr/serializers.cc +++ b/src/tyr/serializers.cc @@ -1,11 +1,7 @@ #include -#include #include -#include -#include #include #include -#include #include #include "baldr/datetime.h" @@ -210,6 +206,15 @@ std::string serializePbf(Api& request) { case Options::status: selection.set_status(true); break; + case Options::sources_to_targets: + selection.set_matrix(true); + break; + case Options::isochrone: + selection.set_isochrone(true); + break; + case Options::expansion: + selection.set_expansion(true); + break; // should never get here, actions which dont have pbf yet return json default: throw std::logic_error("Requested action is not yet serializable as pbf"); @@ -233,6 +238,12 @@ std::string serializePbf(Api& request) { request.clear_status(); if (!selection.options()) request.clear_options(); + if (!selection.matrix()) + request.clear_matrix(); + if (!selection.isochrone()) + request.clear_isochrone(); + if (!selection.expansion()) + request.clear_expansion(); // serialize the bytes auto bytes = request.SerializeAsString(); @@ -244,6 +255,20 @@ std::string serializePbf(Api& request) { return bytes; } + +// Generate leg shape in geojson format. +baldr::json::MapPtr geojson_shape(const std::vector shape) { + auto geojson = baldr::json::map({}); + auto coords = baldr::json::array({}); + coords->reserve(shape.size()); + for (const auto& p : shape) { + coords->emplace_back( + baldr::json::array({baldr::json::fixed_t{p.lng(), 6}, baldr::json::fixed_t{p.lat(), 6}})); + } + geojson->emplace("type", std::string("LineString")); + geojson->emplace("coordinates", coords); + return geojson; +} } // namespace tyr } // namespace valhalla @@ -420,7 +445,7 @@ void serializeIncidentProperties(rapidjson::Writer& wri writer.Key(key_prefix + "alertc_codes"); writer.StartArray(); for (const auto& alertc_code : incident_metadata.alertc_codes()) { - writer.Int(static_cast(alertc_code)); + writer.Uint64(static_cast(alertc_code)); } writer.EndArray(); } diff --git a/src/tyr/trace_serializer.cc b/src/tyr/trace_serializer.cc index 0b509a2f16..a883bc3299 100644 --- a/src/tyr/trace_serializer.cc +++ b/src/tyr/trace_serializer.cc @@ -10,7 +10,6 @@ using namespace valhalla; using namespace valhalla::midgard; using namespace valhalla::baldr; using namespace valhalla::odin; -using namespace valhalla::thor; namespace { @@ -190,9 +189,18 @@ void serialize_edges(const AttributesController& controller, if (controller(kEdgeSpeed)) { writer("speed", static_cast(std::round(edge.speed() * scale))); } + if (controller(kEdgeCountryCrossing)) { + writer("country_crossing", static_cast(edge.country_crossing())); + } if (controller(kEdgeLength)) { writer.set_precision(3); writer("length", edge.length_km() * scale); + if (edge.source_along_edge() != 0.f) { + writer("source_percent_along", edge.source_along_edge()); + } + if (edge.target_along_edge() != 1.f) { + writer("target_percent_along", edge.target_along_edge()); + } } // TODO: do we want to output 'is_route_number'? if (edge.name_size() > 0) { @@ -303,6 +311,7 @@ void serialize_edges(const AttributesController& controller, if (controller(kNodeElapsedTime)) { writer.set_precision(3); writer("elapsed_time", node.cost().elapsed_cost().seconds()); + writer("elapsed_cost", node.cost().elapsed_cost().cost()); } if (controller(kNodeAdminIndex)) { writer("admin_index", static_cast(node.admin_index())); @@ -489,6 +498,20 @@ void append_trace_info( // Add edges serialize_edges(controller, options, trip_path, writer); + // Add elevation at the specified interval + if (options.elevation_interval() > 0.0f) { + float unit_factor = options.units() == Options::miles ? kFeetPerMeter : 1.0f; + float interval = options.elevation_interval(); + writer.set_precision(1); + writer("elevation_interval", interval * unit_factor); + writer.start_array("elevation"); + auto elevation = tyr::get_elevation(trip_path, interval); + for (const auto& h : elevation) { + writer(h * unit_factor); + } + writer.end_array(); + } + // Add matched points, if requested if (controller.category_attribute_enabled(kMatchedCategory) && !match_results.empty()) { serialize_matched_points(controller, match_results, writer); diff --git a/src/valhalla_benchmark_adjacency_list.cc b/src/valhalla_benchmark_adjacency_list.cc index 8d23604ce5..b96ef8c317 100644 --- a/src/valhalla_benchmark_adjacency_list.cc +++ b/src/valhalla_benchmark_adjacency_list.cc @@ -6,11 +6,11 @@ #include #include -#include "config.h" #include "midgard/logging.h" #include "midgard/util.h" #include "sif/edgelabel.h" +#include "argparse_utils.h" #include "baldr/double_bucket_queue.h" using namespace valhalla::midgard; @@ -104,12 +104,16 @@ int Benchmark(const uint32_t n, const float maxcost, const float bucketsize) { } int main(int argc, char* argv[]) { + const auto program = filesystem::path(__FILE__).stem().string(); + // args + boost::property_tree::ptree config; + try { // clang-format off cxxopts::Options options( - "valhalla_benchmakr_adjacency_list", - "valhalla " VALHALLA_VERSION "\n\n" - "valhalla_benchmakr_adjacency_list is benchmark comparing performance of an STL priority_queue\n" + program, + program + " " + VALHALLA_VERSION + "\n\n" + "a program which is benchmark comparing performance of an STL priority_queue\n" "to the approximate double bucket adjacency list class supplied with Valhalla.\n\n"); options.add_options() @@ -117,18 +121,14 @@ int main(int argc, char* argv[]) { ("v,version", "Print the version of this software."); auto result = options.parse(argc, argv); - - if (result.count("help")) { - std::cout << options.help() << "\n"; + if (!parse_common_args(program, options, result, config, "mjolnir.logging")) return EXIT_SUCCESS; - } - - if (result.count("version")) { - std::cout << "valhalla_benchmakr_adjacency_list " << VALHALLA_VERSION << "\n"; - return EXIT_SUCCESS; - } - } catch (const cxxopts::OptionException& e) { - std::cout << "Unable to parse command line options because: " << e.what() << std::endl; + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; return EXIT_FAILURE; } diff --git a/src/valhalla_benchmark_loki.cc b/src/valhalla_benchmark_loki.cc index 97a5d16202..8f3b988707 100644 --- a/src/valhalla_benchmark_loki.cc +++ b/src/valhalla_benchmark_loki.cc @@ -13,7 +13,6 @@ #include #include "baldr/rapidjson_utils.h" -#include "config.h" #include "filesystem.h" #include "loki/search.h" #include "midgard/logging.h" @@ -21,11 +20,9 @@ #include "sif/costfactory.h" #include "worker.h" -filesystem::path config_file_path; -size_t threads, batch, isolated, radius; -bool extrema = false; +#include "argparse_utils.h" + std::string costing_str; -std::vector input_files; using job_t = std::vector; std::vector jobs; @@ -72,64 +69,6 @@ struct result_t { }; using results_t = std::set; -int ParseArguments(int argc, char* argv[]) { - - try { - // clang-format off - cxxopts::Options options( - "valhalla_benchmark_loki", - "valhalla_benchmark_loki " VALHALLA_VERSION "\n\n" - "valhalla_benchmark_loki is a program that does location searches on tiled route data.\n" - "To run it use a valid config file to let it know where the tiled route data \n" - "is. The input is simply a text file of one location per line\n\n"); - - std::string search_type; - options.add_options() - ("h,help", "Print this help message.") - ("v,version", "Print the version of this software.") - ("c,config", "Path to the json configuration file.", cxxopts::value()) - ("t,threads", "Concurrency to use.", cxxopts::value(threads)->default_value(std::to_string(std::thread::hardware_concurrency()))) - ("b,batch", "Number of locations to group together per search", cxxopts::value(batch)->default_value("1")) - ("e,extrema", "Show the input locations of the extrema for a given statistic", cxxopts::value(extrema)->default_value("false")) - ("i,reach", "How many edges need to be reachable before considering it as connected to the larger network", cxxopts::value(isolated)) - ("r,radius", "How many meters to search away from the input location", cxxopts::value(radius)->default_value("0")) - ("costing", "Which costing model to use.", cxxopts::value(costing_str)->default_value("auto")) - ("input_files", "positional arguments", cxxopts::value>(input_files)); - // clang-format on - - options.parse_positional({"input_files"}); - options.positional_help("LOCATIONS.TXT"); - auto result = options.parse(argc, argv); - - if (result.count("help")) { - std::cout << options.help() << "\n"; - exit(0); - } - - if (result.count("version")) { - std::cout << "valhalla_benchmark_loki " << VALHALLA_VERSION << "\n"; - exit(0); - } - - if (!result.count("input_files")) { - std::cerr << "Input file is required\n\n" << options.help() << "\n\n"; - return false; - } - - if (result.count("config") && - filesystem::is_regular_file(config_file_path = - filesystem::path(result["config"].as()))) { - return true; - } else { - std::cerr << "Configuration file is required\n\n" << options.help() << "\n\n"; - } - } catch (const cxxopts::OptionException& e) { - std::cout << "Unable to parse command line options because: " << e.what() << std::endl; - } - - return false; -} - valhalla::sif::cost_ptr_t create_costing() { valhalla::Options options; valhalla::Costing::Type costing; @@ -183,21 +122,52 @@ void work(const boost::property_tree::ptree& config, std::promise& pr } int main(int argc, char** argv) { - if (!ParseArguments(argc, argv)) { - return EXIT_FAILURE; - } + const auto program = filesystem::path(__FILE__).stem().string(); + // args + size_t batch, isolated, radius; + bool extrema = false; + std::vector input_files; + boost::property_tree::ptree config; - // check what type of input we are getting - boost::property_tree::ptree pt; - rapidjson::read_json(config_file_path.string(), pt); + try { + // clang-format off + cxxopts::Options options( + program, + program + " " + VALHALLA_VERSION + "\n\n" + "a program that does location searches on tiled route data.\n" + "To run it use a valid config file to let it know where the tiled route data \n" + "is. The input is simply a text file of one location per line\n\n"); - // configure logging - auto logging_subtree = pt.get_child_optional("loki.logging"); - if (logging_subtree) { - auto logging_config = - valhalla::midgard::ToMap>(logging_subtree.get()); - valhalla::midgard::logging::Configure(logging_config); + std::string search_type; + options.add_options() + ("h,help", "Print this help message.") + ("v,version", "Print the version of this software.") + ("c,config", "Path to the json configuration file.", cxxopts::value()) + ("j,concurrency", "Number of threads to use. Defaults to all threads.", cxxopts::value()) + ("b,batch", "Number of locations to group together per search", cxxopts::value(batch)->default_value("1")) + ("e,extrema", "Show the input locations of the extrema for a given statistic", cxxopts::value(extrema)->default_value("false")) + ("i,reach", "How many edges need to be reachable before considering it as connected to the larger network", cxxopts::value(isolated)) + ("r,radius", "How many meters to search away from the input location", cxxopts::value(radius)->default_value("0")) + ("costing", "Which costing model to use.", cxxopts::value(costing_str)->default_value("auto")) + ("input_files", "positional arguments", cxxopts::value>(input_files)); + // clang-format on + + options.parse_positional({"input_files"}); + options.positional_help("LOCATIONS.TXT"); + auto result = options.parse(argc, argv); + if (!parse_common_args(program, options, result, config, "loki.logging")) + return EXIT_SUCCESS; + + if (!result.count("input_files")) { + throw cxxopts::exceptions::exception("Input file is required\n\n" + options.help()); + } + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; + return EXIT_FAILURE; } // fill up the queue with work @@ -236,9 +206,10 @@ int main(int argc, char** argv) { // start up the threads std::list pool; - std::vector> pool_results(threads); - for (size_t i = 0; i < threads; ++i) { - pool.emplace_back(work, std::cref(pt), std::ref(pool_results[i])); + const auto num_threads = config.get("mjolnir.concurrency"); + std::vector> pool_results(num_threads); + for (size_t i = 0; i < num_threads; ++i) { + pool.emplace_back(work, std::cref(config), std::ref(pool_results[i])); } // let the threads finish up diff --git a/src/valhalla_expand_bounding_box.cc b/src/valhalla_expand_bounding_box.cc index 76db540f72..56d5b7e521 100644 --- a/src/valhalla_expand_bounding_box.cc +++ b/src/valhalla_expand_bounding_box.cc @@ -8,68 +8,47 @@ #include "config.h" #include "filesystem.h" +#include "argparse_utils.h" + namespace bpt = boost::property_tree; int main(int argc, char** argv) { - std::string bbox, config_file_path; - boost::property_tree::ptree pt; + const auto program = filesystem::path(__FILE__).stem().string(); + // args + std::string bbox; + boost::property_tree::ptree config; try { // clang-format off cxxopts::Options options( - "valhalla_expand_bounding_box", - "valhalla_expand_bounding_box " VALHALLA_VERSION "\n\n" + program, + program + " " + VALHALLA_VERSION + "\n\n" "Finds all the nodes in the bounding box and then expands \n" "the bounding box by the shape of the edges that leave the nodes.\n\n"); options.add_options() ("h,help", "Print this help message.") ("v,version", "Print the version of this software.") - ("c,config", "Path to the json configuration file.", cxxopts::value(config_file_path)) + ("c,config", "Path to the json configuration file.", cxxopts::value()) ("i,inline-config", "Inline json config.", cxxopts::value()) ("b,bounding-box", "Bounding box to expand. The format is min_x,min_y,max_x,max_y. Required", cxxopts::value(bbox)); // clang-format on auto result = options.parse(argc, argv); - - if (result.count("help")) { - std::cout << options.help() << "\n"; - return EXIT_SUCCESS; - } - - if (result.count("version")) { - std::cout << "valhalla_expand_bounding_box " << VALHALLA_VERSION << "\n"; + if (!parse_common_args(program, options, result, config, "mjolnir.logging")) return EXIT_SUCCESS; - } - - // Read the config file - if (result.count("inline-config")) { - std::stringstream ss; - ss << result["inline-config"].as(); - rapidjson::read_json(ss, pt); - } else if (result.count("config") && filesystem::is_regular_file(config_file_path)) { - rapidjson::read_json(config_file_path, pt); - } else { - std::cerr << "Configuration is required\n\n" << options.help() << "\n\n"; - return EXIT_FAILURE; - } - - // configure logging - auto logging_subtree = pt.get_child_optional("mjolnir.logging"); - if (logging_subtree) { - auto logging_config = valhalla::midgard::ToMap>( - logging_subtree.get()); - valhalla::midgard::logging::Configure(logging_config); - } if (!result.count("bounding-box")) { std::cerr << "You must provide a bounding box to expand.\n\n"; std::cerr << options.help() << std::endl; return EXIT_FAILURE; } - } catch (const cxxopts::OptionException& e) { - std::cout << "Unable to parse command line options because: " << e.what() << std::endl; + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; return EXIT_FAILURE; } @@ -89,7 +68,7 @@ int main(int argc, char** argv) { valhalla::midgard::AABB2 bb{{result[0], result[1]}, {result[2], result[3]}}; - valhalla::baldr::GraphReader reader(pt.get_child("mjolnir")); + valhalla::baldr::GraphReader reader(config.get_child("mjolnir")); bb = reader.GetMinimumBoundingBox(bb); std::cout << std::fixed << std::setprecision(6) << bb.minx() << "," << bb.miny() << "," << bb.maxx() diff --git a/src/valhalla_export_edges.cc b/src/valhalla_export_edges.cc index ec39588ad0..65867776d5 100644 --- a/src/valhalla_export_edges.cc +++ b/src/valhalla_export_edges.cc @@ -12,15 +12,14 @@ #include #include #include -#include -#include "config.h" +#include "argparse_utils.h" using namespace valhalla::midgard; using namespace valhalla::baldr; // global options instead of passing them around -std::string row_separator, column_separator, config; +std::string row_separator, column_separator; bool ferries, unnamed; namespace { @@ -141,59 +140,49 @@ void extend(GraphReader& reader, // program entry point int main(int argc, char* argv[]) { + const auto program = filesystem::path(__FILE__).stem().string(); + // args + std::string bbox; + boost::property_tree::ptree config; try { // clang-format off cxxopts::Options options( - "valhalla_export_edges", - "valhalla_export_edges " VALHALLA_VERSION "\n\n" - "valhalla_export_edges is a simple command line test tool which\n" + program, + program + " " + VALHALLA_VERSION + "\n\n" + "a simple command line test tool which\n" "dumps information about each graph edge.\n\n"); using namespace std::string_literals; options.add_options() ("h,help", "Print this help message.") ("v,version", "Print the version of this software.") - ("c,column", "What separator to use between columns [default=\\0].", cxxopts::value(column_separator)->default_value("\0"s)) + ("c,config", "Path to the json configuration file.", cxxopts::value()) + ("i,inline-config", "Inline json config.", cxxopts::value()) + ("x,column", "What separator to use between columns [default=\\0].", cxxopts::value(column_separator)->default_value("\0"s)) ("r,row", "What separator to use between row [default=\\n].", cxxopts::value(row_separator)->default_value("\n")) ("f,ferries", "Export ferries as well [default=false]", cxxopts::value(ferries)->default_value("false")) - ("u,unnamed", "Export unnamed edges as well [default=false]", cxxopts::value(unnamed)->default_value("false")) - ("config", "positional argument", cxxopts::value(config)); + ("u,unnamed", "Export unnamed edges as well [default=false]", cxxopts::value(unnamed)->default_value("false")); // clang-format on - options.parse_positional({"config"}); - options.positional_help("Config file path"); auto result = options.parse(argc, argv); - - if (result.count("help")) { - std::cout << options.help() << "\n"; - return EXIT_SUCCESS; - } - - if (result.count("version")) { - std::cout << "valhalla_export_edges " << VALHALLA_VERSION << "\n"; + if (!parse_common_args(program, options, result, config, "")) return EXIT_SUCCESS; - } - - if (!result.count("config") || !filesystem::is_regular_file(filesystem::path(config))) { - std::cerr << "Configuration file is required\n\n" << options.help() << "\n\n"; - return EXIT_FAILURE; - } - } catch (const cxxopts::OptionException& e) { - std::cout << "Unable to parse command line options because: " << e.what() << std::endl; + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; return EXIT_FAILURE; } - // parse the config - boost::property_tree::ptree pt; - rapidjson::read_json(config.c_str(), pt); + // get something we can use to fetch tiles + valhalla::baldr::GraphReader reader(config.get_child("mjolnir")); - // configure logging + // configure logging here, we want it to go to stderr valhalla::midgard::logging::Configure({{"type", "std_err"}, {"color", "true"}}); - // get something we can use to fetch tiles - valhalla::baldr::GraphReader reader(pt.get_child("mjolnir")); - // keep the global number of edges encountered at the point we encounter each tile // this allows an edge to have a sequential global id and makes storing it very small LOG_INFO("Enumerating edges..."); diff --git a/src/valhalla_path_comparison.cc b/src/valhalla_path_comparison.cc index 3aeb30c35f..f75d5ba1dd 100644 --- a/src/valhalla_path_comparison.cc +++ b/src/valhalla_path_comparison.cc @@ -6,7 +6,6 @@ #include #include -#include "config.h" #include "worker.h" #include "baldr/graphid.h" @@ -22,6 +21,8 @@ #include "thor/pathinfo.h" #include "thor/route_matcher.h" +#include "argparse_utils.h" + using namespace valhalla; using namespace valhalla::sif; using namespace valhalla::meili; @@ -58,8 +59,8 @@ void print_edge(GraphReader& reader, auto node_id = pred_edge->endnode(); auto node_tile = reader.GetGraphTile(node_id); auto node = node_tile->node(node_id); - EdgeLabel pred_label(0, pred_id, pred_edge, {}, 0.0f, 0.0f, static_cast(0), 0, - {}, kInvalidRestriction, true, false, InternalTurn::kNoTurn); + EdgeLabel pred_label(0, pred_id, pred_edge, {}, 0.0f, static_cast(0), 0, + kInvalidRestriction, true, false, InternalTurn::kNoTurn); std::cout << "-------Transition-------\n"; std::cout << "Pred GraphId: " << pred_id << std::endl; Cost trans_cost = costing->TransitionCost(edge, node, pred_label); @@ -154,65 +155,56 @@ void walk_edges(const std::string& shape, } // args -std::string routetype, config; +std::string routetype, route_config; std::string json_str = ""; std::string shape = ""; // Main method for testing a single path int main(int argc, char* argv[]) { + const auto program = filesystem::path(__FILE__).stem().string(); + // args + boost::property_tree::ptree config; + try { // clang-format off cxxopts::Options options( - "valhalla_path_comparison", - "valhalla_path_comparison " VALHALLA_VERSION "\n\n" - "valhalla_path_comparison is a simple command line dev tool for comparing the cost between " + program, + program + " " + VALHALLA_VERSION + "\n\n" + "a simple command line dev tool for comparing the cost between " "two routes.\n" "Use the -j option for specifying the locations or the -s option to enter an encoded shape.\n\n"); options.add_options() ("h,help", "Print this help message.") ("v,version", "Print the version of this software.") + ("c,config", "Path to the json configuration file.", cxxopts::value()) + ("i,inline-config", "Inline json config.", cxxopts::value()) ("t,type", "Route Type: auto|bicycle|pedestrian|truck etc. Default auto.", cxxopts::value()->default_value("auto")) ("s,shape", "", cxxopts::value()) ("j,json", R"(JSON Example: {"paths":" "[[{"lat":12.47,"lon":15.2},{"lat":12.46,"lon":15.21}],[{"lat":12.36,"lon":15.17},{"lat":12.37,"lon":15.18}]]," - "costing":"bicycle","costing_options":{"bicycle":{"use_roads":0.55,"use_hills":0.1}}})", cxxopts::value()) - ("config", "positional argument", cxxopts::value()); + "costing":"bicycle","costing_options":{"bicycle":{"use_roads":0.55,"use_hills":0.1}}})", cxxopts::value()); // clang-format on - options.parse_positional({"config"}); - options.positional_help("Config file path"); auto result = options.parse(argc, argv); - - if (result.count("help")) { - std::cout << options.help() << "\n"; - return EXIT_SUCCESS; - } - - if (result.count("version")) { - std::cout << "valhalla_path_comparison " << VALHALLA_VERSION << "\n"; + if (!parse_common_args(program, options, result, config, "mjolnir.logging")) return EXIT_SUCCESS; - } - - if (result.count("config") && - filesystem::is_regular_file(filesystem::path(result["config"].as()))) { - config = result["config"].as(); - } else { - std::cerr << "Configuration file is required\n\n" << options.help() << "\n\n"; - return EXIT_FAILURE; - } if (result.count("json")) { json_str = result["json"].as(); } else if (result.count("shape")) { shape = result["shape"].as(); } else { - std::cerr << "The json parameter or shape parameter was not supplied but is required.\n\n" - << options.help() << std::endl; - return EXIT_FAILURE; + throw cxxopts::exceptions::exception( + "The json parameter or shape parameter was not supplied but is required.\n\n" + + options.help()); } - } catch (const cxxopts::OptionException& e) { - std::cout << "Unable to parse command line options because: " << e.what() << std::endl; + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; return EXIT_FAILURE; } @@ -272,17 +264,8 @@ int main(int argc, char* argv[]) { map_match = false; } - // parse the config - boost::property_tree::ptree pt; - rapidjson::read_json(config.c_str(), pt); - // Get something we can use to fetch tiles - valhalla::baldr::GraphReader reader(pt.get_child("mjolnir")); - - if (!map_match) { - rapidjson::Document doc; - sif::ParseCosting(doc, "/costing_options", *request.mutable_options()); - } + valhalla::baldr::GraphReader reader(config.get_child("mjolnir")); // Construct costing valhalla::Costing::Type costing; @@ -291,6 +274,12 @@ int main(int argc, char* argv[]) { } else { throw std::runtime_error("No costing method found"); } + + if (!map_match) { + rapidjson::Document doc; + sif::ParseCosting(doc, "/costing_options", *request.mutable_options()); + } + valhalla::sif::TravelMode mode; auto mode_costings = valhalla::sif::CostFactory{}.CreateModeCosting(request.options(), mode); auto cost_ptr = mode_costings[static_cast(mode)]; @@ -302,7 +291,7 @@ int main(int argc, char* argv[]) { } // If JSON is entered we do map matching - MapMatcherFactory map_matcher_factory(pt); + MapMatcherFactory map_matcher_factory(config); std::shared_ptr matcher(map_matcher_factory.Create(request.options())); uint32_t i = 0; diff --git a/src/valhalla_run_isochrone.cc b/src/valhalla_run_isochrone.cc index 2285347ce3..ac61bedff9 100644 --- a/src/valhalla_run_isochrone.cc +++ b/src/valhalla_run_isochrone.cc @@ -8,20 +8,18 @@ #include +#include "argparse_utils.h" #include "baldr/graphreader.h" #include "baldr/pathlocation.h" #include "loki/search.h" #include "midgard/logging.h" +#include "proto/options.pb.h" #include "sif/costconstants.h" #include "sif/costfactory.h" #include "thor/isochrone.h" #include "tyr/serializers.h" #include "worker.h" -#include "proto/options.pb.h" - -#include "config.h" - using namespace valhalla; using namespace valhalla::midgard; using namespace valhalla::baldr; @@ -31,17 +29,18 @@ using namespace valhalla::thor; // Main method for testing a single path int main(int argc, char* argv[]) { + const auto program = filesystem::path(__FILE__).stem().string(); // args - std::string json_str, config; + std::string json_str; std::string filename = ""; - boost::property_tree::ptree pt; + boost::property_tree::ptree config; try { // clang-format off cxxopts::Options options( - "valhalla_run_isochrone", - "valhalla_run_isochrone " VALHALLA_VERSION "\n\n" - "valhalla_run_isochrone is a simple command line test tool for generating an isochrone.\n" + program, + program + " " + VALHALLA_VERSION + "\n\n" + "a simple command line test tool for generating an isochrone.\n" "Use the -j option for specifying the location and isocrhone options.\n\n"); options.add_options() @@ -51,59 +50,36 @@ int main(int argc, char* argv[]) { "'{\"locations\":[{\"lat\":40.748174,\"lon\":-73.984984}],\"costing\":" "\"auto\",\"contours\":[{\"time\":15,\"color\":\"ff0000\"}]}'", cxxopts::value()) ("c,config", "Valhalla configuration file", cxxopts::value()) - ("f,file", "GeoJSON file name. If omitted program will print to stdout.", cxxopts::value()); + ("i,inline-config", "Inline JSON config", cxxopts::value()) + ("f,file", "file name. If omitted program will print to stdout.", cxxopts::value()); // clang-format on auto result = options.parse(argc, argv); - - if (result.count("help")) { - std::cout << options.help() << "\n"; + if (!parse_common_args(program, options, result, config, "mjolnir.logging")) return EXIT_SUCCESS; - } - - if (result.count("version")) { - std::cout << "valhalla_run_isochrone " << VALHALLA_VERSION << "\n"; - return EXIT_SUCCESS; - } - - if (result.count("config") && - filesystem::is_regular_file(filesystem::path(result["config"].as()))) { - config = result["config"].as(); - } else { - std::cerr << "Configuration file is required\n\n" << options.help() << "\n\n"; - return EXIT_FAILURE; - } - - // parse the config - rapidjson::read_json(config.c_str(), pt); - - // configure logging - auto logging_subtree = pt.get_child_optional("thor.logging"); - if (logging_subtree) { - auto logging_config = valhalla::midgard::ToMap>( - logging_subtree.get()); - valhalla::midgard::logging::Configure(logging_config); - } if (!result.count("json")) { - std::cerr << "A JSON format request must be present." - << "\n"; - return EXIT_FAILURE; + throw cxxopts::exceptions::exception("A JSON format request must be present.\n\n" + + options.help()); } json_str = result["json"].as(); if (result.count("file")) { filename = result["file"].as(); } - } catch (const cxxopts::OptionException& e) { - std::cout << "Unable to parse command line options because: " << e.what() << std::endl; + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; return EXIT_FAILURE; } // Process json request Api request; ParseApi(json_str, valhalla::Options::isochrone, request); + auto& options = *request.mutable_options(); // Get the denoise parameter @@ -155,7 +131,7 @@ int main(int argc, char* argv[]) { } // Get something we can use to fetch tiles - valhalla::baldr::GraphReader reader(pt.get_child("mjolnir")); + valhalla::baldr::GraphReader reader(config.get_child("mjolnir")); // Construct costing CostFactory factory; @@ -203,11 +179,11 @@ int main(int argc, char* argv[]) { // Compute the isotile auto t1 = std::chrono::high_resolution_clock::now(); - Isochrone isochrone; + valhalla::thor::Isochrone isochrone; auto expansion_type = routetype == "multimodal" ? ExpansionType::multimodal : (reverse ? ExpansionType::reverse : ExpansionType::forward); - auto isotile = isochrone.Expand(expansion_type, request, reader, mode_costing, mode); + auto isogrid = isochrone.Expand(expansion_type, request, reader, mode_costing, mode); auto t2 = std::chrono::high_resolution_clock::now(); uint32_t msecs = std::chrono::duration_cast(t2 - t1).count(); @@ -215,27 +191,23 @@ int main(int argc, char* argv[]) { // Generate contours t2 = std::chrono::high_resolution_clock::now(); - auto contours = isotile->GenerateContours(contour_times, polygons, denoise, generalize); auto t3 = std::chrono::high_resolution_clock::now(); msecs = std::chrono::duration_cast(t3 - t2).count(); LOG_INFO("Contour Generation took " + std::to_string(msecs) + " ms"); - // Serialize to GeoJSON - std::string geojson = - valhalla::tyr::serializeIsochrones(request, contour_times, contours, polygons, show_locations); - + std::string res = valhalla::tyr::serializeIsochrones(request, contour_times, isogrid); auto t4 = std::chrono::high_resolution_clock::now(); msecs = std::chrono::duration_cast(t4 - t3).count(); - LOG_INFO("GeoJSON serialization took " + std::to_string(msecs) + " ms"); + LOG_INFO("Isochrone serialization took " + std::to_string(msecs) + " ms"); msecs = std::chrono::duration_cast(t4 - t1).count(); LOG_INFO("Isochrone took " + std::to_string(msecs) + " ms"); if (!filename.empty()) { - std::ofstream geojsonOut(filename, std::ofstream::out); - geojsonOut << geojson; - geojsonOut.close(); + std::ofstream resOut(filename, std::ofstream::out); + resOut << res; + resOut.close(); } else { - std::cout << "\n" << geojson << std::endl; + std::cout << "\n" << res << std::endl; } // Shutdown protocol buffer library diff --git a/src/valhalla_run_matrix.cc b/src/valhalla_run_matrix.cc index 5de085aee1..3a1160d049 100644 --- a/src/valhalla_run_matrix.cc +++ b/src/valhalla_run_matrix.cc @@ -7,12 +7,11 @@ #include #include "baldr/rapidjson_utils.h" -#include #include +#include "argparse_utils.h" #include "baldr/graphreader.h" #include "baldr/pathlocation.h" -#include "config.h" #include "loki/worker.h" #include "midgard/logging.h" #include "odin/directionsbuilder.h" @@ -44,32 +43,36 @@ std::string GetFormattedTime(uint32_t secs) { // Log results void LogResults(const bool optimize, const valhalla::Options& options, - const std::vector& res) { - LOG_INFO("Results:"); - uint32_t idx1 = 0; - uint32_t idx2 = 0; - uint32_t nlocs = options.sources_size(); - for (auto& td : res) { - LOG_INFO(std::to_string(idx1) + "," + std::to_string(idx2) + - ": Distance= " + std::to_string(td.dist) + " Time= " + GetFormattedTime(td.time) + - " secs = " + std::to_string(td.time)); - idx2++; - if (idx2 == nlocs) { - idx2 = 0; - idx1++; + const valhalla::Matrix& matrix, + const bool log_details) { + + if (log_details) { + LOG_INFO("Results:"); + uint32_t idx1 = 0; + uint32_t idx2 = 0; + for (uint32_t i = 0; i < matrix.times().size(); i++) { + auto distance = matrix.distances().Get(i); + LOG_INFO(std::to_string(idx1) + "," + std::to_string(idx2) + ": Distance= " + + std::to_string(distance) + " Time= " + GetFormattedTime(matrix.times().Get(i)) + + " secs = " + std::to_string(distance)); + idx2++; + if (idx2 == options.sources_size()) { + idx2 = 0; + idx1++; + } } } if (optimize) { // Optimize the path auto t10 = std::chrono::high_resolution_clock::now(); std::vector costs; - costs.reserve(res.size()); - for (auto& td : res) { - costs.push_back(static_cast(td.time)); + costs.reserve(matrix.times().size()); + for (const auto& time : matrix.times()) { + costs.push_back(time); } Optimizer opt; - auto tour = opt.Solve(nlocs, costs); + auto tour = opt.Solve(static_cast(options.sources_size()), costs); LOG_INFO("Optimal Tour:"); for (auto& loc : tour) { LOG_INFO(" : " + std::to_string(loc)); @@ -82,16 +85,20 @@ void LogResults(const bool optimize, // Main method for testing time and distance matrix methods int main(int argc, char* argv[]) { + const auto program = filesystem::path(__FILE__).stem().string(); // args - std::string json_str, config; + std::string json_str; uint32_t iterations; + bool log_details; + bool optimize; + boost::property_tree::ptree config; try { // clang-format off cxxopts::Options options( - "valhalla_run_matrix", - "valhalla_run_matrix " VALHALLA_VERSION "\n\n" - "valhalla_run_matrix is a command line test tool for time+distance matrix routing.\n" + program, + program + " " + VALHALLA_VERSION + "\n\n" + "a command line test tool for time+distance matrix routing.\n" "Use the -j option for specifying source to target locations."); options.add_options() @@ -106,39 +113,31 @@ int main(int argc, char* argv[]) { "York\",\"state\":\"NY\",\"postal_code\":\"10017-3507\",\"country\":\"US\"}],\"costing\":" "\"auto\",\"directions_options\":{\"units\":\"miles\"}}'", cxxopts::value()) ("m,multi-run", "Generate the route N additional times before exiting.", cxxopts::value()->default_value("1")) - ("c,config", "Valhalla configuration file", cxxopts::value()); + ("l,log-details", "Logs details about the solution", cxxopts::value()->default_value("false")) + ("o,optimize", "Run optimization", cxxopts::value()->default_value("false")) + ("c,config", "Valhalla configuration file", cxxopts::value()) + ("i,inline-config", "Inline JSON config", cxxopts::value()); // clang-format on auto result = options.parse(argc, argv); - - if (result.count("help")) { - std::cout << options.help() << "\n"; + if (!parse_common_args(program, options, result, config, "mjolnir.logging")) return EXIT_SUCCESS; - } - - if (result.count("version")) { - std::cout << "valhalla_run_matrix " << VALHALLA_VERSION << "\n"; - return EXIT_SUCCESS; - } - - if (result.count("config") && - filesystem::is_regular_file(filesystem::path(result["config"].as()))) { - config = result["config"].as(); - } else { - std::cerr << "Configuration file is required\n\n" << options.help() << "\n\n"; - return EXIT_FAILURE; - } if (!result.count("json")) { - std::cerr << "A JSON format request must be present." - << "\n"; - return EXIT_FAILURE; + throw cxxopts::exceptions::exception("A JSON format request must be present.\n\n" + + options.help()); } - json_str = result["json"].as(); + json_str = result["json"].as(); iterations = result["multi-run"].as(); - } catch (const cxxopts::OptionException& e) { - std::cout << "Unable to parse command line options because: " << e.what() << std::endl; + log_details = result["log-details"].as(); + optimize = result["optimize"].as(); + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << "\n"; return EXIT_FAILURE; } @@ -146,21 +145,8 @@ int main(int argc, char* argv[]) { ParseApi(json_str, valhalla::Options::sources_to_targets, request); auto& options = *request.mutable_options(); - // parse the config - boost::property_tree::ptree pt; - rapidjson::read_json(config.c_str(), pt); - - // configure logging - auto logging_subtree = pt.get_child_optional("thor.logging"); - if (logging_subtree) { - auto logging_config = - valhalla::midgard::ToMap>(logging_subtree.get()); - valhalla::midgard::logging::Configure(logging_config); - } - // Get something we can use to fetch tiles - valhalla::baldr::GraphReader reader(pt.get_child("mjolnir")); + valhalla::baldr::GraphReader reader(config.get_child("mjolnir")); // Construct costing CostFactory factory; @@ -175,7 +161,7 @@ int main(int argc, char* argv[]) { // Find path locations (loki) for sources and targets auto t0 = std::chrono::high_resolution_clock::now(); - loki_worker_t lw(pt); + loki_worker_t lw(config); lw.matrix(request); auto t1 = std::chrono::high_resolution_clock::now(); uint32_t ms = std::chrono::duration_cast(t1 - t0).count(); @@ -183,7 +169,7 @@ int main(int argc, char* argv[]) { // Get the max matrix distances for construction of the CostMatrix and TimeDistanceMatrix classes std::unordered_map max_matrix_distance; - for (const auto& kv : pt.get_child("service_limits")) { + for (const auto& kv : config.get_child("service_limits")) { // Skip over any service limits that are not for a costing method if (kv.first == "max_exclude_locations" || kv.first == "max_reachability" || kv.first == "max_radius" || kv.first == "max_timedep_distance" || kv.first == "skadi" || @@ -193,8 +179,8 @@ int main(int argc, char* argv[]) { kv.first == "max_distance_disable_hierarchy_culling") { continue; } - max_matrix_distance.emplace(kv.first, - pt.get("service_limits." + kv.first + ".max_matrix_distance")); + max_matrix_distance.emplace(kv.first, config.get("service_limits." + kv.first + + ".max_matrix_distance")); } if (max_matrix_distance.empty()) { @@ -210,11 +196,11 @@ int main(int argc, char* argv[]) { } // If the sources and targets are equal we can run optimize - bool optimize = true; - if (options.sources_size() == options.targets_size()) { + if (optimize && options.sources_size() == options.targets_size()) { for (uint32_t i = 0; i < options.sources_size(); ++i) { if (options.sources(i).ll().lat() != options.targets(i).ll().lat() || options.sources(i).ll().lng() != options.targets(i).ll().lng()) { + LOG_WARN("Targets differ from sources, skipping optimization..."); optimize = false; break; } @@ -227,34 +213,31 @@ int main(int argc, char* argv[]) { } // Timing with CostMatrix - std::vector res; CostMatrix matrix; t0 = std::chrono::high_resolution_clock::now(); for (uint32_t n = 0; n < iterations; n++) { - res.clear(); - res = matrix.SourceToTarget(*options.mutable_sources(), *options.mutable_targets(), reader, - mode_costing, mode, max_distance); - matrix.clear(); + request.clear_matrix(); + matrix.SourceToTarget(request, reader, mode_costing, mode, max_distance); + matrix.Clear(); } t1 = std::chrono::high_resolution_clock::now(); ms = std::chrono::duration_cast(t1 - t0).count(); float avg = (static_cast(ms) / static_cast(iterations)) * 0.001f; LOG_INFO("CostMatrix average time to compute: " + std::to_string(avg) + " sec"); - LogResults(optimize, options, res); + LogResults(optimize, options, request.matrix(), log_details); // Run with TimeDistanceMatrix TimeDistanceMatrix tdm; for (uint32_t n = 0; n < iterations; n++) { - res.clear(); - res = tdm.SourceToTarget(*options.mutable_sources(), *options.mutable_targets(), reader, - mode_costing, mode, max_distance); - tdm.clear(); + request.clear_matrix(); + tdm.SourceToTarget(request, reader, mode_costing, mode, max_distance); + tdm.Clear(); } t1 = std::chrono::high_resolution_clock::now(); ms = std::chrono::duration_cast(t1 - t0).count(); avg = (static_cast(ms) / static_cast(iterations)) * 0.001f; LOG_INFO("TimeDistanceMatrix average time to compute: " + std::to_string(avg) + " sec"); - LogResults(optimize, options, res); + LogResults(optimize, options, request.matrix(), log_details); // Shutdown protocol buffer library google::protobuf::ShutdownProtobufLibrary(); diff --git a/src/valhalla_run_route.cc b/src/valhalla_run_route.cc index 20f07c91d2..64cc726f01 100644 --- a/src/valhalla_run_route.cc +++ b/src/valhalla_run_route.cc @@ -4,9 +4,7 @@ #include #include #include -#include #include -#include #include #include @@ -39,7 +37,7 @@ #include "proto/options.pb.h" #include "proto/trip.pb.h" -#include "config.h" +#include "argparse_utils.h" using namespace valhalla::midgard; using namespace valhalla::baldr; @@ -68,14 +66,14 @@ class PathStatistics { uint32_t trip_time; float trip_dist; float arc_dist; - uint32_t manuevers; + uint32_t maneuvers; double elapsed_cost_seconds; double elapsed_cost_cost; public: PathStatistics(std::pair p1, std::pair p2) : origin(p1), destination(p2), success("false"), passes(0), runtime(), trip_time(), trip_dist(), - arc_dist(), manuevers(), elapsed_cost_seconds(0), elapsed_cost_cost(0) { + arc_dist(), maneuvers(), elapsed_cost_seconds(0), elapsed_cost_cost(0) { } void setSuccess(std::string s) { @@ -96,8 +94,8 @@ class PathStatistics { void setArcDist(float d) { arc_dist = d; } - void setManuevers(uint32_t n) { - manuevers = n; + void setManeuvers(uint32_t n) { + maneuvers = n; } void setElapsedCostSeconds(double secs) { elapsed_cost_seconds = secs; @@ -109,7 +107,7 @@ class PathStatistics { valhalla::midgard::logging::Log((boost::format("%f,%f,%f,%f,%s,%d,%d,%d,%f,%f,%d,%f,%f") % origin.first % origin.second % destination.first % destination.second % success % passes % runtime % trip_time % - trip_dist % arc_dist % manuevers % elapsed_cost_seconds % + trip_dist % arc_dist % maneuvers % elapsed_cost_seconds % elapsed_cost_cost) .str(), " [STATISTICS] "); @@ -227,7 +225,7 @@ const valhalla::TripLeg* PathTest(GraphReader& reader, if (ret) { LOG_INFO("RouteMatcher succeeded"); } else { - LOG_ERROR("RouteMatcher failed"); + LOG_ERROR("RouteMatcher failed."); } } @@ -454,7 +452,7 @@ valhalla::DirectionsLeg DirectionsTest(valhalla::Api& api, } data.setTripTime(trip_directions.summary().time()); data.setTripDist(trip_directions.summary().length()); - data.setManuevers(trip_directions.maneuver_size()); + data.setManeuvers(trip_directions.maneuver_size()); data.setElapsedCostSeconds(etl.node().rbegin()->cost().elapsed_cost().seconds()); data.setElapsedCostCost(etl.node().rbegin()->cost().elapsed_cost().cost()); @@ -463,11 +461,11 @@ valhalla::DirectionsLeg DirectionsTest(valhalla::Api& api, // Main method for testing a single path int main(int argc, char* argv[]) { + const auto program = filesystem::path(__FILE__).stem().string(); // args - std::string json_str, json_file, config; - filesystem::path config_file_path; + std::string json_str, json_file; + boost::property_tree::ptree config; - boost::property_tree::ptree pt; bool match_test, verbose_lanes; bool multi_run = false; uint32_t iterations; @@ -475,9 +473,9 @@ int main(int argc, char* argv[]) { try { // clang-format off cxxopts::Options options( - "valhalla_run_route", - "valhalla_run_route " VALHALLA_VERSION "\n\n" - "valhalla_run_route is a command line test tool for shortest path routing.\n" + program, + program + " " + VALHALLA_VERSION + "\n\n" + "a command line test tool for shortest path routing.\n" "Use the -j option for specifying the locations and costing method and options."); options.add_options() @@ -495,39 +493,13 @@ int main(int argc, char* argv[]) { ("match-test", "Test RouteMatcher with resulting shape.", cxxopts::value(match_test)->default_value("false")) ("multi-run", "Generate the route N additional times before exiting.", cxxopts::value(iterations)->default_value("1")) ("verbose-lanes", "Include verbose lanes output in DirectionsTest.", cxxopts::value(verbose_lanes)->default_value("false")) - ("c,config", "Valhalla configuration file", cxxopts::value()); + ("c,config", "Valhalla configuration file", cxxopts::value()) + ("i,inline-config", "Inline JSON config", cxxopts::value()); // clang-format on auto result = options.parse(argc, argv); - - if (result.count("help")) { - std::cout << options.help() << "\n"; + if (!parse_common_args(program, options, result, config, "mjolnir.logging")) return EXIT_SUCCESS; - } - - if (result.count("version")) { - std::cout << "valhalla_run_route " << VALHALLA_VERSION << "\n"; - return EXIT_SUCCESS; - } - - // parse the config - if (result.count("config") && - filesystem::is_regular_file(config_file_path = - filesystem::path(result["config"].as()))) { - config = config_file_path.string(); - } else { - std::cerr << "Configuration file is required\n\n" << options.help() << "\n\n"; - } - rapidjson::read_json(config.c_str(), pt); - - // configure logging - auto logging_subtree = pt.get_child_optional("thor.logging"); - if (logging_subtree) { - auto logging_config = valhalla::midgard::ToMap>( - logging_subtree.get()); - valhalla::midgard::logging::Configure(logging_config); - } if (iterations > 1) { multi_run = true; @@ -541,11 +513,14 @@ int main(int argc, char* argv[]) { } else if (result.count("json")) { json_str = result["json"].as(); } else { - std::cerr << "Either json or json-file args must be set." << std::endl; - return EXIT_FAILURE; + throw cxxopts::exceptions::exception("Either json or json-file args must be set."); } - } catch (const cxxopts::OptionException& e) { - std::cout << "Unable to parse command line options because: " << e.what() << std::endl; + } catch (cxxopts::exceptions::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception& e) { + std::cerr << "Unable to parse command line options because: " << e.what() << "\n" + << "This is a bug, please report it at " PACKAGE_BUGREPORT << std::endl; return EXIT_FAILURE; } @@ -574,11 +549,11 @@ int main(int argc, char* argv[]) { d1 += locations[i].latlng_.Distance(locations[i + 1].latlng_) * kKmPerMeter; } // Get something we can use to fetch tiles - valhalla::baldr::GraphReader reader(pt.get_child("mjolnir")); + valhalla::baldr::GraphReader reader(config.get_child("mjolnir")); // Get the maximum distance for time dependent routes float max_timedep_distance = - pt.get("service_limits.max_timedep_distance", kDefaultMaxTimeDependentDistance); + config.get("service_limits.max_timedep_distance", kDefaultMaxTimeDependentDistance); auto t0 = std::chrono::high_resolution_clock::now(); @@ -590,7 +565,7 @@ int main(int argc, char* argv[]) { // Find path locations (loki) for sources and targets auto tw0 = std::chrono::high_resolution_clock::now(); - loki_worker_t lw(pt); + loki_worker_t lw(config); auto tw1 = std::chrono::high_resolution_clock::now(); auto msw = std::chrono::duration_cast(tw1 - tw0).count(); LOG_INFO("Location Worker construction took " + std::to_string(msw) + " ms"); @@ -602,11 +577,11 @@ int main(int argc, char* argv[]) { LOG_INFO("Location Processing took " + std::to_string(ms) + " ms"); // Get the route - BidirectionalAStar bd(pt.get_child("thor")); - MultiModalPathAlgorithm mm(pt.get_child("thor")); - TimeDepForward timedep_forward(pt.get_child("thor")); - TimeDepReverse timedep_reverse(pt.get_child("thor")); - MarkupFormatter markup_formatter(pt); + BidirectionalAStar bd(config.get_child("thor")); + MultiModalPathAlgorithm mm(config.get_child("thor")); + TimeDepForward timedep_forward(config.get_child("thor")); + TimeDepReverse timedep_reverse(config.get_child("thor")); + MarkupFormatter markup_formatter(config); for (uint32_t i = 0; i < n; i++) { // Set origin and destination for this segment valhalla::Location origin = options.locations(i); diff --git a/src/valhalla_service.cc b/src/valhalla_service.cc index d87e6bd1f4..54d69913e4 100644 --- a/src/valhalla_service.cc +++ b/src/valhalla_service.cc @@ -1,25 +1,17 @@ #include -#include #include #include -#include -#include -#include -#include #include #include #include -#include -#include "baldr/rapidjson_utils.h" -#include - -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES #include #include using namespace prime_server; #endif +#include "config.h" #include "midgard/logging.h" #include "loki/worker.h" @@ -28,7 +20,7 @@ using namespace prime_server; #include "tyr/actor.h" int main(int argc, char** argv) { -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES if (argc < 2 || argc > 4) { LOG_ERROR("Usage: " + std::string(argv[0]) + " config/file.json [concurrency]"); LOG_ERROR("Usage: " + std::string(argv[0]) + " config/file.json action json_request"); @@ -44,8 +36,7 @@ int main(int argc, char** argv) { // config file // TODO: validate the config std::string config_file(argv[1]); - boost::property_tree::ptree config; - rapidjson::read_json(config_file, config); + boost::property_tree::ptree config = valhalla::config(config_file); // one shot direct request mode if (argc == 4) { @@ -140,7 +131,7 @@ int main(int argc, char** argv) { return 0; } -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES // gracefully shutdown when asked via SIGTERM prime_server::quiesce(config.get("httpd.service.drain_seconds", 28), config.get("httpd.service.shutting_seconds", 1)); diff --git a/src/worker.cc b/src/worker.cc index da922e2ef9..1154e542e1 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -1,4 +1,3 @@ -#include #include #include #include @@ -22,7 +21,7 @@ #include using namespace valhalla; -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES using namespace prime_server; #endif @@ -138,6 +137,8 @@ const std::unordered_map error_codes{ {445, {445, "Shape match algorithm specification in api request is incorrect. Please see documentation for valid shape_match input.", 400, HTTP_400, OSRM_INVALID_URL, "wrong_match_type"}}, {499, {499, "Unknown", 400, HTTP_400, OSRM_INVALID_URL, "unknown"}}, {503, {503, "Leg count mismatch", 400, HTTP_400, OSRM_INVALID_URL, "wrong_number_of_legs"}}, + {504, {504, "This service does not support GeoTIFF serialization.", 400, HTTP_400, OSRM_INVALID_VALUE, "unknown"}}, + {599, {599, "Unknown serialization error", 400, HTTP_400, OSRM_INVALID_VALUE, "unknown"}}, }; // unordered map for warning pairs @@ -156,9 +157,12 @@ const std::unordered_map warning_codes = { {204, R"("exclude_polygons" received invalid input, ignoring exclude_polygons)"}, {205, R"("disable_hierarchy_pruning" exceeded the max distance, ignoring disable_hierarchy_pruning)"}, {206, R"(CostMatrix does not consider "targets" with "date_time" set, ignoring date_time)"}, + {207, R"(TimeDistanceMatrix does not consider "shape_format", ignoring shape_format)"}, // 3xx is used when costing options were specified but we had to change them internally for some reason {300, R"(Many:Many CostMatrix was requested, but server only allows 1:Many TimeDistanceMatrix)"}, {301, R"(1:Many TimeDistanceMatrix was requested, but server only allows Many:Many CostMatrix)"}, + // 4xx is used when we do sneaky important things the user should be aware of + {400, R"(CostMatrix turned off destination-only on a second pass for connections: )"} }; // clang-format on @@ -370,6 +374,13 @@ void parse_location(valhalla::Location* location, if (street_side_max_distance) { location->set_street_side_max_distance(*street_side_max_distance); } + auto street_side_cutoff = rapidjson::get_optional(r_loc, "/street_side_cutoff"); + if (street_side_cutoff) { + valhalla::RoadClass cutoff_street_side; + if (RoadClass_Enum_Parse(*street_side_cutoff, &cutoff_street_side)) { + location->set_street_side_cutoff(cutoff_street_side); + } + } boost::optional exclude_closures; // is it json? @@ -534,7 +545,7 @@ void parse_locations(const rapidjson::Document& doc, // Forward valhalla_exception_t types as-is, since they contain a more specific error message catch (const valhalla_exception_t& e) { throw e; - } // generic execptions and other stuff get a generic message + } // generic exceptions and other stuff get a generic message catch (...) { throw valhalla_exception_t{location_parse_error_code}; } @@ -576,6 +587,38 @@ void parse_contours(const rapidjson::Document& doc, } } +// parse all costings needed to fulfill the request, including recostings +void parse_recostings(const rapidjson::Document& doc, + const std::string& key, + valhalla::Options& options) { + // make sure we only have unique recosting names in the end + std::unordered_set names; + auto check_name = [&names](const valhalla::Costing& recosting) -> void { + if (!recosting.has_name_case()) { + throw valhalla_exception_t{127}; + } else if (!names.insert(recosting.name()).second) { + throw valhalla_exception_t{128}; + } + }; + + // look either in JSON & PBF + auto recostings = rapidjson::get_child_optional(doc, "/recostings"); + if (recostings && recostings->IsArray()) { + names.reserve(recostings->GetArray().Size()); + for (size_t i = 0; i < recostings->GetArray().Size(); ++i) { + // parse the options + std::string key = "/recostings/" + std::to_string(i); + sif::ParseCosting(doc, key, options.add_recostings()); + check_name(*options.recostings().rbegin()); + } + } else if (options.recostings().size()) { + for (auto& recosting : *options.mutable_recostings()) { + check_name(recosting); + sif::ParseCosting(doc, key, &recosting, recosting.type()); + } + } +} + /** * This function takes a json document and parses it into an options (request pbf) object. * The implementation is such that if you passed an already filled out options object the @@ -637,10 +680,15 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { // so that we serialize correctly at the end we fix up any request discrepancies if (options.format() == Options::pbf) { - const std::unordered_set pbf_actions{ - Options::route, Options::optimized_route, Options::trace_route, - Options::centroid, Options::trace_attributes, Options::status, - }; + const std::unordered_set pbf_actions{Options::route, + Options::optimized_route, + Options::trace_route, + Options::centroid, + Options::trace_attributes, + Options::status, + Options::sources_to_targets, + Options::isochrone, + Options::expansion}; // if its not a pbf supported action we reset to json if (pbf_actions.count(options.action()) == 0) { options.set_format(Options::json); @@ -649,6 +697,11 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { options.clear_jsonp(); } } +#ifndef ENABLE_GDAL + else if (options.format() == Options::geotiff) { + throw valhalla_exception_t{504}; + } +#endif auto units = rapidjson::get_optional(doc, "/units"); if (units && ((*units == "miles") || (*units == "mi"))) { @@ -733,7 +786,7 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { rapidjson::SetValueByPointer(doc, "/costing_options/auto/ignore_closures", true); } - // set the costing based on the name given, redundant for pbf input + // set the costing based on the name given and parse its costing options Costing::Type costing; if (!valhalla::Costing_Enum_Parse(costing_str, &costing)) throw valhalla_exception_t{125, "'" + costing_str + "'"}; @@ -796,10 +849,17 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { options.set_shape_format(polyline5); } else if (*shape_format == "geojson") { options.set_shape_format(geojson); + } else if (*shape_format == "no_shape") { + if (action == Options::height) { + throw valhalla_exception_t{164}; + } + options.set_shape_format(no_shape); } else { // Throw an error if shape format is invalid throw valhalla_exception_t{164}; } + } else if (action == Options::sources_to_targets) { + options.set_shape_format(options.has_shape_format_case() ? options.shape_format() : no_shape); } // whether or not to output b64 encoded openlr @@ -822,6 +882,12 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { } } + // TODO(nils): if we parse the costing options before the ignore_closures logic, + // the gurka_closure_penalty test fails. investigate why.. intuitively it makes no sense, + // as in the above logic the costing options aren't even parsed yet, + // so how can it determine "ignore_closures" there? + sif::ParseCosting(doc, "/costing_options", options); + // if any of the locations params have a date_time object in their locations, we'll remember // only /sources_to_targets will parse more than one location collection and there it's fine bool had_date_time = false; @@ -921,6 +987,19 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { } } + // Get the elevation interval for returning elevation along the path in a route or + // trace attribute call. Defaults to 0.0 (no elevation is returned) + constexpr float kMaxElevationInterval = 1000.0f; + auto elevation_interval = rapidjson::get_optional(doc, "/elevation_interval"); + if (elevation_interval) { + options.set_elevation_interval( + std::max(std::min(*elevation_interval, kMaxElevationInterval), 0.0f)); + } else { + // Constrain to range [0-kMaxElevationInterval] + options.set_elevation_interval( + std::max(std::min(options.elevation_interval(), kMaxElevationInterval), 0.0f)); + } + // Elevation service options options.set_range(rapidjson::get(doc, "/range", options.range())); constexpr uint32_t MAX_HEIGHT_PRECISION = 2; @@ -937,27 +1016,8 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { options.set_verbose(rapidjson::get(doc, "/verbose", options.verbose())); } - // Parse all of the costing options in their specified order - sif::ParseCosting(doc, "/costing_options", options); - // parse any named costings for re-costing a given path - auto recostings = rapidjson::get_child_optional(doc, "/recostings"); - if (recostings && recostings->IsArray()) { - // make sure we only have unique recosting names in the end - std::unordered_set names; - names.reserve(recostings->GetArray().Size()); - for (size_t i = 0; i < recostings->GetArray().Size(); ++i) { - // parse the options - std::string key = "/recostings/" + std::to_string(i); - sif::ParseCosting(doc, key, options.add_recostings()); - if (!options.recostings().rbegin()->has_name_case()) { - throw valhalla_exception_t{127}; - } else if (!names.insert(options.recostings().rbegin()->name()).second) { - throw valhalla_exception_t{128}; - } - } - // TODO: throw if not all names are unique? - } + parse_recostings(doc, "/recostings", options); // get the locations in there parse_locations(doc, api, "locations", 130, ignore_closures, had_date_time); @@ -1057,6 +1117,9 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { // should the expansion track opposites? options.set_skip_opposites(rapidjson::get(doc, "/skip_opposites", options.skip_opposites())); + // should the expansion be less verbose, printing each edge only once, default false + options.set_dedupe(rapidjson::get(doc, "/dedupe", options.dedupe())); + // get the contours in there parse_contours(doc, options.mutable_contours()); @@ -1159,6 +1222,14 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { // whether to return guidance_views, default false options.set_guidance_views(rapidjson::get(doc, "/guidance_views", options.guidance_views())); + // whether to return bannerInstructions in OSRM serializer, default false + options.set_banner_instructions( + rapidjson::get(doc, "/banner_instructions", options.banner_instructions())); + + // whether to return voiceInstructions in OSRM serializer, default false + options.set_voice_instructions( + rapidjson::get(doc, "/voice_instructions", options.voice_instructions())); + // whether to include roundabout_exit maneuvers, default true auto roundabout_exits = rapidjson::get(doc, "/roundabout_exits", @@ -1186,12 +1257,12 @@ valhalla_exception_t::valhalla_exception_t(unsigned code, const std::string& ext } // function to add warnings to proto info object -void add_warning(valhalla::Api& api, unsigned code) { - auto message = warning_codes.find(code); - if (message != warning_codes.end()) { - auto* warning = api.mutable_info()->mutable_warnings()->Add(); - warning->set_description(message->second); - warning->set_code(message->first); +void add_warning(valhalla::Api& api, unsigned code, const std::string& extra) { + auto warning = warning_codes.find(code); + if (warning != warning_codes.end()) { + auto* warning_pbf = api.mutable_info()->mutable_warnings()->Add(); + warning_pbf->set_description(warning->second + extra); + warning_pbf->set_code(warning->first); } } @@ -1256,7 +1327,7 @@ void ParseApi(const std::string& request, Options::Action action, valhalla::Api& from_json(document, action, api); } -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES void ParseApi(const http_request_t& request, valhalla::Api& api) { // block all but get and post if (request.method != method_t::POST && request.method != method_t::GET) { diff --git a/taginfo.json b/taginfo.json index 338769fa37..8d91ce36bc 100644 --- a/taginfo.json +++ b/taginfo.json @@ -8,7 +8,7 @@ "doc_url": "", "icon_url": "https://avatars0.githubusercontent.com/u/10232331", "contact_name": "Greg Knisely", - "contact_email": "greg.knisely@mapbox.com" + "contact_email": "greg@hammerhead.io" }, "tags": [ { @@ -242,7 +242,7 @@ "object_types": [ "relation" ], - "description": "Special cases of state/provinces in eg France and the Phillipines" + "description": "Special cases of state/provinces in eg France and the Philippines" }, { "key": "admin_level", @@ -267,12 +267,124 @@ ], "description": "Alternative name for the way, frequently used when a street has another official or locally preferred name" }, + { + "key": "alt_name:", + "object_types": [ + "way" + ], + "description": "Alternative name in a given language for the way, frequently used when a street has another official or locally preferred name" + }, { "key": "alt_name:pronunciation", "object_types": [ "way" ], - "description": "This tag contains a phonetic guide to pronouncing the alternative name for the way, frequently used when a street has another official or locally preferred name" + "description": "This tag contains a phonetic guide(IPA) to pronouncing the alternative name for the way, frequently used when a street has another official or locally preferred name" + }, + { + "key": "alt_name:pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the alternative name for the way, frequently used when a street has another official or locally preferred name. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "alt_name:pronunciation", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the alternative name in a given language for the way, frequently used when a street has another official or locally preferred name" + }, + { + "key": "alt_name:pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the alternative name in a given language for the way, frequently used when a street has another official or locally preferred name. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "alt_name:left", + "object_types": [ + "way" + ], + "description": "Alternative name for the way for the left side of the way, frequently used when a street has another official or locally preferred name" + }, + { + "key": "alt_name:left:", + "object_types": [ + "way" + ], + "description": "Alternative name in a given language for the left side of the way, frequently used when a street has another official or locally preferred name" + }, + { + "key": "alt_name:left:pronunciation", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the alternative name for the left side of the way, frequently used when a street has another official or locally preferred name" + }, + { + "key": "alt_name:left:pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the alternative name for the left side of the way, frequently used when a street has another official or locally preferred name. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "alt_name:left:pronunciation", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the alternative name in a given language for the left side of the way, frequently used when a street has another official or locally preferred name" + }, + { + "key": "alt_name:left:pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the alternative name in a given language for the left side of the way, frequently used when a street has another official or locally preferred name. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "alt_name:right", + "object_types": [ + "way" + ], + "description": "Alternative name for the way for the right side of the way, frequently used when a street has another official or locally preferred name" + }, + { + "key": "alt_name:right:", + "object_types": [ + "way" + ], + "description": "Alternative name in a given language for the right side of the way, frequently used when a street has another official or locally preferred name" + }, + { + "key": "alt_name:right:pronunciation", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the alternative name for the right side of the way, frequently used when a street has another official or locally preferred name" + }, + { + "key": "alt_name:right:pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the alternative name for the right side of the way, frequently used when a street has another official or locally preferred name. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "alt_name:right:pronunciation", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the alternative name in a given language for the right side of the way, frequently used when a street has another official or locally preferred name" + }, + { + "key": "alt_name:right:pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the alternative name in a given language for the right side of the way, frequently used when a street has another official or locally preferred name. Phonetic Alphabet: jeita|katakana|nt-sampa." }, { "key": "area", @@ -597,7 +709,7 @@ "description": "Sets the bike reverse flag to true. Note from wiki: (when cyclists are allowed to travel in both directions on a oneway street (but no lane is present))" }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "no @ (opening_hours format)", "key": "bicycle:conditional", "object_types": [ @@ -605,7 +717,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "yes @ (opening_hours format)", "key": "bicycle:conditional", "object_types": [ @@ -613,7 +725,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "private @ (opening_hours format)", "key": "bicycle:conditional", "object_types": [ @@ -621,7 +733,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "delivery @ (opening_hours format)", "key": "bicycle:conditional", "object_types": [ @@ -629,7 +741,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "designated @ (opening_hours format)", "key": "bicycle:conditional", "object_types": [ @@ -915,7 +1027,7 @@ "description": "Sets the bus reverse flag to true." }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "no @ (opening_hours format)", "key": "bus:conditional", "object_types": [ @@ -923,7 +1035,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "yes @ (opening_hours format)", "key": "bus:conditional", "object_types": [ @@ -931,7 +1043,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "private @ (opening_hours format)", "key": "bus:conditional", "object_types": [ @@ -939,7 +1051,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "delivery @ (opening_hours format)", "key": "bus:conditional", "object_types": [ @@ -947,7 +1059,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "designated @ (opening_hours format)", "key": "bus:conditional", "object_types": [ @@ -955,7 +1067,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "destination @ (opening_hours format)", "key": "bus:conditional", "object_types": [ @@ -1016,6 +1128,13 @@ ], "description": "Allows access for all modes unless the mode override is set." }, + { + "key": "crossing", + "value": "traffic_signals", + "object_types": [ + "node" + ] + }, { "key": "cyclestreet", "value": "yes", @@ -1581,9 +1700,37 @@ "way" ] }, + { + "key": "destination:lang:", + "description": "Toward location in a given language. Used for exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "object_types": [ + "way" + ] + }, { "key": "destination:pronunciation", - "description": "This tag contains a phonetic guide to pronouncing the toward location. Used for exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "description": "This tag contains a phonetic guide(IPA) to pronouncing the toward location. Used for exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "object_types": [ + "way" + ] + }, + { + "key": "destination:lang::pronunciation", + "description": "This tag contains a phonetic guide(IPA) to pronouncing the toward location in a given language. Used for exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "object_types": [ + "way" + ] + }, + { + "key": "destination:pronunciation:", + "description": "This tag contains a phonetic guide to pronouncing the toward location. Phonetic Alphabet: jeita|katakana|nt-sampa. Used for exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "object_types": [ + "way" + ] + }, + { + "key": "destination:lang::pronunciation:", + "description": "This tag contains a phonetic guide to pronouncing the toward location in a given language. Phonetic Alphabet: jeita|katakana|nt-sampa. Used for exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", "object_types": [ "way" ] @@ -1596,12 +1743,40 @@ ] }, { - "description": "This tag contains a phonetic guide to pronouncing the sinage information to the given edge in the graph which is helpful to know to where a given road leads. Forward and backward allow for distinct values based on the direction the way is drawn specifically useful for single carriageways.", + "description": "This tag contains a phonetic guide(IPA) to pronouncing the sinage information to the given edge in the graph which is helpful to know to where a given road leads. Forward and backward allow for distinct values based on the direction the way is drawn specifically useful for single carriageways.", "key": "destination:backward:pronunciation", "object_types": [ "way" ] }, + { + "description": "This tag contains a phonetic guide to pronouncing the sinage information to the given edge in the graph which is helpful to know to where a given road leads. Phonetic Alphabet: jeita|katakana|nt-sampa. Forward and backward allow for distinct values based on the direction the way is drawn specifically useful for single carriageways.", + "key": "destination:backward:pronunciation:", + "object_types": [ + "way" + ] + }, + { + "description": "Adds sinage information in a given language to the given edge in the graph which is helpful to know to where a given road leads. Forward and backward allow for distinct values based on the direction the way is drawn specifically useful for single carriageways.", + "key": "destination:backward:lang:", + "object_types": [ + "way" + ] + }, + { + "description": "This tag contains a phonetic guide(IPA) in a given language to pronouncing the sinage information to the given edge in the graph which is helpful to know to where a given road leads. Forward and backward allow for distinct values based on the direction the way is drawn specifically useful for single carriageways.", + "key": "destination:backward:lang::pronunciation", + "object_types": [ + "way" + ] + }, + { + "description": "This tag contains a phonetic guide in a given language to pronouncing the sinage information to the given edge in the graph which is helpful to know to where a given road leads. Phonetic Alphabet: jeita|katakana|nt-sampa. Forward and backward allow for distinct values based on the direction the way is drawn specifically useful for single carriageways.", + "key": "destination:backward:lang::pronunciation:", + "object_types": [ + "way" + ] + }, { "description": "Adds sinage information to the given edge in the graph which is helpful to know to where a given road leads. Forward and backward allow for distinct values based on the direction the way is drawn specifically useful for single carriageways.", "key": "destination:forward", @@ -1610,12 +1785,40 @@ ] }, { - "description": "This tag contains a phonetic guide to pronouncing the sinage information to the given edge in the graph which is helpful to know to where a given road leads. Forward and backward allow for distinct values based on the direction the way is drawn specifically useful for single carriageways.", + "description": "This tag contains a phonetic guide(IPA) to pronouncing the sinage information to the given edge in the graph which is helpful to know to where a given road leads. Forward and backward allow for distinct values based on the direction the way is drawn specifically useful for single carriageways.", "key": "destination:forward:pronunciation", "object_types": [ "way" ] }, + { + "description": "This tag contains a phonetic guide to pronouncing the sinage information to the given edge in the graph which is helpful to know to where a given road leads. Phonetic Alphabet: jeita|katakana|nt-sampa. Forward and backward allow for distinct values based on the direction the way is drawn specifically useful for single carriageways.", + "key": "destination:forward:pronunciation:", + "object_types": [ + "way" + ] + }, + { + "description": "Adds sinage information in a given language to the given edge in the graph which is helpful to know to where a given road leads. Forward and backward allow for distinct values based on the direction the way is drawn specifically useful for single carriageways.", + "key": "destination:forward:lang:", + "object_types": [ + "way" + ] + }, + { + "description": "This tag contains a phonetic guide(IPA) in a given language to pronouncing the sinage information to the given edge in the graph which is helpful to know to where a given road leads. Forward and backward allow for distinct values based on the direction the way is drawn specifically useful for single carriageways.", + "key": "destination:forward:lang::pronunciation", + "object_types": [ + "way" + ] + }, + { + "description": "This tag contains a phonetic guide in a given language to pronouncing the sinage information to the given edge in the graph which is helpful to know to where a given road leads. Phonetic Alphabet: jeita|katakana|nt-sampa. Forward and backward allow for distinct values based on the direction the way is drawn specifically useful for single carriageways.", + "key": "destination:forward:lang::pronunciation:", + "object_types": [ + "way" + ] + }, { "key": "destination:ref", "description": "Branch route number for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", @@ -1625,7 +1828,35 @@ }, { "key": "destination:ref:pronunciation", - "description": "This tag contains a phonetic guide to pronouncing the branch route number for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "description": "This tag contains a phonetic guide(IPA) to pronouncing the branch route number for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "object_types": [ + "way" + ] + }, + { + "key": "destination:ref:pronunciation:", + "description": "This tag contains a phonetic guide to pronouncing the branch route number for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info Phonetic Alphabet: jeita|katakana|nt-sampa.", + "object_types": [ + "way" + ] + }, + { + "key": "destination:ref:lang:", + "description": "Branch route number in a given language for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "object_types": [ + "way" + ] + }, + { + "key": "destination:ref:lang::pronunciation", + "description": "This tag contains a phonetic guide(IPA) to pronouncing the branch route number in a given language for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "object_types": [ + "way" + ] + }, + { + "key": "destination:ref:lang::pronunciation:", + "description": "This tag contains a phonetic guide to pronouncing the branch route number in a given language for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info Phonetic Alphabet: jeita|katakana|nt-sampa.", "object_types": [ "way" ] @@ -1639,7 +1870,35 @@ }, { "key": "destination:ref:to:pronunciation", - "description": "This tag contains a phonetic guide to pronouncing the toward route number for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "description": "This tag contains a phonetic guide(IPA) to pronouncing the toward route number for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "object_types": [ + "way" + ] + }, + { + "key": "destination:ref:to:pronunciation:", + "description": "This tag contains a phonetic guide to pronouncing the toward route number for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info Phonetic Alphabet: jeita|katakana|nt-sampa.", + "object_types": [ + "way" + ] + }, + { + "key": "destination:ref:to:lang:", + "description": "Toward route number in a given language for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "object_types": [ + "way" + ] + }, + { + "key": "destination:ref:to:lang::pronunciation", + "description": "This tag contains a phonetic guide(IPA) to pronouncing the toward route number in a given language for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "object_types": [ + "way" + ] + }, + { + "key": "destination:ref:to:lang::pronunciation:", + "description": "This tag contains a phonetic guide to pronouncing the toward route number in a given language for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info Phonetic Alphabet: jeita|katakana|nt-sampa.", "object_types": [ "way" ] @@ -1653,7 +1912,35 @@ }, { "key": "destination:street:pronunciation", - "description": "This tag contains a phonetic guide to pronouncing the branch road name for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "description": "This tag contains a phonetic guide(IPA) to pronouncing the branch road name for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "object_types": [ + "way" + ] + }, + { + "key": "destination:street:pronunciation:", + "description": "This tag contains a phonetic guide to pronouncing the branch road name for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info Phonetic Alphabet: jeita|katakana|nt-sampa.", + "object_types": [ + "way" + ] + }, + { + "key": "destination:street:lang:", + "description": "Branch road name in a given language for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "object_types": [ + "way" + ] + }, + { + "key": "destination:street:lang::pronunciation", + "description": "This tag contains a phonetic guide(IPA) to pronouncing the branch road name in a given language for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "object_types": [ + "way" + ] + }, + { + "key": "destination:street:lang::pronunciation:", + "description": "This tag contains a phonetic guide to pronouncing the branch road name in a given language for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info Phonetic Alphabet: jeita|katakana|nt-sampa.", "object_types": [ "way" ] @@ -1667,7 +1954,35 @@ }, { "key": "destination:street:to:pronunciation", - "description": "This tag contains a phonetic guide to pronouncing the toward road name for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "description": "This tag contains a phonetic guide(IPA) to pronouncing the toward road name for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "object_types": [ + "way" + ] + }, + { + "key": "destination:street:to:pronunciation:", + "description": "This tag contains a phonetic guide to pronouncing the toward road name for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info Phonetic Alphabet: jeita|katakana|nt-sampa.", + "object_types": [ + "way" + ] + }, + { + "key": "destination:street:to:lang:", + "description": "Toward road name in a given language for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "object_types": [ + "way" + ] + }, + { + "key": "destination:street:to:lang::pronunciation", + "description": "This tag contains a phonetic guide(IPA) to pronouncing the toward road name in a given language for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "object_types": [ + "way" + ] + }, + { + "key": "destination:street:to:lang::pronunciation:", + "description": "This tag contains a phonetic guide to pronouncing the toward road name in a given language for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info Phonetic Alphabet: jeita|katakana|nt-sampa.", "object_types": [ "way" ] @@ -1682,7 +1997,7 @@ "description": "Overrides access flags for emergency vehicles. Emergency Routing is allowed." }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "no @ (opening_hours format)", "key": "emergency:conditional", "object_types": [ @@ -1690,7 +2005,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "yes @ (opening_hours format)", "key": "emergency:conditional", "object_types": [ @@ -1698,7 +2013,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "private @ (opening_hours format)", "key": "emergency:conditional", "object_types": [ @@ -1706,7 +2021,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "delivery @ (opening_hours format)", "key": "emergency:conditional", "object_types": [ @@ -1714,7 +2029,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "designated @ (opening_hours format)", "key": "emergency:conditional", "object_types": [ @@ -1722,7 +2037,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "destination @ (opening_hours format)", "key": "emergency:conditional", "object_types": [ @@ -1991,7 +2306,7 @@ "description": "Sets the foot reverse flag to true." }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "no @ (opening_hours format)", "key": "foot:conditional", "object_types": [ @@ -1999,7 +2314,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "yes @ (opening_hours format)", "key": "foot:conditional", "object_types": [ @@ -2007,7 +2322,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "private @ (opening_hours format)", "key": "foot:conditional", "object_types": [ @@ -2015,7 +2330,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "delivery @ (opening_hours format)", "key": "foot:conditional", "object_types": [ @@ -2023,7 +2338,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "designated @ (opening_hours format)", "key": "foot:conditional", "object_types": [ @@ -2031,7 +2346,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "destination @ (opening_hours format)", "key": "foot:conditional", "object_types": [ @@ -2481,7 +2796,7 @@ "description": "Overrides access flags for hgv. Hgv Routing is not allowed." }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "no @ (opening_hours format)", "key": "hgv:conditional", "object_types": [ @@ -2489,7 +2804,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "yes @ (opening_hours format)", "key": "hgv:conditional", "object_types": [ @@ -2497,7 +2812,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "private @ (opening_hours format)", "key": "hgv:conditional", "object_types": [ @@ -2505,7 +2820,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "delivery @ (opening_hours format)", "key": "hgv:conditional", "object_types": [ @@ -2513,7 +2828,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "designated @ (opening_hours format)", "key": "hgv:conditional", "object_types": [ @@ -2521,7 +2836,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "destination @ (opening_hours format)", "key": "hgv:conditional", "object_types": [ @@ -3017,7 +3332,7 @@ "description": "Allows access for hov modes unless the value is no." }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "no @ (opening_hours format)", "key": "hov:conditional", "object_types": [ @@ -3025,7 +3340,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "yes @ (opening_hours format)", "key": "hov:conditional", "object_types": [ @@ -3033,7 +3348,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "private @ (opening_hours format)", "key": "hov:conditional", "object_types": [ @@ -3041,7 +3356,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "delivery @ (opening_hours format)", "key": "hov:conditional", "object_types": [ @@ -3049,7 +3364,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "designated @ (opening_hours format)", "key": "hov:conditional", "object_types": [ @@ -3057,7 +3372,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "destination @ (opening_hours format)", "key": "hov:conditional", "object_types": [ @@ -3116,7 +3431,35 @@ "object_types": [ "way" ], - "description": "This tag contains a phonetic guide to pronouncing the international route network and number." + "description": "This tag contains a phonetic guide(IPA) to pronouncing the international route network and number." + }, + { + "key": "int_ref:pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the international route network and number. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "int_ref:", + "object_types": [ + "way" + ], + "description": "International route network and number in a given language." + }, + { + "key": "int_ref::pronunciation", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the international route network and number in a given language." + }, + { + "key": "int_ref::pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the international route network and number in a given language. Phonetic Alphabet: jeita|katakana|nt-sampa." }, { "key": "junction", @@ -3136,39 +3479,109 @@ }, { "key": "junction:ref", - "description": "Junction/exit number for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "description": "Junction/exit number for signage (i.e. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info)", "object_types": [ "way" ] }, { "key": "junction:ref:pronunciation", - "description": "This tag contains a phonetic guide to pronouncing the junction/exit number for sinage ie. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info", + "description": "This tag contains a phonetic guide(IPA) to pronouncing the junction/exit number for signage (i.e. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info)", "object_types": [ "way" ] }, { - "key": "lanes", - "description": "Lane count", + "key": "junction:ref:pronunciation:", + "description": "This tag contains a phonetic guide to pronouncing the junction/exit number for signage (i.e. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info) Phonetic Alphabet: jeita|katakana|nt-sampa.", "object_types": [ "way" ] }, { - "key": "lanes:backward", - "description": "Lane count in the backward direction", + "key": "junction:ref:", + "description": "Junction/exit number for signage in a given language (i.e. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info)", "object_types": [ "way" ] }, { - "key": "lanes:bus", - "value": "1", + "key": "junction:ref::pronunciation", + "description": "This tag contains a phonetic guide(IPA) to pronouncing the junction/exit number for signage in a given language (i.e. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info)", "object_types": [ "way" - ], - "description": "Bus forward override. Sets bus forward to true." + ] + }, + { + "key": "junction:ref::pronunciation:", + "description": "This tag contains a phonetic guide to pronouncing the junction/exit number for signage in a given language (i.e. exit information, see: https://wiki.openstreetmap.org/wiki/Exit_Info) Phonetic Alphabet: jeita|katakana|nt-sampa.", + "object_types": [ + "way" + ] + }, + { + "key": "junction:name", + "description": "Junction name", + "object_types": [ + "way" + ] + }, + { + "key": "junction:name:pronunciation", + "description": "This tag contains a phonetic guide(IPA) to pronouncing the junction name", + "object_types": [ + "way" + ] + }, + { + "key": "junction:name:pronunciation:", + "description": "This tag contains a phonetic guide to pronouncing the junction name. Phonetic Alphabet: jeita|katakana|nt-sampa.", + "object_types": [ + "way" + ] + }, + { + "key": "junction:name:", + "description": "Junction name in a given language", + "object_types": [ + "way" + ] + }, + { + "key": "junction:name::pronunciation", + "description": "This tag contains a phonetic guide(IPA) to pronouncing the junction name in a given language", + "object_types": [ + "way" + ] + }, + { + "key": "junction:name::pronunciation:", + "description": "This tag contains a phonetic guide to pronouncing the junction name in a given language. Phonetic Alphabet: jeita|katakana|nt-sampa.", + "object_types": [ + "way" + ] + }, + { + "key": "lanes", + "description": "Lane count", + "object_types": [ + "way" + ] + }, + { + "key": "lanes:backward", + "description": "Lane count in the backward direction", + "object_types": [ + "way" + ] + }, + { + "key": "lanes:bus", + "value": "1", + "object_types": [ + "way" + ], + "description": "Bus forward override. Sets bus forward to true." }, { "key": "lanes:bus", @@ -3382,6 +3795,20 @@ ], "description": "Maxheight for hgv." }, + { + "key": "maxheight:forward", + "object_types": [ + "way" + ], + "description": "Maxheight for hgv in forward direction." + }, + { + "key": "maxheight:backward", + "object_types": [ + "way" + ], + "description": "Maxheight for hgv in backward direction." + }, { "key": "maxheight:physical", "object_types": [ @@ -3396,6 +3823,20 @@ ], "description": "Maxlength for hgv." }, + { + "key": "maxlength:forward", + "object_types": [ + "way" + ], + "description": "Maxlength for hgv in forward direction." + }, + { + "key": "maxlength:backward", + "object_types": [ + "way" + ], + "description": "Maxlength for hgv in backward direction." + }, { "key": "maxspeed", "description": "Speed Limit (assume kph except where mph given)", @@ -3431,6 +3872,20 @@ ], "description": "Maxspeed for hgv." }, + { + "key": "maxspeed:hgv:forward", + "object_types": [ + "way" + ], + "description": "Maxspeed for hgv in forward direction." + }, + { + "key": "maxspeed:hgv:backward", + "object_types": [ + "way" + ], + "description": "Maxspeed for hgv in backward direction." + }, { "key": "maxspeed:practical", "description": "Considered the average speed on a road (assume kph except where mph given)", @@ -3445,6 +3900,20 @@ ], "description": "Maxweight for hgv." }, + { + "key": "maxweight:forward", + "object_types": [ + "way" + ], + "description": "Maxweight for hgv in forward direction." + }, + { + "key": "maxweight:backward", + "object_types": [ + "way" + ], + "description": "Maxweight for hgv in backward direction." + }, { "key": "maxwidth", "object_types": [ @@ -3452,6 +3921,20 @@ ], "description": "Maxwidth for hgv." }, + { + "key": "maxwidth:forward", + "object_types": [ + "way" + ], + "description": "Maxwidth for hgv in forward direction." + }, + { + "key": "maxwidth:backward", + "object_types": [ + "way" + ], + "description": "Maxwidth for hgv in backward direction." + }, { "key": "maxwidth:physical", "object_types": [ @@ -3558,7 +4041,7 @@ "description": "Sets the motor scooter reverse flag to true." }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "no @ (opening_hours format)", "key": "mofa:conditional", "object_types": [ @@ -3566,7 +4049,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "yes @ (opening_hours format)", "key": "mofa:conditional", "object_types": [ @@ -3574,7 +4057,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "private @ (opening_hours format)", "key": "mofa:conditional", "object_types": [ @@ -3582,7 +4065,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "delivery @ (opening_hours format)", "key": "mofa:conditional", "object_types": [ @@ -3590,7 +4073,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "designated @ (opening_hours format)", "key": "mofa:conditional", "object_types": [ @@ -3598,7 +4081,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "destination @ (opening_hours format)", "key": "mofa:conditional", "object_types": [ @@ -3722,7 +4205,7 @@ "description": "Sets the motor scooter reverse flag to true." }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "no @ (opening_hours format)", "key": "moped:conditional", "object_types": [ @@ -3730,7 +4213,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "yes @ (opening_hours format)", "key": "moped:conditional", "object_types": [ @@ -3738,7 +4221,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "private @ (opening_hours format)", "key": "moped:conditional", "object_types": [ @@ -3746,7 +4229,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "delivery @ (opening_hours format)", "key": "moped:conditional", "object_types": [ @@ -3754,7 +4237,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "designated @ (opening_hours format)", "key": "moped:conditional", "object_types": [ @@ -3762,7 +4245,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "destination @ (opening_hours format)", "key": "moped:conditional", "object_types": [ @@ -3941,7 +4424,7 @@ "description": "Overrides access flags for auto. Auto Routing allowed." }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "no @ (opening_hours format)", "key": "motor_vehicle:conditional", "object_types": [ @@ -3949,7 +4432,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "yes @ (opening_hours format)", "key": "motor_vehicle:conditional", "object_types": [ @@ -3957,7 +4440,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "private @ (opening_hours format)", "key": "motor_vehicle:conditional", "object_types": [ @@ -3965,7 +4448,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "delivery @ (opening_hours format)", "key": "motor_vehicle:conditional", "object_types": [ @@ -3973,7 +4456,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "designated @ (opening_hours format)", "key": "motor_vehicle:conditional", "object_types": [ @@ -3981,7 +4464,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "destination @ (opening_hours format)", "key": "motor_vehicle:conditional", "object_types": [ @@ -4160,7 +4643,7 @@ "description": "Overrides access flags for auto. Auto Routing allowed." }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "no @ (opening_hours format)", "key": "motorcar:conditional", "object_types": [ @@ -4168,7 +4651,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "yes @ (opening_hours format)", "key": "motorcar:conditional", "object_types": [ @@ -4176,7 +4659,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "private @ (opening_hours format)", "key": "motorcar:conditional", "object_types": [ @@ -4184,7 +4667,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "delivery @ (opening_hours format)", "key": "motorcar:conditional", "object_types": [ @@ -4192,7 +4675,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "designated @ (opening_hours format)", "key": "motorcar:conditional", "object_types": [ @@ -4200,7 +4683,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "destination @ (opening_hours format)", "key": "motorcar:conditional", "object_types": [ @@ -4208,7 +4691,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "no @ (opening_hours format)", "key": "motorcycle:conditional", "object_types": [ @@ -4216,7 +4699,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "yes @ (opening_hours format)", "key": "motorcycle:conditional", "object_types": [ @@ -4224,7 +4707,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "private @ (opening_hours format)", "key": "motorcycle:conditional", "object_types": [ @@ -4232,7 +4715,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "delivery @ (opening_hours format)", "key": "motorcycle:conditional", "object_types": [ @@ -4240,7 +4723,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "designated @ (opening_hours format)", "key": "motorcycle:conditional", "object_types": [ @@ -4248,7 +4731,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "destination @ (opening_hours format)", "key": "motorcycle:conditional", "object_types": [ @@ -4400,99 +4883,425 @@ ] }, { - "description": "Sets the hiking difficulty for cycle and pedestrian passes", - "value": "difficult_alpine_hiking", - "key": "mtb:scale:uphill", + "description": "Sets the hiking difficulty for cycle and pedestrian passes", + "value": "difficult_alpine_hiking", + "key": "mtb:scale:uphill", + "object_types": [ + "way" + ] + }, + { + "description": "Sets the hiking difficulty for cycle and pedestrian passes", + "value": "demanding_alpine_hiking", + "key": "mtb:scale:uphill", + "object_types": [ + "way" + ] + }, + { + "description": "Sets the hiking difficulty for cycle and pedestrian passes", + "value": "alpine_hiking", + "key": "mtb:scale:uphill", + "object_types": [ + "way" + ] + }, + { + "description": "Sets the hiking difficulty for cycle and pedestrian passes", + "value": "demanding_mountain_hiking", + "key": "mtb:scale:uphill", + "object_types": [ + "way" + ] + }, + { + "description": "Sets the hiking difficulty for cycle and pedestrian passes", + "value": "mountain_hiking", + "key": "mtb:scale:uphill", + "object_types": [ + "way" + ] + }, + { + "description": "Sets the hiking difficulty for cycle and pedestrian passes", + "value": "hiking", + "key": "mtb:scale:uphill", + "object_types": [ + "way" + ] + }, + { + "key": "name", + "object_types": [ + "node", + "way", + "relation" + ], + "description": "The name of the feature, street names, river names, POI names etc." + }, + { + "key": "name:pronunciation", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the name of the feature, street names, river names, POI names etc." + }, + { + "key": "name:pronunciation:", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the name of the feature, street names, river names, POI names etc. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "name:", + "object_types": [ + "way", + "relation" + ], + "description": "The name of the feature in a given language" + }, + { + "key": "name::pronunciation", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the name of the feature in a given language" + }, + { + "key": "name::pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the name of the feature in a given language. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "name:left", + "object_types": [ + "node", + "way", + "relation" + ], + "description": "The name of the feature, street names, river names, POI names etc. for the left side" + }, + { + "key": "name:left:pronunciation", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the name of the feature, street names, river names, POI names etc. for the left side" + }, + { + "key": "name:left:pronunciation:", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the name of the feature, street names, river names, POI names etc. for the left side. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "name:left:", + "object_types": [ + "way", + "relation" + ], + "description": "The name of the feature in a given language for the left side" + }, + { + "key": "name:left::pronunciation", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the name of the feature in a given language for the left side" + }, + { + "key": "name:left::pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the name of the feature in a given language for the left side. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "name:right", + "object_types": [ + "node", + "way", + "relation" + ], + "description": "The name of the feature, street names, river names, POI names etc. for the right side" + }, + { + "key": "name:right:pronunciation", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the name of the feature, street names, river names, POI names etc. for the right side" + }, + { + "key": "name:right:pronunciation:", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the name of the feature, street names, river names, POI names etc. for the right side. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "name:right:", + "object_types": [ + "way", + "relation" + ], + "description": "The name of the feature in a given language for the right side" + }, + { + "key": "name:right::pronunciation", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the name of the feature in a given language for the right side" + }, + { + "key": "name:right::pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the name of the feature in a given language for the right side. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "name:forward", + "object_types": [ + "node", + "way", + "relation" + ], + "description": "The name of the feature, street names, river names, POI names etc. for the forward direction" + }, + { + "key": "name:forward:pronunciation", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the name of the feature, street names, river names, POI names etc. for the forward direction" + }, + { + "key": "name:forward:pronunciation:", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the name of the feature, street names, river names, POI names etc. for the forward direction. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "name:forward:", + "object_types": [ + "way", + "relation" + ], + "description": "The name of the feature in a given language for the forward direction" + }, + { + "key": "name:forward::pronunciation", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the name of the feature in a given language for the forward direction" + }, + { + "key": "name:forward::pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the name of the feature in a given language for the forward direction. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "name:backward", + "object_types": [ + "node", + "way", + "relation" + ], + "description": "The name of the feature, street names, river names, POI names etc. for the backward direction" + }, + { + "key": "name:backward:pronunciation", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the name of the feature, street names, river names, POI names etc. for the backward direction" + }, + { + "key": "name:backward:pronunciation:", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the name of the feature, street names, river names, POI names etc. for the backward direction. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "name:backward:", + "object_types": [ + "way", + "relation" + ], + "description": "The name of the feature in a given language for the backward direction" + }, + { + "key": "name:backward::pronunciation", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the name of the feature in a given language for the backward direction" + }, + { + "key": "name:backward::pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the name of the feature in a given language for the backward direction. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "tunnel:name", + "object_types": [ + "way" + ], + "description": "The tunnel name of the way." + }, + { + "key": "tunnel:name:pronunciation", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the tunnel name of the way." + }, + { + "key": "tunnel:name:pronunciation:", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the tunnel name of the way. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "tunnel:name:", + "object_types": [ + "way" + ], + "description": "The tunnel name in a given language of the way." + }, + { + "key": "tunnel:name::pronunciation", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the tunnel name in a given language of the way." + }, + { + "key": "tunnel:name::pronunciation:", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the tunnel name in a given language of the way. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "tunnel:name:left", "object_types": [ "way" - ] + ], + "description": "The tunnel name of the way for the left side." }, { - "description": "Sets the hiking difficulty for cycle and pedestrian passes", - "value": "demanding_alpine_hiking", - "key": "mtb:scale:uphill", + "key": "tunnel:name:left:pronunciation", "object_types": [ + "node", "way" - ] + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the tunnel name of the way for the left side." }, { - "description": "Sets the hiking difficulty for cycle and pedestrian passes", - "value": "alpine_hiking", - "key": "mtb:scale:uphill", + "key": "tunnel:name:left:pronunciation:", "object_types": [ + "node", "way" - ] + ], + "description": "This tag contains a phonetic guide to pronouncing the tunnel name of the way for the left side. Phonetic Alphabet: jeita|katakana|nt-sampa." }, { - "description": "Sets the hiking difficulty for cycle and pedestrian passes", - "value": "demanding_mountain_hiking", - "key": "mtb:scale:uphill", + "key": "tunnel:name:left:", "object_types": [ "way" - ] + ], + "description": "The tunnel name in a given language of the way for the left side." }, { - "description": "Sets the hiking difficulty for cycle and pedestrian passes", - "value": "mountain_hiking", - "key": "mtb:scale:uphill", + "key": "tunnel:name:left::pronunciation", "object_types": [ + "node", "way" - ] + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the tunnel name in a given language of the way for the left side." }, { - "description": "Sets the hiking difficulty for cycle and pedestrian passes", - "value": "hiking", - "key": "mtb:scale:uphill", + "key": "tunnel:name:left::pronunciation:", "object_types": [ + "node", "way" - ] + ], + "description": "This tag contains a phonetic guide to pronouncing the tunnel name in a given language of the way for the left side. Phonetic Alphabet: jeita|katakana|nt-sampa." }, { - "key": "name", + "key": "tunnel:name:right", "object_types": [ - "node", - "way", - "relation" + "way" ], - "description": "The name of the feature, street names, river names, POI names etc." + "description": "The tunnel name of the way for the right side." }, { - "key": "name:pronunciation", + "key": "tunnel:name:right:pronunciation", "object_types": [ "node", "way" ], - "description": "This tag contains a phonetic guide to pronouncing the name of the feature, street names, river names, POI names etc." + "description": "This tag contains a phonetic guide(IPA) to pronouncing the tunnel name of the way for the right side." }, { - "key": "tunnel:name", + "key": "tunnel:name:right:pronunciation:", "object_types": [ + "node", "way" ], - "description": "The tunnel name of the way." + "description": "This tag contains a phonetic guide to pronouncing the tunnel name of the way for the right side. Phonetic Alphabet: jeita|katakana|nt-sampa." }, { - "key": "tunnel:name:pronunciation", + "key": "tunnel:name:right:", "object_types": [ - "node", "way" ], - "description": "This tag contains a phonetic guide to pronouncing the tunnel name of the way." + "description": "The tunnel name in a given language of the way for the right side." }, { - "key": "name:en", + "key": "tunnel:name:right::pronunciation", "object_types": [ - "way", - "relation" + "node", + "way" ], - "description": "The english name of the feature" + "description": "This tag contains a phonetic guide(IPA) to pronouncing the tunnel name in a given language of the way for the right side." }, { - "key": "name:en:pronunciation", + "key": "tunnel:name:right::pronunciation:", "object_types": [ + "node", "way" ], - "description": "This tag contains a phonetic guide to pronouncing the english name of the feature" + "description": "This tag contains a phonetic guide to pronouncing the tunnel name in a given language of the way for the right side. Phonetic Alphabet: jeita|katakana|nt-sampa." }, { "key": "ncn", @@ -4552,14 +5361,126 @@ "object_types": [ "way" ], - "description": "Official name for a country used in that country if it differs from what is in the name tag" + "description": "Official name for a country or street" }, { "key": "official_name:pronunciation", "object_types": [ "way" ], - "description": "This tag contains a phonetic guide to pronouncing the official name for a country used in that country if it differs from what is in the name tag" + "description": "This tag contains a phonetic guide(IPA) to pronouncing the official name for a street" + }, + { + "key": "official_name:pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the official name for a street. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "official_name:", + "object_types": [ + "way" + ], + "description": "Official name in a given language for a street" + }, + { + "key": "official_name::pronunciation", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the official name in a given language for a street" + }, + { + "key": "official_name::pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the official name in a given language for a street. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "official_name:left", + "object_types": [ + "way" + ], + "description": "Official name for a street for the left side." + }, + { + "key": "official_name:left:pronunciation", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the official name a street for the left side." + }, + { + "key": "official_name:left:pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the official name a street for the left side. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "official_name:left:", + "object_types": [ + "way" + ], + "description": "Official name in a given language for a street for the left side." + }, + { + "key": "official_name:left::pronunciation", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the official name in a given language for a street for the left side." + }, + { + "key": "official_name:left::pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the official name in a given language for a street for the left side. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "official_name:right", + "object_types": [ + "way" + ], + "description": "Official name for a street for the right side." + }, + { + "key": "official_name:right:pronunciation", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the official name a street for the right side." + }, + { + "key": "official_name:right:pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the official name a street for the right side. Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "official_name:right:", + "object_types": [ + "way" + ], + "description": "Official name in a given language for a street for the right side." + }, + { + "key": "official_name:right::pronunciation", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the official name in a given language for a street for the right side." + }, + { + "key": "official_name:right::pronunciation:", + "object_types": [ + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the official name in a given language for a street for the right side. Phonetic Alphabet: jeita|katakana|nt-sampa." }, { "key": "access", @@ -5003,7 +5924,7 @@ "description": "Overrides access flags for pedestrian. Pedestrian Routing allowed." }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "no @ (opening_hours format)", "key": "pedestrian:conditional", "object_types": [ @@ -5011,7 +5932,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "yes @ (opening_hours format)", "key": "pedestrian:conditional", "object_types": [ @@ -5019,7 +5940,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "private @ (opening_hours format)", "key": "pedestrian:conditional", "object_types": [ @@ -5027,7 +5948,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "delivery @ (opening_hours format)", "key": "pedestrian:conditional", "object_types": [ @@ -5035,7 +5956,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "designated @ (opening_hours format)", "key": "pedestrian:conditional", "object_types": [ @@ -5043,7 +5964,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "destination @ (opening_hours format)", "key": "pedestrian:conditional", "object_types": [ @@ -5159,7 +6080,7 @@ "description": "Overrides access flags for bus. Bus Routing allowed." }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "no @ (opening_hours format)", "key": "psv:conditional", "object_types": [ @@ -5167,7 +6088,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "yes @ (opening_hours format)", "key": "psv:conditional", "object_types": [ @@ -5175,7 +6096,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "private @ (opening_hours format)", "key": "psv:conditional", "object_types": [ @@ -5183,7 +6104,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "delivery @ (opening_hours format)", "key": "psv:conditional", "object_types": [ @@ -5191,7 +6112,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "designated @ (opening_hours format)", "key": "psv:conditional", "object_types": [ @@ -5199,7 +6120,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "destination @ (opening_hours format)", "key": "psv:conditional", "object_types": [ @@ -5252,7 +6173,40 @@ "node", "way" ], - "description": "This tag contains a phonetic guide to pronouncing the junction/exit number, see: https://wiki.openstreetmap.org/wiki/Exit_Info" + "description": "This tag contains a phonetic guide(IPA) to pronouncing the junction/exit number, see: https://wiki.openstreetmap.org/wiki/Exit_Info" + }, + { + "key": "ref:pronunciation:", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide to pronouncing the junction/exit number, see: https://wiki.openstreetmap.org/wiki/Exit_Info Phonetic Alphabet: jeita|katakana|nt-sampa." + }, + { + "key": "ref:", + "object_types": [ + "node", + "way", + "relation" + ], + "description": "Route network and number. Junction/exit number in a given language, see: https://wiki.openstreetmap.org/wiki/Exit_Info" + }, + { + "key": "ref::pronunciation", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the junction/exit number in a given language, see: https://wiki.openstreetmap.org/wiki/Exit_Info" + }, + { + "key": "ref::pronunciation:", + "object_types": [ + "node", + "way" + ], + "description": "This tag contains a phonetic guide(IPA) to pronouncing the junction/exit number in a given language, see: https://wiki.openstreetmap.org/wiki/Exit_Info Phonetic Alphabet: jeita|katakana|nt-sampa." }, { "key": "restriction", @@ -6796,7 +7750,7 @@ "description": "Sets the taxi reverse flag to true." }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "no @ (opening_hours format)", "key": "taxi:conditional", "object_types": [ @@ -6804,7 +7758,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "yes @ (opening_hours format)", "key": "taxi:conditional", "object_types": [ @@ -6812,7 +7766,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "private @ (opening_hours format)", "key": "taxi:conditional", "object_types": [ @@ -6820,7 +7774,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "delivery @ (opening_hours format)", "key": "taxi:conditional", "object_types": [ @@ -6828,7 +7782,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "designated @ (opening_hours format)", "key": "taxi:conditional", "object_types": [ @@ -6836,7 +7790,7 @@ ] }, { - "description": "Adds conditional access to an edge with a time depedency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", + "description": "Adds conditional access to an edge with a time dependency in opening_hours format, see: https://wiki.openstreetmap.org/wiki/Key:opening_hours", "value": "destination @ (opening_hours format)", "key": "taxi:conditional", "object_types": [ @@ -7352,6 +8306,134 @@ "way" ], "description": "Considers the way lit" + }, + { + "key": "motor_vehicle:forward", + "value": "yes", + "object_types": [ + "way" + ], + "description": "Enables motor_vehicle access in the forward direction" + }, + { + "key": "motor_vehicle:forward", + "value": "no", + "object_types": [ + "way" + ], + "description": "Disables motor_vehicle access in the forward direction" + }, + { + "key": "motor_vehicle:backward", + "value": "yes", + "object_types": [ + "way" + ], + "description": "Enables motor_vehicle access in the backward direction" + }, + { + "key": "motor_vehicle:backward", + "value": "no", + "object_types": [ + "way" + ], + "description": "Disables motor_vehicle access in the backward direction" + }, + { + "key": "vehicle:forward", + "value": "yes", + "object_types": [ + "way" + ], + "description": "Enables vehicle access in the forward direction" + }, + { + "key": "vehicle:forward", + "value": "no", + "object_types": [ + "way" + ], + "description": "Disables vehicle access in the forward direction" + }, + { + "key": "vehicle:backward", + "value": "yes", + "object_types": [ + "way" + ], + "description": "Enables vehicle access in the backward direction" + }, + { + "key": "vehicle:backward", + "value": "no", + "object_types": [ + "way" + ], + "description": "Disables vehicle access in the backward direction" + }, + { + "key": "foot:forward", + "value": "yes", + "object_types": [ + "way" + ], + "description": "Enables foot access in the forward direction" + }, + { + "key": "foot:forward", + "value": "no", + "object_types": [ + "way" + ], + "description": "Disables foot access in the forward direction" + }, + { + "key": "foot:backward", + "value": "yes", + "object_types": [ + "way" + ], + "description": "Enables foot access in the backward direction" + }, + { + "key": "foot:backward", + "value": "no", + "object_types": [ + "way" + ], + "description": "Disables foot access in the backward direction" + }, + { + "key": "bicycle:forward", + "value": "yes", + "object_types": [ + "way" + ], + "description": "Enables bicycle access in the forward direction" + }, + { + "key": "bicycle:forward", + "value": "no", + "object_types": [ + "way" + ], + "description": "Disables bicycle access in the forward direction" + }, + { + "key": "bicycle:backward", + "value": "yes", + "object_types": [ + "way" + ], + "description": "Enables bicycle access in the backward direction" + }, + { + "key": "bicycle:backward", + "value": "no", + "object_types": [ + "way" + ], + "description": "Disables bicycle access in the backward direction" } ] } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bb4f2b03cc..de1b8c0cd0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(${VALHALLA_SOURCE_DIR}/third_party/googletest ${CMAKE_BINARY_DIR}/googletest) + set_target_properties(gtest PROPERTIES FOLDER "Dependencies") set_target_properties(gtest_main PROPERTIES FOLDER "Dependencies") set_target_properties(gmock PROPERTIES FOLDER "Dependencies") @@ -13,28 +14,25 @@ add_library(valhalla_test ${TEST_SRCS} ${VALHALLA_SOURCE_DIR}/third_party/microtar/src/microtar.h ${VALHALLA_SOURCE_DIR}/third_party/microtar/src/microtar.c - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib/lz4.c - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib/lz4hc.c - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib/lz4frame.c - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib/xxhash.c ) target_include_directories(valhalla_test PUBLIC ${VALHALLA_SOURCE_DIR}/test - ${VALHALLA_SOURCE_DIR}/test/gurka - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib - ${VALHALLA_SOURCE_DIR}/third_party/protozero/include + ${VALHALLA_SOURCE_DIR}/test/gurka) +target_include_directories(valhalla_test SYSTEM PUBLIC + ${robinhoodhashing_include_dir} ${VALHALLA_SOURCE_DIR}/third_party/just_gtfs/include + ${VALHALLA_SOURCE_DIR}/third_party/protozero/include ${VALHALLA_SOURCE_DIR}/third_party/libosmium/include ${VALHALLA_SOURCE_DIR}/third_party/microtar/src) -target_link_libraries(valhalla_test valhalla gtest gtest_main gmock pthread) +target_link_libraries(valhalla_test valhalla gtest gtest_main gmock pthread PkgConfig::LZ4) if(ENABLE_DATA_TOOLS) - target_link_libraries(valhalla_test GEOS::GEOS) + target_link_libraries(valhalla_test PkgConfig::GEOS) endif() ## Lists tests -set(tests aabb2 access_restriction actor admin attributes_controller datetime directededge +set(tests aabb2 access_restriction actor admin attributes_controller configuration datetime directededge distanceapproximator double_bucket_queue edgecollapser edgestatus ellipse encode enhancedtrippath factory graphid graphtile graphtileheader gridded_data grid_range_query grid_traversal instructions json laneconnectivity linesegment2 location logging maneuversbuilder map_matcher_factory mapmatch_config @@ -43,12 +41,12 @@ set(tests aabb2 access_restriction actor admin attributes_controller datetime di streetnames_us streetname_us tilehierarchy tiles transitdeparture transitroute transitschedule transitstop turn turnlanes util_midgard util_skadi vector2 verbal_text_formatter verbal_text_formatter_us verbal_text_formatter_us_co verbal_text_formatter_us_tx viterbi_search compression filesystem traffictile - incident_loading worker_nullptr_tiles tar_index curl_tilegetter) + incident_loading worker_nullptr_tiles curl_tilegetter) if(ENABLE_DATA_TOOLS) list(APPEND tests astar astar_bikeshare complexrestriction countryaccess edgeinfobuilder graphbuilder graphparser graphtilebuilder graphreader isochrone predictive_traffic idtable mapmatch matrix matrix_bss minbb multipoint_routes - names node_search reach recover_shortcut refs search servicedays shape_attributes signinfo summary urban + names node_search reach recover_shortcut refs search servicedays shape_attributes signinfo summary urban tar_index thor_worker timedep_paths timeparsing trivial_paths uniquenames util_mjolnir utrecht lua alternates) if(ENABLE_HTTP) list(APPEND tests http_tiles) @@ -66,19 +64,39 @@ if(NOT APPLE) list(APPEND tests narrativebuilder util_odin) endif() -if (UNIX AND ENABLE_SINGLE_FILES_WERROR) - # Enables stricter compiler checks on a file-by-file basis - # which allows us to migrate piecemeal - set_source_files_properties( - openlr.cc - incidents.cc - incident_loading.cc - PROPERTIES COMPILE_FLAGS "-Wall -Werror") -endif() +# tests files that have warning, +# any other file not int this list will be processed with the +# "-Werror" flag +# Avoid adding a test to this list +set(tests_with_warnings + filesystem + graphbuilder + graphtilebuilder + gridded_data + maneuversbuilder + narrativebuilder + parse_request + sample + sequence + statsd + traffictile + util_odin + timedep_paths + worker + ) ## Add executable targets foreach(test ${tests}) add_executable(${test} EXCLUDE_FROM_ALL ${test}.cc ) + if (UNIX AND ENABLE_SINGLE_FILES_WERROR) + if (${test} IN_LIST tests_with_warnings) + set_source_files_properties(${test}.cc PROPERTIES COMPILE_FLAGS "-Wall") + else() + set_source_files_properties(${test}.cc PROPERTIES COMPILE_FLAGS "-Wall -Werror") + endif() + endif() + + set_target_properties(${test} PROPERTIES FOLDER "Tests") target_compile_definitions(${test} PRIVATE VALHALLA_SOURCE_DIR="${VALHALLA_SOURCE_DIR}/" @@ -113,7 +131,7 @@ foreach(test ${tyr_tests}) endforeach() ## Test-specific data, properties and dependencies -set_target_properties(logging PROPERTIES COMPILE_DEFINITIONS LOGGING_LEVEL_ALL) +target_compile_definitions(logging PRIVATE LOGGING_LEVEL_ALL) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/test/data/tz.sqlite DEPENDS ${VALHALLA_SOURCE_DIR}/scripts/valhalla_build_timezones @@ -129,7 +147,7 @@ set_target_properties(build_timezones PROPERTIES FOLDER "Tests") add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/test/data/utrecht_tiles/traffic.tar COMMAND ${CMAKE_COMMAND} -E make_directory test/data/utrecht_tiles/ COMMAND ${CMAKE_BINARY_DIR}/valhalla_build_tiles - --inline-config '{"mjolnir":{"id_table_size":1000,"tile_dir":"test/data/utrecht_tiles","timezone":"test/data/tz.sqlite","admin":"${VALHALLA_SOURCE_DIR}/test/data/netherlands_admin.sqlite","include_construction":true,"hierarchy":true,"shortcuts":true,"concurrency":1,"logging":{"type":""}}}' + --inline-config '{"mjolnir":{"id_table_size":1000,"tile_dir":"test/data/utrecht_tiles","timezone":"test/data/tz.sqlite","admin":"${VALHALLA_SOURCE_DIR}/test/data/netherlands_admin.sqlite","include_construction":true,"hierarchy":true,"shortcuts":true,"concurrency":1,"logging":{"type":""},"data_processing":{"grid_divisions_within_tile":32}}}' -s initialize -e parseways ${VALHALLA_SOURCE_DIR}/test/data/utrecht_netherlands.osm.pbf COMMAND ${CMAKE_BINARY_DIR}/valhalla_build_tiles @@ -149,7 +167,7 @@ add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/test/data/utrecht_tiles/traffic.ta -t ${VALHALLA_SOURCE_DIR}/test/data/traffic_tiles/ COMMAND ${CMAKE_BINARY_DIR}/valhalla_build_extract --inline-config '{"mjolnir":{"tile_dir":"test/data/utrecht_tiles","tile_extract":"test/data/utrecht_tiles/tiles.tar","traffic_extract":"test/data/utrecht_tiles/traffic.tar","concurrency":1,"logging":{"type":""}}}' - --with-traffic + --with-traffic --overwrite COMMENT "Building Utrecht Tiles..." WORKING_DIRECTORY ${CMAKE_BINARY_DIR} DEPENDS valhalla_build_tiles valhalla_add_predicted_traffic build_timezones ${VALHALLA_SOURCE_DIR}/test/data/utrecht_netherlands.osm.pbf ${CMAKE_BINARY_DIR}/valhalla_build_extract) @@ -338,7 +356,8 @@ if(ENABLE_DATA_TOOLS) add_dependencies(run-astar whitelion_tiles roma_tiles reversed_whitelion_tiles bayfront_singapore_tiles ny_ar_tiles pa_ar_tiles nh_ar_tiles melborne_tiles utrecht_tiles) add_dependencies(run-alternates utrecht_tiles) add_dependencies(run-tar_index utrecht_tiles) -if(ENABLE_HTTP) + add_dependencies(run-graphbuilder build_timezones) + if(ENABLE_HTTP) add_dependencies(run-http_tiles utrecht_tiles) endif() endif() diff --git a/test/access_restriction.cc b/test/access_restriction.cc index 5613753614..46ca8332e7 100644 --- a/test/access_restriction.cc +++ b/test/access_restriction.cc @@ -1,5 +1,3 @@ -#include - #include "baldr/accessrestriction.h" #include "test.h" diff --git a/test/actor.cc b/test/actor.cc index db86ad572f..3b181e3bdc 100644 --- a/test/actor.cc +++ b/test/actor.cc @@ -1,6 +1,4 @@ #include -#include -#include #include #include "tyr/actor.h" diff --git a/test/admin.cc b/test/admin.cc index a9984bf103..f256ccfdd3 100644 --- a/test/admin.cc +++ b/test/admin.cc @@ -1,5 +1,3 @@ -#include - #include "baldr/admin.h" #include "test.h" diff --git a/test/alternates.cc b/test/alternates.cc index 22a2309b25..0230676dd4 100644 --- a/test/alternates.cc +++ b/test/alternates.cc @@ -1,6 +1,5 @@ #include "test.h" -#include #include #include "baldr/rapidjson_utils.h" @@ -10,7 +9,6 @@ #include "odin/worker.h" #include "thor/worker.h" #include "tyr/serializers.h" -#include using namespace valhalla; using namespace valhalla::thor; diff --git a/test/astar.cc b/test/astar.cc index b43bc610d7..efa1370a9d 100644 --- a/test/astar.cc +++ b/test/astar.cc @@ -1,7 +1,6 @@ #include "midgard/logging.h" #include "test.h" #include -#include #include "baldr/graphid.h" #include "baldr/graphreader.h" @@ -39,7 +38,6 @@ #include "proto/trip.pb.h" #include -#include #include #if !defined(VALHALLA_SOURCE_DIR) @@ -206,8 +204,8 @@ void make_tile() { void create_costing_options(Options& options, Costing::Type costing) { const rapidjson::Document doc; - sif::ParseCosting(doc, "/costing_options", options); options.set_costing_type(costing); + sif::ParseCosting(doc, "/costing_options", options); } // Convert locations to format needed by PathAlgorithm std::vector ToPBFLocations(const std::vector& locations, @@ -239,7 +237,7 @@ std::unique_ptr get_graph_reader(const std::string& tile_dir) { std::unique_ptr reader(new vb::GraphReader(conf)); auto tile = reader->GetGraphTile(tile_id); - EXPECT_NE(tile, nullptr) << "Unable to load test tile! Did `make_tile` run succesfully?"; + EXPECT_NE(tile, nullptr) << "Unable to load test tile! Did `make_tile` run successfully?"; if (tile->header()->directededgecount() != 28) { throw std::logic_error("test-tiles does not contain expected number of edges"); } @@ -500,7 +498,7 @@ TEST(Astar, test_oneway_wrong_way) { try { auto response = tester.test(request); - FAIL() << "Expectd exception!"; + FAIL() << "Expected exception!"; } catch (const std::exception& e) { EXPECT_EQ(std::string(e.what()), "No path could be found for input"); } catch (...) { FAIL() << "Wrong exception type"; } @@ -956,7 +954,7 @@ void test_backtrack_complex_restriction(int date_time_type) { default: throw std::runtime_error("unhandled case"); } - EXPECT_EQ(leg.shape(), correct_shape) + EXPECT_TRUE(test::encoded_shape_equality(leg.shape(), correct_shape)) << "Did not find expected shape. Found \n" + leg.shape() + "\nbut expected \n" + correct_shape; std::vector names; @@ -1348,7 +1346,7 @@ TEST(Astar, test_complex_restriction_short_path_melborne) { R"({"locations":[{"lat":-37.627860699397075,"lon":145.365825588286},{"lat":-37.62842169939707,"lon":145.36587158828598}],"costing":"auto"})"; auto response = tester.test(request); const auto& leg = response.trip().routes(0).legs(0); - EXPECT_EQ(leg.shape(), "~{rwfAmslgtGxNkUvDtDtLjM"); + EXPECT_TRUE(test::encoded_shape_equality(leg.shape(), "~{rwfAmslgtGxNkUvDtDtLjM")); } { // Tests "X-crossing", @@ -1357,7 +1355,7 @@ TEST(Astar, test_complex_restriction_short_path_melborne) { R"({"locations":[{"lat":-37.62403769939707,"lon":145.360320588286},{"lat":-37.624804699397075,"lon":145.36041758828597}],"costing":"auto"})"; auto response = tester.test(request); const auto& leg = response.trip().routes(0).legs(0); - EXPECT_EQ(leg.shape(), "rmkwfAwzagtGlAgCnB}CfHcKzLk@lPbQ"); + EXPECT_TRUE(test::encoded_shape_equality(leg.shape(), "rmkwfAwzagtGlAgCnB}CfHcKzLk@lPbQ")); } } @@ -1571,7 +1569,7 @@ TEST(BiDiAstar, test_recost_path) { )"; const gurka::ways ways = { // make ABC to be a shortcut - {"ABC", {{"highway", "primary"}, {"maxspeed", "80"}}}, + {"ABC", {{"highway", "motorway"}, {"maxspeed", "80"}}}, // make CDE to be a shortcut {"CDE", {{"highway", "primary"}, {"maxspeed", "80"}}}, {"1A", {{"highway", "secondary"}}}, @@ -1580,8 +1578,8 @@ TEST(BiDiAstar, test_recost_path) { {"D5", {{"highway", "secondary"}}}, // set speeds less than on ABCDE path to force the algorithm // to go through ABCDE nodes instead of AXY - {"AX", {{"highway", "primary"}, {"maxspeed", "70"}}}, - {"XY", {{"highway", "primary"}, {"maxspeed", "70"}}}, + {"AX", {{"highway", "motorway"}, {"maxspeed", "70"}}}, + {"XY", {{"highway", "trunk"}, {"maxspeed", "70"}}}, {"YE", {{"highway", "primary"}, {"maxspeed", "80"}}}, {"E2", {{"highway", "secondary"}}}, }; @@ -1679,6 +1677,81 @@ TEST(BiDiAstar, test_recost_path) { } } +// TODO(nils): this test fails currently, because bidir A* has a problem with 2 shortcuts between the +// same nodes: https://github.com/valhalla/valhalla/issues/4609 +TEST(BiDiAstar, DISABLED_test_recost_path_failing) { + const std::string ascii_map = R"( + X-----------Y + / \ + 1----A E---2 + \ / + B--C--------D + )"; + const gurka::ways ways = { + // make ABCDE to be a shortcut + {"ABC", {{"highway", "primary"}, {"maxspeed", "80"}}}, + {"CDE", {{"highway", "primary"}, {"maxspeed", "80"}}}, + {"1A", {{"highway", "secondary"}}}, + // set speeds less than on ABCDE path to force the algorithm + // to go through ABCDE nodes instead of AXY + {"AX", {{"highway", "primary"}, {"maxspeed", "70"}}}, + {"XY", {{"highway", "primary"}, {"maxspeed", "70"}}}, + {"YE", {{"highway", "primary"}, {"maxspeed", "80"}}}, + {"E2", {{"highway", "secondary"}}}, + }; + + auto nodes = gurka::detail::map_to_coordinates(ascii_map, 500); + + const std::string test_dir = "test/data/astar_shortcuts_recosting"; + const auto map = gurka::buildtiles(nodes, ways, {}, {}, test_dir); + + vb::GraphReader graphreader(map.config.get_child("mjolnir")); + + // before continue check that ABC is actually a shortcut + const auto ABCDE = gurka::findEdgeByNodes(graphreader, nodes, "A", "E"); + ASSERT_TRUE(std::get<1>(ABCDE)->is_shortcut()) << "Expected ABCDE to be a shortcut"; + + Options options; + create_costing_options(options, Costing::auto_); + vs::TravelMode travel_mode = vs::TravelMode::kDrive; + const auto mode_costing = vs::CostFactory().CreateModeCosting(options, travel_mode); + + std::vector locations; + // set origin location + locations.push_back({nodes["1"]}); + // set destination location + locations.push_back({nodes["2"]}); + auto pbf_locations = ToPBFLocations(locations, graphreader, mode_costing[int(travel_mode)]); + + vt::BidirectionalAStar astar; + + // hack hierarchy limits to allow to go through the shortcut + { + auto& hierarchy_limits = + mode_costing[int(travel_mode)]->GetHierarchyLimits(); // access mutable limits + for (auto& hierarchy : hierarchy_limits) { + hierarchy.Relax(0.f, 0.f); + } + } + const auto path = + astar.GetBestPath(pbf_locations[0], pbf_locations[1], graphreader, mode_costing, travel_mode) + .front(); + + // collect names of base edges + std::vector expected_names = {"1A", "AB", "BC", "CD", "DE", "E2"}; + std::vector actual_names; + for (const auto& info : path) { + const auto* edge = graphreader.directededge(info.edgeid); + ASSERT_FALSE(edge->is_shortcut()) << "Final path shouldn't contain shortcuts"; + + const auto name = graphreader.edgeinfo(info.edgeid).GetNames()[0]; + actual_names.emplace_back(name); + } + // TODO(nils): it gets the wrong path! bidir A* has a problem with 2 shortcuts between the same + // nodes + EXPECT_EQ(actual_names, expected_names); +} + class BiAstarTest : public thor::BidirectionalAStar { public: explicit BiAstarTest(const boost::property_tree::ptree& config = {}) : BidirectionalAStar(config) { diff --git a/test/astar_bikeshare.cc b/test/astar_bikeshare.cc index e1cbb884f8..9c698696ae 100644 --- a/test/astar_bikeshare.cc +++ b/test/astar_bikeshare.cc @@ -84,7 +84,8 @@ void test_request(const std::string& request, for (const auto& d : directions) { if (expected_shape) { - EXPECT_EQ(d.shape(), *expected_shape) << "The shape is incorrect"; + EXPECT_TRUE(test::encoded_shape_equality(d.shape(), *expected_shape)) + << "The shape is incorrect"; } size_t idx = -1; for (const auto& m : d.maneuver()) { @@ -305,8 +306,8 @@ TEST(AstarBss, test_Truck) { // (pedestrian way) ------------------------------> // // Since BSS connections are created over both Pedestrian mode and Bicycle mode, user should be able -// to turn back the bike right on the cyclelane, change the travel mode and coninue his journey way on -// the pedestrian way. +// to turn back the bike right on the cyclelane, change the travel mode and continue his journey way +// on the pedestrian way. TEST(AstarBss, test_BSSConnections_on_Pedestrian_and_Bicycle) { std::string request = R"({"locations":[{"lat":48.864218,"lon":2.362034},{"lat":48.869068,"lon":2.362151}],"costing":"bikeshare"})"; diff --git a/test/attributes_controller.cc b/test/attributes_controller.cc index 664bb8221d..280ace3a3b 100644 --- a/test/attributes_controller.cc +++ b/test/attributes_controller.cc @@ -9,7 +9,7 @@ using namespace valhalla::baldr; namespace { -TEST(AttrController, TestCtorDefautAttributes) { +TEST(AttrController, TestCtorDefaultAttributes) { AttributesController controller; EXPECT_EQ(controller.attributes, AttributesController::kDefaultAttributes); } diff --git a/test/bindings/python/test_utrecht.py b/test/bindings/python/test_utrecht.py index 8fcb490166..6268bc6293 100644 --- a/test/bindings/python/test_utrecht.py +++ b/test/bindings/python/test_utrecht.py @@ -12,9 +12,9 @@ def has_cyrillic(text): """ - This is ensuring that the given text contains cyrllic characters + This is ensuring that the given text contains Cyrillic characters :param text: The text to validate - :return: Returns true if there are cyrillic characters + :return: Returns true if there are Cyrillic characters """ # Note: The character range includes the entire Cyrillic script range including the extended # Cyrillic alphabet (e.g. ё, Є, ў) diff --git a/test/bindings/python/valhalla.json b/test/bindings/python/valhalla.json index 57c828d422..e9fd61ff71 100644 --- a/test/bindings/python/valhalla.json +++ b/test/bindings/python/valhalla.json @@ -9,6 +9,7 @@ "tile_extract": "test/data/utrecht_tiles/tiles.tar", "traffic_extract": "/data/valhalla/traffic.tar", "admin": "/data/valhalla/admin.sqlite", + "landmarks": "/data/valhalla/landmarks.sqlite", "timezone": "/data/valhalla/tz_world.sqlite", "transit_dir": "/data/valhalla/transit", "transit_feeds_dir": "/data/valhalla/transit_feeds", diff --git a/test/compression.cc b/test/compression.cc index f0509db6db..1c3bed9a43 100644 --- a/test/compression.cc +++ b/test/compression.cc @@ -68,11 +68,11 @@ TEST(Compression, roundtrip) { } TEST(Compression, fail_deflate) { - auto deflate_src_fail = [](z_stream& s) -> int { + auto deflate_src_fail = []([[maybe_unused]] z_stream& s) -> int { throw std::runtime_error("you cant catch me"); return Z_FINISH; }; - auto deflate_dst_fail = [](z_stream& s) -> void { + auto deflate_dst_fail = []([[maybe_unused]] z_stream& s) -> void { throw std::runtime_error("im the gingerbread man"); }; @@ -89,16 +89,18 @@ TEST(Compression, fail_deflate) { } TEST(Compression, fail_inflate) { - auto inflate_src_fail = [](z_stream& s) -> void { throw std::runtime_error("you cant catch me"); }; + auto inflate_src_fail = []([[maybe_unused]] z_stream& s) -> void { + throw std::runtime_error("you cant catch me"); + }; std::string bad = "this isn't gzipped"; auto inflate_src_fail2 = [&bad](z_stream& s) -> void { s.next_in = static_cast(static_cast(&bad[0])); s.avail_in = static_cast(bad.size() * sizeof(std::string::value_type)); }; - auto inflate_src_fail3 = [](z_stream& s) -> void { + auto inflate_src_fail3 = []([[maybe_unused]] z_stream& s) -> void { /* Nothing to do, simulates 'cannot inflate' - reproducible if no disk space. */ }; - auto inflate_dst_fail = [](z_stream& s) -> int { + auto inflate_dst_fail = []([[maybe_unused]] z_stream& s) -> int { throw std::runtime_error("im the gingerbread man"); return Z_NO_FLUSH; }; @@ -128,7 +130,7 @@ TEST(Compression, fail_inflate) { inflate_dst_fail)) << "dst should fail"; - bool inflate_result; + bool inflate_result{false}; EXPECT_NO_THROW( inflate_result = valhalla::baldr::inflate(inflate_src_fail3, std::bind(inflate_dst, std::placeholders::_1, diff --git a/test/configuration.cc b/test/configuration.cc new file mode 100644 index 0000000000..8a20e5786c --- /dev/null +++ b/test/configuration.cc @@ -0,0 +1,51 @@ +#include "test.h" +#include + +#include "config.h" + +namespace { + +TEST(Configuration, UseBeforeConfiguration) { + EXPECT_ANY_THROW(valhalla::config()); +} + +TEST(Configuration, ReadInlineConfig) { + using namespace valhalla; + + auto inline_config = R"( + { + "obj1": { + "val": "example" + }, + "obj2": { + "inner": { + "val": 4 + } + } + } + )"; + + auto conf = config(inline_config); + + EXPECT_EQ(conf.get("obj1.val"), "example"); + EXPECT_EQ(conf.get("obj2.inner.val"), 4); +} + +TEST(Configuration, OneInstanceExisting) { + using namespace valhalla; + + auto inline_config_1 = "{'key1': 'val1'}"; + auto inline_config_2 = "{'key2': 'val2'}"; + + auto conf1 = &config(inline_config_1); + auto conf2 = &config(inline_config_2); + + EXPECT_EQ(conf1, conf2); +} + +} // namespace + +int main(int argc, char* argv[]) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/countryaccess.cc b/test/countryaccess.cc index de12565617..fa4b7063f3 100644 --- a/test/countryaccess.cc +++ b/test/countryaccess.cc @@ -1,4 +1,5 @@ -#include "filesystem.h" +#include + #include "midgard/sequence.h" #include "mjolnir/graphbuilder.h" #include "mjolnir/graphenhancer.h" @@ -33,8 +34,8 @@ const std::string config_file = "test/test_config_country"; // Remove a temporary file if it exists void remove_temp_file(const std::string& fname) { - if (filesystem::exists(fname)) { - filesystem::remove(fname); + if (std::filesystem::exists(fname)) { + std::filesystem::remove(fname); } } @@ -55,24 +56,6 @@ void write_config(const std::string& filename) { file.close(); } -const auto node_predicate = [](const OSMWayNode& a, const OSMWayNode& b) { - return a.node.osmid_ < b.node.osmid_; -}; - -OSMNode GetNode(uint64_t node_id, sequence& way_nodes) { - auto found = way_nodes.find({node_id}, node_predicate); - EXPECT_NE(found, way_nodes.end()) << "Couldn't find node: " + std::to_string(node_id); - return (*found).node; -} - -auto way_predicate = [](const OSMWay& a, const OSMWay& b) { return a.osmwayid_ < b.osmwayid_; }; - -OSMWay GetWay(uint32_t way_id, sequence& ways) { - auto found = ways.find({way_id}, way_predicate); - EXPECT_NE(found, ways.end()) << "Couldn't find way: " + std::to_string(way_id); - return *found; -} - void CountryAccess(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); @@ -81,8 +64,8 @@ void CountryAccess(const std::string& config_file) { GraphReader graph_reader(conf.get_child("mjolnir")); for (const auto& level : TileHierarchy::levels()) { auto level_dir = graph_reader.tile_dir() + "/" + std::to_string(level.level); - if (filesystem::exists(level_dir) && !filesystem::is_empty(level_dir)) { - filesystem::remove_all(level_dir); + if (std::filesystem::exists(level_dir) && !std::filesystem::is_empty(level_dir)) { + std::filesystem::remove_all(level_dir); } } @@ -92,16 +75,15 @@ void CountryAccess(const std::string& config_file) { std::string nodes_file = "test_nodes_amsterdam.bin"; std::string edges_file = "test_edges_amsterdam.bin"; std::string access_file = "test_access_amsterdam.bin"; - std::string pronunciation_file = "test_pronunciation_amsterdam.bin"; std::string cr_from_file = "test_from_cr_amsterdam.bin"; std::string cr_to_file = "test_to_cr_amsterdam.bin"; std::string bss_nodes_file = "test_bss_nodes_amsterdam.bin"; + std::string linguistic_node_file = "test_linguistic_node_amsterdam.bin"; // Parse Amsterdam OSM data - auto osmdata = - PBFGraphParser::ParseWays(conf.get_child("mjolnir"), - {VALHALLA_SOURCE_DIR "test/data/amsterdam.osm.pbf"}, ways_file, - way_nodes_file, access_file, pronunciation_file); + auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), + {VALHALLA_SOURCE_DIR "test/data/amsterdam.osm.pbf"}, + ways_file, way_nodes_file, access_file); PBFGraphParser::ParseRelations(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/amsterdam.osm.pbf"}, cr_from_file, @@ -109,7 +91,7 @@ void CountryAccess(const std::string& config_file) { PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/amsterdam.osm.pbf"}, way_nodes_file, - bss_nodes_file, osmdata); + bss_nodes_file, linguistic_node_file, osmdata); std::map tiles = GraphBuilder::BuildEdges(conf.get_child("mjolnir"), ways_file, way_nodes_file, nodes_file, @@ -117,7 +99,7 @@ void CountryAccess(const std::string& config_file) { // Build the graph using the OSMNodes and OSMWays from the parser GraphBuilder::Build(conf, osmdata, ways_file, way_nodes_file, nodes_file, edges_file, cr_from_file, - cr_to_file, pronunciation_file, tiles); + cr_to_file, linguistic_node_file, tiles); // load a tile and test the default access. GraphId id(820099, 2, 0); @@ -255,6 +237,7 @@ void CountryAccess(const std::string& config_file) { remove_temp_file(access_file); remove_temp_file(cr_from_file); remove_temp_file(cr_to_file); + remove_temp_file(linguistic_node_file); } TEST(CountryAccess, Basic) { diff --git a/test/data/language_admin.sqlite b/test/data/language_admin.sqlite new file mode 100644 index 0000000000..705c67e406 Binary files /dev/null and b/test/data/language_admin.sqlite differ diff --git a/test/data/netherlands_admin.sqlite b/test/data/netherlands_admin.sqlite index 70e2f02a46..63c1aa404f 100644 Binary files a/test/data/netherlands_admin.sqlite and b/test/data/netherlands_admin.sqlite differ diff --git a/test/datetime.cc b/test/datetime.cc index d8a74211c3..07de5345c0 100644 --- a/test/datetime.cc +++ b/test/datetime.cc @@ -104,8 +104,8 @@ void TryGetDOW(const std::string& date_time, uint32_t expected_dow) { void TryGetDuration(const std::string& date_time, uint32_t seconds, const std::string& expected_date_time) { - - auto tz = DateTime::get_tz_db().from_index(DateTime::get_tz_db().to_index("America/New_York")); + const auto* tz = + DateTime::get_tz_db().from_index(DateTime::get_tz_db().to_index("America/New_York")); EXPECT_EQ(DateTime::get_duration(date_time, seconds, tz), expected_date_time) << std::string("Incorrect duration ") + DateTime::get_duration(date_time, seconds, tz) + @@ -550,6 +550,16 @@ TEST(DateTime, TestIsRestricted) { TryIsRestricted(td, "2022-05-10T16:00", false); TryIsRestricted(td, "2021-02-18T16:00", false); TryIsRestricted(td, "2021-06-26T16:00", false); + + td = TimeDomain(35184375234560); // "Jun-Aug" + TryIsRestricted(td, "2024-04-03T08:00", false); + TryIsRestricted(td, "2024-05-31T21:00", false); + TryIsRestricted(td, "2024-06-01T00:01", true); + TryIsRestricted(td, "2024-06-15T16:00", true); + TryIsRestricted(td, "2024-07-10T16:00", true); + TryIsRestricted(td, "2024-08-18T16:00", true); + TryIsRestricted(td, "2024-08-31T23:59", true); + TryIsRestricted(td, "2024-09-01T00:01", false); } TEST(DateTime, TestTimezoneDiff) { @@ -631,6 +641,46 @@ TEST(DateTime, TestDayOfWeek) { EXPECT_EQ(dow, 3) << "DateTime::day_of_week failed: 3 expected"; } +TEST(DateTime, TimezoneAliases) { + const auto& dt_db = DateTime::get_tz_db(); + // map of alias and target names + // this can be old deprecated timezone name (pre-2023) or renamed timezones (after 2023) + std::vector> pairs = + {{"Etc/Zulu", "Etc/UTC"}, + {"Etc/GMT-0", "Etc/GMT"}, + {"ROC", "Asia/Taipei"}, + {"GB", "Europe/London"}, + {"NZ-CHAT", "Pacific/Chatham"}, + {"Asia/Ujung_Pandang", "Asia/Makassar"}, + {"Africa/Bamako", "Africa/Abidjan"}, + {"Africa/Kampala", "Africa/Nairobi"}, + {"Asia/Kuala_Lumpur", "Asia/Singapore"}, // ^ deprecated 2018 tz + {"America/Nuuk", "America/Godthab"}, + {"Pacific/Kanton", "Pacific/Enderbury"}, + {"Europe/Kyiv", "Europe/Kiev"}}; // ^ renamed 2023 tz + + for (const auto& pair : pairs) { + auto alias_idx = dt_db.from_index(dt_db.to_index(pair.first)); + EXPECT_EQ(alias_idx, dt_db.from_index(dt_db.to_index(pair.second))); + } +} + +TEST(DateTime, TimezoneIndices) { + const auto& dt_db = DateTime::get_tz_db(); + + // test some official/current timezone names & indices + std::vector> pairs = { + {1, "Africa/Abidjan"}, {82, "America/Indiana/Tell_City"}, {323, "Europe/Samara"}, + {387, "WET"}, {629, "America/Ciudad_Juarez"}, // new timezone since 2023c update + {726, "Asia/Qostanay"}, // new timezone since 2023c update + }; + + for (const auto& pair : pairs) { + EXPECT_EQ(pair.first, dt_db.to_index(pair.second)); + EXPECT_EQ(pair.second, dt_db.from_index(pair.first)->name()); + } +} + TEST(DateTime, TestSecondOfWeek) { auto tz = DateTime::get_tz_db().from_index(DateTime::get_tz_db().to_index("America/New_York")); // 2019-11-06T17:15 @@ -686,19 +736,24 @@ TEST(DateTime, DiffCaching) { tc{123, 335, 7}, tc{2, 335, 8}, }; - // for each test case + // some of the above tz indices point to the same tz bcs of deprecations + // and how we handle them + std::unordered_set unique_tzs; uint64_t total_offset = 0; + // for each test case for (const auto& test_case : test_cases) { // the first part is how many iterations/calls to the diff method for (int i = 0; i < std::get<0>(test_case); ++i) { // the second two parts of the tuple are the origin tz index and the destination tz index + unique_tzs.insert(tzdb.from_index(std::get<1>(test_case))); + unique_tzs.insert(tzdb.from_index(std::get<2>(test_case))); total_offset += DateTime::timezone_diff(1586579654 + i * 15, tzdb.from_index(std::get<1>(test_case)), tzdb.from_index(std::get<2>(test_case)), &cache); } } EXPECT_NE(total_offset, 0); - EXPECT_GE(cache.size(), test_cases.size()); + EXPECT_GE(cache.size(), unique_tzs.size()); } } // namespace diff --git a/test/double_bucket_queue.cc b/test/double_bucket_queue.cc index c809016404..a20ed61fd4 100644 --- a/test/double_bucket_queue.cc +++ b/test/double_bucket_queue.cc @@ -1,10 +1,10 @@ #include "baldr/double_bucket_queue.h" #include "config.h" #include "midgard/util.h" +#include "sif/edgelabel.h" #include #include #include -#include #include #include @@ -13,6 +13,12 @@ using namespace std; using namespace valhalla; using namespace valhalla::baldr; +using namespace valhalla::sif; + +constexpr size_t kEdgeLabelExpectedSize = 40; +constexpr size_t kPathEdgeLabelExpectedSize = 48; +constexpr size_t kBDEdgeLabelExpectedSize = 64; +constexpr size_t kMMEdgeLabelExpectedSize = 72; namespace { @@ -91,7 +97,7 @@ TEST(DoubleBucketQueue, RC4FloatPrecisionErrors) { } /** - void TestDecreseCost() { + void TestDecreaseCost() { std::vector costs = { 67, 325, 25, 466, 1000, 100005, 758, 167, 258, 16442, 278, 111111000 }; std::vector expectedorder = costs; @@ -203,6 +209,14 @@ TEST(DoubleBucketQueue, TestSimulation) { } } +// Test EdgeLabel size +TEST(EdgeLabel, test_sizeof) { + EXPECT_EQ(sizeof(EdgeLabel), kEdgeLabelExpectedSize); + EXPECT_EQ(sizeof(PathEdgeLabel), kPathEdgeLabelExpectedSize); + EXPECT_EQ(sizeof(BDEdgeLabel), kBDEdgeLabelExpectedSize); + EXPECT_EQ(sizeof(MMEdgeLabel), kMMEdgeLabelExpectedSize); +} + } // namespace int main(int argc, char* argv[]) { diff --git a/test/edgecollapser.cc b/test/edgecollapser.cc index ff3c95a1e9..7e4f63d38a 100644 --- a/test/edgecollapser.cc +++ b/test/edgecollapser.cc @@ -105,12 +105,12 @@ struct test_graph_reader : public vb::GraphReader { TEST(EdgeCollapser, TestCollapseEdgeSimple) { vb::GraphId base_id = vb::TileHierarchy::GetGraphId(valhalla::midgard::PointLL(0, 0), 0); - // simplest graph with a collapsible node: - // - // /---(edge 0)-->\ /---(edge 1)-->\ - // (node 0) (node 1) (node 2) - // \<--(edge 2)---/ \<--(edge 3)---/ - // + /* simplest graph with a collapsible node: + + /---(edge 0)-->\ /---(edge 1)-->\ + (node 0) (node 1) (node 2) + \<--(edge 2)---/ \<--(edge 3)---/ + */ valhalla::midgard::PointLL base_ll(0.0f, 0.0f); graph_tile_builder builder; builder.append_node(base_ll, 0.00f, 0.0f, 1, 0); @@ -154,16 +154,17 @@ TEST(EdgeCollapser, TestCollapseEdgeSimple) { TEST(EdgeCollapser, TestCollapseEdgeJunction) { vb::GraphId base_id = vb::TileHierarchy::GetGraphId(valhalla::midgard::PointLL(0, 0), 0); - // simplest graph with a non-collapsible node: - // - // /---(edge 0)-->\ /---(edge 1)-->\ - // (node 0) (node 1) (node 2) - // \<--(edge 2)---/ / \ \<--(edge 4)---/ - // | | - // (edge 5) ^ v (edge 3) - // | | - // \ / - // (node 3) + /* simplest graph with a non-collapsible node: + + /---(edge 0)-->\ /---(edge 1)-->\ + (node 0) (node 1) (node 2) + \<--(edge 2)---/ / \ \<--(edge 4)---/ + | | + (edge 5) ^ v (edge 3) + | | + \ / + (node 3) + */ valhalla::midgard::PointLL base_ll(0.0f, 0.0f); graph_tile_builder builder; builder.append_node(base_ll, 0.00f, 0.00f, 1, 0); @@ -210,13 +211,13 @@ TEST(EdgeCollapser, TestCollapseEdgeJunction) { TEST(EdgeCollapser, TestCollapseEdgeChain) { vb::GraphId base_id = vb::TileHierarchy::GetGraphId(valhalla::midgard::PointLL(0, 0), 0); - // graph with 3 collapsible edges, all chained together. (e.g: think of the - // middle segment as a bridge). - // - // /---(e0)-->\ /---(e1)-->\ /---(e3)-->\ - // (n0) (n1) (n2) (n3) - // \<--(e2)---/ \<--(e4)---/ \<--(e5)---/ - // + /* graph with 3 collapsible edges, all chained together. (e.g: think of the + middle segment as a bridge). + + /---(e0)-->\ /---(e1)-->\ /---(e3)-->\ + (n0) (n1) (n2) (n3) + \<--(e2)---/ \<--(e4)---/ \<--(e5)---/ + */ valhalla::midgard::PointLL base_ll(0.0f, 0.0f); graph_tile_builder builder; builder.append_node(base_ll, 0.00f, 0.0f, 1, 0); diff --git a/test/edgeinfobuilder.cc b/test/edgeinfobuilder.cc index dcd86f398f..b2da922b7a 100644 --- a/test/edgeinfobuilder.cc +++ b/test/edgeinfobuilder.cc @@ -2,7 +2,6 @@ #include #include -#include #include #include "baldr/edgeinfo.h" @@ -66,9 +65,9 @@ TEST(EdgeInfoBuilder, TestWriteRead) { // Name std::vector name_info_list; - name_info_list.push_back({963}); - name_info_list.push_back({957}); - name_info_list.push_back({862}); + name_info_list.push_back({963, 0, 0, 0, 0}); + name_info_list.push_back({957, 0, 0, 0, 0}); + name_info_list.push_back({862, 0, 0, 0, 0}); eibuilder.set_name_info_list(name_info_list); // Shape diff --git a/test/elevation_builder.cc b/test/elevation_builder.cc index 60542dd5c0..2c91008768 100644 --- a/test/elevation_builder.cc +++ b/test/elevation_builder.cc @@ -15,8 +15,7 @@ #include "midgard/sequence.h" #include "mjolnir/elevationbuilder.h" #include "mjolnir/graphtilebuilder.h" -#include "mjolnir/util.h" -#include "mjolnir/valhalla_add_elevation_utils.h" +#include "mjolnir/util.h #include "pixels.h" #include "skadi/sample.h" #include "tile_server.h" @@ -228,6 +227,7 @@ TEST(ElevationBuilder, get_tile_ids) { // check if config contains tile_dir EXPECT_TRUE(valhalla::mjolnir::get_tile_ids(config, {"/0/003/196.gph"}).empty()); + // TODO: I don't understand what the following lines have to do with anything? // check if config contains elevation dir config.erase("additional_data.elevation"); EXPECT_TRUE(valhalla::mjolnir::get_tile_ids(config, {"/0/003/196.gph"}).empty()); diff --git a/test/encode.cc b/test/encode.cc index fdf619755e..37a346d67c 100644 --- a/test/encode.cc +++ b/test/encode.cc @@ -19,18 +19,6 @@ void assert_approx_equal(const container_t& a, const container_t& b, const float } } -// need ostream operators for some of these types -std::string to_string(const container_t& points) { - std::string out = "{"; - for (const auto& p : points) { - out += "{" + std::to_string(p.first) + ", " + std::to_string(p.second) + "}"; - } - out += "}"; - if (out.length() > 2) - out.erase(out.end() - 3, out.end() - 1); - return out; -} - void do_polyline_pair(const container_t& points, const std::string& encoded) { auto enc_answer = encode(points); EXPECT_EQ(enc_answer, encoded) << "Simple polyline encoding failed"; diff --git a/test/factory.cc b/test/factory.cc index 381211ea4f..3facb253bf 100644 --- a/test/factory.cc +++ b/test/factory.cc @@ -15,14 +15,13 @@ namespace { TEST(Factory, Register) { Options options; const rapidjson::Document doc; - sif::ParseCosting(doc, "/costing_options", options); CostFactory factory; options.set_costing_type(Costing::auto_); + sif::ParseCosting(doc, "/costing_options", options); auto car = factory.Create(options); options.set_costing_type(Costing::bicycle); + sif::ParseCosting(doc, "/costing_options", options); auto bike = factory.Create(options); - options.set_costing_type(Costing::multimodal); - EXPECT_THROW(factory.Create(options), std::runtime_error); auto truck = factory.Create(Costing::truck); } diff --git a/test/graphbuilder.cc b/test/graphbuilder.cc index 64957ffe05..4c8419f097 100644 --- a/test/graphbuilder.cc +++ b/test/graphbuilder.cc @@ -1,12 +1,12 @@ #include "mjolnir/graphbuilder.h" #include "baldr/graphreader.h" #include "midgard/sequence.h" +#include "mjolnir/admin.h" #include "mjolnir/directededgebuilder.h" #include "mjolnir/osmdata.h" #include "mjolnir/pbfgraphparser.h" #include "mjolnir/util.h" -#include #include #include @@ -33,11 +33,10 @@ using valhalla::mjolnir::TileManifest; namespace { const std::string pbf_file = {VALHALLA_SOURCE_DIR "test/data/harrisburg.osm.pbf"}; -const std::string tile_dir = "test/data/graphbuilder_tiles"; +const std::string tile_dir = {VALHALLA_BUILD_DIR "test/data/graphbuilder_tiles"}; const size_t id_table_size = 1000; const std::string access_file = "test_access_harrisburg.bin"; -const std::string pronunciation_file = "test_pronunciation_harrisburg.bin"; const std::string bss_file = "test_bss_nodes_harrisburg.bin"; const std::string edges_file = "test_edges_harrisburg.bin"; const std::string from_restriction_file = "test_from_complex_restrictions_harrisburg.bin"; @@ -45,6 +44,7 @@ const std::string nodes_file = "test_nodes_harrisburg.bin"; const std::string to_restriction_file = "test_to_complex_restrictions_harrisburg.bin"; const std::string way_nodes_file = "test_way_nodes_harrisburg.bin"; const std::string ways_file = "test_ways_harrisburg.bin"; +const std::string linguistic_node_file = "test_linguistic_node_harrisburg.bin"; // Test output from construct edges and that the expected number of tiles are produced from the // build tiles step that follows. @@ -64,7 +64,7 @@ TEST(GraphBuilder, TestConstructEdges) { // This directory should be empty filesystem::remove_all(tile_dir); GraphBuilder::Build(config, osm_data, ways_file, way_nodes_file, nodes_file, edges_file, - from_restriction_file, to_restriction_file, pronunciation_file, tiles); + from_restriction_file, to_restriction_file, linguistic_node_file, tiles); GraphReader reader(config.get_child("mjolnir")); EXPECT_EQ(reader.GetTileSet(2).size(), 4); // Clear the tile directory so it doesn't interfere with the next test with graphreader. @@ -86,7 +86,7 @@ TEST(Graphbuilder, TestConstructEdgesSubset) { // This directory should be empty filesystem::remove_all(tile_dir); GraphBuilder::Build(config, osm_data, ways_file, way_nodes_file, nodes_file, edges_file, - from_restriction_file, to_restriction_file, pronunciation_file, tiles); + from_restriction_file, to_restriction_file, linguistic_node_file, tiles); GraphReader reader(config.get_child("mjolnir")); EXPECT_EQ(reader.GetTileSet(2).size(), 1); EXPECT_TRUE(reader.DoesTileExist(GraphId{5993698})); @@ -113,6 +113,44 @@ TEST(Graphbuilder, TestDEBuilderLength) { std::runtime_error); } +// test new timezones here instead of a new mjolnir test +class TestNodeInfo : NodeInfo { +public: + using NodeInfo::set_timezone; + + uint32_t get_raw_timezone_field() const { + return timezone_; + } + + uint32_t get_raw_timezone_ext1_field() const { + return timezone_ext_1_; + } +}; + +TEST(Graphbuilder, NewTimezones) { + TestNodeInfo test_node; + auto* sql_db = GetDBHandle(VALHALLA_BUILD_DIR "test/data/tz.sqlite"); + + auto sconn = make_spatialite_cache(sql_db); + const auto& tzdb = DateTime::get_tz_db(); + + // America/Ciudad_Juarez + auto ciudad_juarez_polys = GetTimeZones(sql_db, {-106.450948, 31.669746, -106.386046, 31.724371}); + EXPECT_EQ(ciudad_juarez_polys.begin()->first, tzdb.to_index("America/Ciudad_Juarez")); + test_node.set_timezone(ciudad_juarez_polys.begin()->first); + EXPECT_EQ(test_node.get_raw_timezone_field(), tzdb.to_index("America/Ojinaga")); + EXPECT_EQ(test_node.get_raw_timezone_ext1_field(), 1); + + // Asia/Qostanay + auto qostanay_polys = GetTimeZones(sql_db, {62.41766759, 51.37601571, 64.83104595, 52.71089583}); + EXPECT_EQ(qostanay_polys.begin()->first, tzdb.to_index("Asia/Qostanay")); + test_node.set_timezone(qostanay_polys.begin()->first); + EXPECT_EQ(test_node.get_raw_timezone_field(), tzdb.to_index("Asia/Qyzylorda")); + EXPECT_EQ(test_node.get_raw_timezone_ext1_field(), 1); + + sqlite3_close(sql_db); +} + class HarrisburgTestSuiteEnv : public ::testing::Environment { public: void SetUp() override { @@ -122,10 +160,11 @@ class HarrisburgTestSuiteEnv : public ::testing::Environment { const auto& mjolnir_config = config.get_child("mjolnir"); const std::vector& input_files = {pbf_file}; OSMData osmdata = PBFGraphParser::ParseWays(mjolnir_config, input_files, ways_file, - way_nodes_file, access_file, pronunciation_file); + way_nodes_file, access_file); PBFGraphParser::ParseRelations(mjolnir_config, input_files, from_restriction_file, to_restriction_file, osmdata); - PBFGraphParser::ParseNodes(mjolnir_config, input_files, way_nodes_file, bss_file, osmdata); + PBFGraphParser::ParseNodes(mjolnir_config, input_files, way_nodes_file, bss_file, + linguistic_node_file, osmdata); } void TearDown() override { @@ -135,6 +174,7 @@ class HarrisburgTestSuiteEnv : public ::testing::Environment { filesystem::remove(from_restriction_file); filesystem::remove(to_restriction_file); filesystem::remove(bss_file); + filesystem::remove(linguistic_node_file); } }; diff --git a/test/graphparser.cc b/test/graphparser.cc index 8bcb762a24..9cd7a7a2b4 100644 --- a/test/graphparser.cc +++ b/test/graphparser.cc @@ -1,7 +1,6 @@ #include "baldr/graphreader.h" #include "baldr/rapidjson_utils.h" #include "baldr/tilehierarchy.h" -#include "filesystem.h" #include "midgard/sequence.h" #include "mjolnir/bssbuilder.h" #include "mjolnir/graphbuilder.h" @@ -11,6 +10,7 @@ #include #include +#include #include #include "baldr/directededge.h" @@ -28,6 +28,16 @@ namespace { const std::string config_file = "test/test_config_gp"; +std::string ways_file = "test_ways.bin"; +std::string way_nodes_file = "test_way_nodes.bin"; +std::string nodes_file = "test_nodes.bin"; +std::string edges_file = "test_edges.bin"; +std::string access_file = "test_access.bin"; +std::string from_restriction_file = "test_from_complex_restrictions.bin"; +std::string to_restriction_file = "test_to_complex_restrictions.bin"; +std::string bss_nodes_file = "test_bss_nodes.bin"; +std::string linguistic_node_file = "test_linguistic_node.bin"; + const auto node_predicate = [](const OSMWayNode& a, const OSMWayNode& b) { return a.node.osmid_ < b.node.osmid_; }; @@ -48,22 +58,59 @@ OSMWay GetWay(uint32_t way_id, sequence& ways) { return *found; } +void DoConfig() { + std::ofstream file; + try { + file.open(config_file, std::ios_base::trunc); + file << "{ \ + \"mjolnir\": { \ + \"id_table_size\": 1000, \ + \"tile_dir\": \"test/data/parser_tiles\" \ + } \ + }"; + } catch (...) {} + file.close(); +} + +// must do clean up here vs TearDown() as we are building data +// in the same directory multiple times +void CleanUp() { + if (std::filesystem::exists(ways_file)) + std::filesystem::remove(ways_file); + + if (std::filesystem::exists(way_nodes_file)) + std::filesystem::remove(way_nodes_file); + + if (std::filesystem::exists(nodes_file)) + std::filesystem::remove(nodes_file); + + if (std::filesystem::exists(edges_file)) + std::filesystem::remove(edges_file); + + if (std::filesystem::exists(access_file)) + std::filesystem::remove(access_file); + + if (std::filesystem::exists(from_restriction_file)) + std::filesystem::remove(from_restriction_file); + + if (std::filesystem::exists(to_restriction_file)) + std::filesystem::remove(to_restriction_file); + + if (std::filesystem::exists(bss_nodes_file)) + std::filesystem::remove(bss_nodes_file); + + if (std::filesystem::exists(linguistic_node_file)) + std::filesystem::remove(linguistic_node_file); +} + void BollardsGatesAndAccess(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); - std::string ways_file = "test_ways.bin"; - std::string way_nodes_file = "test_way_nodes.bin"; - std::string access_file = "test_access.bin"; - std::string pronunciation_file = "test_pronunciation.bin"; - std::string from_restriction_file = "test_from_complex_restrictions.bin"; - std::string to_restriction_file = "test_to_complex_restrictions.bin"; - std::string bss_nodes_file = "test_bss_nodes.bin"; - auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/liechtenstein-latest.osm.pbf"}, - ways_file, way_nodes_file, access_file, pronunciation_file); + ways_file, way_nodes_file, access_file); PBFGraphParser::ParseRelations(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/liechtenstein-latest.osm.pbf"}, @@ -71,7 +118,7 @@ void BollardsGatesAndAccess(const std::string& config_file) { PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/liechtenstein-latest.osm.pbf"}, - way_nodes_file, bss_nodes_file, osmdata); + way_nodes_file, bss_nodes_file, linguistic_node_file, osmdata); sequence way_nodes(way_nodes_file, false); way_nodes.sort(node_predicate); @@ -206,29 +253,16 @@ void BollardsGatesAndAccess(const std::string& config_file) { EXPECT_TRUE((bike_network & kMcn) && (bike_network & kRcn) && way_75786176.bike_network() == 0) << "rcn and mtb not marked on way 75786176."; - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(bss_nodes_file); + CleanUp(); } void RemovableBollards(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); - std::string ways_file = "test_ways.bin"; - std::string way_nodes_file = "test_way_nodes.bin"; - std::string access_file = "test_access.bin"; - std::string pronunciation_file = "test_pronunciation.bin"; - std::string from_restriction_file = "test_from_complex_restrictions.bin"; - std::string to_restriction_file = "test_to_complex_restrictions.bin"; - std::string bss_nodes_file = "test_bss_nodes.bin"; - auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/rome.osm.pbf"}, ways_file, - way_nodes_file, access_file, pronunciation_file); + way_nodes_file, access_file); PBFGraphParser::ParseRelations(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/rome.osm.pbf"}, @@ -236,7 +270,7 @@ void RemovableBollards(const std::string& config_file) { PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/rome.osm.pbf"}, way_nodes_file, - bss_nodes_file, osmdata); + bss_nodes_file, linguistic_node_file, osmdata); sequence way_nodes(way_nodes_file, false); way_nodes.sort(node_predicate); @@ -249,30 +283,16 @@ void RemovableBollards(const std::string& config_file) { kEmergencyAccess | kPedestrianAccess | kWheelchairAccess | kBicycleAccess | kMopedAccess | kMotorcycleAccess); - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(bss_nodes_file); + CleanUp(); } void Exits(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); - std::string ways_file = "test_ways.bin"; - std::string way_nodes_file = "test_way_nodes.bin"; - std::string access_file = "test_access.bin"; - std::string pronunciation_file = "test_pronunciation.bin"; - std::string from_restriction_file = "test_from_complex_restrictions.bin"; - std::string to_restriction_file = "test_to_complex_restrictions.bin"; - std::string bss_nodes_file = "test_bss_nodes.bin"; - - auto osmdata = - PBFGraphParser::ParseWays(conf.get_child("mjolnir"), - {VALHALLA_SOURCE_DIR "test/data/harrisburg.osm.pbf"}, ways_file, - way_nodes_file, access_file, pronunciation_file); + auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), + {VALHALLA_SOURCE_DIR "test/data/harrisburg.osm.pbf"}, + ways_file, way_nodes_file, access_file); PBFGraphParser::ParseRelations(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/harrisburg.osm.pbf"}, @@ -280,7 +300,7 @@ void Exits(const std::string& config_file) { PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/harrisburg.osm.pbf"}, way_nodes_file, - bss_nodes_file, osmdata); + bss_nodes_file, linguistic_node_file, osmdata); sequence way_nodes(way_nodes_file, false); way_nodes.sort(node_predicate); @@ -301,30 +321,16 @@ void Exits(const std::string& config_file) { EXPECT_EQ(osmdata.node_names.name(node.exit_to_index()), "PA441") << "node exit_to not set correctly ."; - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(bss_nodes_file); + CleanUp(); } void Baltimore(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); - std::string ways_file = "test_ways.bin"; - std::string way_nodes_file = "test_way_nodes.bin"; - std::string access_file = "test_access.bin"; - std::string pronunciation_file = "test_pronunciation.bin"; - std::string from_restriction_file = "test_from_complex_restrictions.bin"; - std::string to_restriction_file = "test_to_complex_restrictions.bin"; - std::string bss_nodes_file = "test_bss_nodes.bin"; - - auto osmdata = - PBFGraphParser::ParseWays(conf.get_child("mjolnir"), - {VALHALLA_SOURCE_DIR "test/data/baltimore.osm.pbf"}, ways_file, - way_nodes_file, access_file, pronunciation_file); + auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), + {VALHALLA_SOURCE_DIR "test/data/baltimore.osm.pbf"}, + ways_file, way_nodes_file, access_file); PBFGraphParser::ParseRelations(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/baltimore.osm.pbf"}, @@ -332,7 +338,7 @@ void Baltimore(const std::string& config_file) { PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/baltimore.osm.pbf"}, way_nodes_file, - bss_nodes_file, osmdata); + bss_nodes_file, linguistic_node_file, osmdata); sequence ways(ways_file, false); ways.sort(way_predicate); @@ -429,29 +435,16 @@ void Baltimore(const std::string& config_file) { FAIL() << "98040438 restriction test failed."; } - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(bss_nodes_file); + CleanUp(); } void Bike(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); - std::string ways_file = "test_ways.bin"; - std::string way_nodes_file = "test_way_nodes.bin"; - std::string access_file = "test_access.bin"; - std::string pronunciation_file = "test_pronunciation.bin"; - std::string from_restriction_file = "test_from_complex_restrictions.bin"; - std::string to_restriction_file = "test_to_complex_restrictions.bin"; - std::string bss_nodes_file = "test_bss_nodes.bin"; - auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/bike.osm.pbf"}, ways_file, - way_nodes_file, access_file, pronunciation_file); + way_nodes_file, access_file); PBFGraphParser::ParseRelations(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/bike.osm.pbf"}, @@ -459,7 +452,7 @@ void Bike(const std::string& config_file) { PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/bike.osm.pbf"}, way_nodes_file, - bss_nodes_file, osmdata); + bss_nodes_file, linguistic_node_file, osmdata); sequence ways(ways_file, false); ways.sort(way_predicate); @@ -526,35 +519,23 @@ void Bike(const std::string& config_file) { EXPECT_TRUE(way_156539491.moped_forward()); EXPECT_TRUE(way_156539491.bike_backward()); - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); + CleanUp(); } void Bus(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); - std::string ways_file = "test_ways.bin"; - std::string way_nodes_file = "test_way_nodes.bin"; - std::string access_file = "test_access.bin"; - std::string pronunciation_file = "test_pronunciation.bin"; - std::string from_restriction_file = "test_from_complex_restrictions.bin"; - std::string to_restriction_file = "test_to_complex_restrictions.bin"; - std::string bss_nodes_file = "test_bss_nodes.bin"; - auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/bus.osm.pbf"}, ways_file, - way_nodes_file, access_file, pronunciation_file); + way_nodes_file, access_file); PBFGraphParser::ParseRelations(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/bus.osm.pbf"}, from_restriction_file, to_restriction_file, osmdata); PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/bus.osm.pbf"}, - way_nodes_file, bss_nodes_file, osmdata); + way_nodes_file, bss_nodes_file, linguistic_node_file, osmdata); sequence ways(ways_file, false); ways.sort(way_predicate); @@ -607,36 +588,23 @@ void Bus(const std::string& config_file) { EXPECT_FALSE(way_225895737.bus_backward()); EXPECT_FALSE(way_225895737.bike_backward()); - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(bss_nodes_file); + CleanUp(); } void BicycleTrafficSignals(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); - std::string ways_file = "test_ways.bin"; - std::string way_nodes_file = "test_way_nodes.bin"; - std::string access_file = "test_access.bin"; - std::string pronunciation_file = "test_pronunciation.bin"; - std::string from_restriction_file = "test_from_complex_restrictions.bin"; - std::string to_restriction_file = "test_to_complex_restrictions.bin"; - std::string bss_nodes_file = "test_bss_nodes.bin"; - auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/nyc.osm.pbf"}, ways_file, - way_nodes_file, access_file, pronunciation_file); + way_nodes_file, access_file); PBFGraphParser::ParseRelations(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/nyc.osm.pbf"}, from_restriction_file, to_restriction_file, osmdata); PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/nyc.osm.pbf"}, - way_nodes_file, bss_nodes_file, osmdata); + way_nodes_file, bss_nodes_file, linguistic_node_file, osmdata); sequence way_nodes(way_nodes_file, false); way_nodes.sort(node_predicate); @@ -657,26 +625,7 @@ void BicycleTrafficSignals(const std::string& config_file) { << "Bike rental at a shop not marked as intersection." */ - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(bss_nodes_file); -} - -void DoConfig() { - std::ofstream file; - try { - file.open(config_file, std::ios_base::trunc); - file << "{ \ - \"mjolnir\": { \ - \"id_table_size\": 1000, \ - \"tile_dir\": \"test/data/parser_tiles\" \ - } \ - }"; - } catch (...) {} - file.close(); + CleanUp(); } TEST(GraphParser, TestBollardsGatesAndAccess) { @@ -721,19 +670,9 @@ TEST(GraphParser, TestImportBssNode) { conf.put("mjolnir.import_bike_share_stations", true); - std::string ways_file = "test_ways.bin"; - std::string way_nodes_file = "test_way_nodes.bin"; - std::string nodes_file = "test_nodes.bin"; - std::string edges_file = "test_edges.bin"; - std::string access_file = "test_access.bin"; - std::string pronunciation_file = "test_pronunciation.bin"; - std::string from_restriction_file = "test_from_complex_restrictions.bin"; - std::string to_restriction_file = "test_to_complex_restrictions.bin"; - std::string bss_nodes_file = "test_bss_nodes.bin"; - auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/rome.osm.pbf"}, ways_file, - way_nodes_file, access_file, pronunciation_file); + way_nodes_file, access_file); PBFGraphParser::ParseRelations(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/rome.osm.pbf"}, @@ -741,7 +680,7 @@ TEST(GraphParser, TestImportBssNode) { PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/rome.osm.pbf"}, way_nodes_file, - bss_nodes_file, osmdata); + bss_nodes_file, linguistic_node_file, osmdata); GraphReader reader(conf.get_child("mjolnir")); @@ -750,7 +689,7 @@ TEST(GraphParser, TestImportBssNode) { edges_file); GraphBuilder::Build(conf, osmdata, ways_file, way_nodes_file, nodes_file, edges_file, - from_restriction_file, to_restriction_file, pronunciation_file, tiles); + from_restriction_file, to_restriction_file, linguistic_node_file, tiles); BssBuilder::Build(conf, osmdata, bss_nodes_file); @@ -778,7 +717,7 @@ TEST(GraphParser, TestImportBssNode) { EXPECT_EQ(directededge->surface(), Surface::kPavedRough) << "The edges' surface is incorrect"; EXPECT_EQ(directededge->cyclelane(), CycleLane::kNone) << "The edges' cyclelane is incorrect"; EXPECT_EQ(directededge->classification(), RoadClass::kResidential) - << "The edges' road calss is incorrect"; + << "The edges' road class is incorrect"; EXPECT_EQ(directededge->use(), Use::kRoad) << "The edges' use is incorrect"; EdgeInfo edgeinfo = local_tile->edgeinfo(directededge); @@ -820,14 +759,7 @@ TEST(GraphParser, TestImportBssNode) { kPedestrianAccess); check_edge_attribute(local_tile->directededge(edge_idx_2 + count_2 - 2), kPedestrianAccess, kBicycleAccess); - - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(bss_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(bss_nodes_file); + CleanUp(); } } // namespace diff --git a/test/grid_traversal.cc b/test/grid_traversal.cc index 8fc06e06e9..f11996235c 100644 --- a/test/grid_traversal.cc +++ b/test/grid_traversal.cc @@ -1,5 +1,3 @@ -#include -#include #include #include "midgard/linesegment2.h" @@ -31,7 +29,7 @@ std::string result_to_str(const std::vector>& result) { void assert_equal_squares(const std::vector>& got, const std::vector>& expected, - const std::string& msg) { + const std::string& /*msg*/) { ASSERT_EQ(got, expected) << "got: " << result_to_str(got) << " expected: " << result_to_str(expected); @@ -64,7 +62,7 @@ TEST(GridTraversal, TestGridTraversal) { assert_equal_squares( grid.Traverse({0.3, 0.25}, {0.98, 0.83}), std::vector>{{0, 0}, {1, 0}, {1, 1}, {2, 1}, {2, 2}}, - "It should intersect sqaures from SQUARE(0 0) to SQUARE(2 2) in the lower side way"); + "It should intersect squares from SQUARE(0 0) to SQUARE(2 2) in the lower side way"); assert_equal_squares(grid.Traverse({-1, 0.1}, {2, 0.1}), std::vector>{{0, 0}, {1, 0}, {2, 0}}, diff --git a/test/gurka/CMakeLists.txt b/test/gurka/CMakeLists.txt index 767a1284df..7c516a1b29 100644 --- a/test/gurka/CMakeLists.txt +++ b/test/gurka/CMakeLists.txt @@ -6,19 +6,34 @@ if(ENABLE_DATA_TOOLS) add_custom_target(run-gurka) set_target_properties(run-gurka PROPERTIES FOLDER "Tests") - if (UNIX AND ENABLE_SINGLE_FILES_WERROR) - # Enables stricter compiler checks on a file-by-file basis - # which allows us to migrate piecemeal - set_source_files_properties( - test_route_incidents.cc - test_incident_loading.cc - test_closure_annotations.cc - PROPERTIES COMPILE_FLAGS "-Werror") - endif() + # source files that have warning, + # any other file not int this list will be processed with the + # "-Werror" flag + # Avoid adding a filename to this list + set(TESTS_WITH_WARNINGS + test_gtfs.cc + test_languages.cc + test_locate.cc + test_multi_level_loki.cc + test_osrm_serializer.cc + test_pbf_api.cc + test_phonemes.cc + test_phonemes_w_langs.cc + test_recost.cc + test_time_tracking.cc + ) ## Add executable targets foreach(FULLFILENAME IN ITEMS ${TEST_FILES}) file(RELATIVE_PATH FILENAME "${CMAKE_CURRENT_LIST_DIR}" ${FULLFILENAME}) + if (UNIX AND ENABLE_SINGLE_FILES_WERROR) + if (${FILENAME} IN_LIST TESTS_WITH_WARNINGS) + set_source_files_properties(${FILENAME} PROPERTIES COMPILE_FLAGS "-Wall") + else() + set_source_files_properties(${FILENAME} PROPERTIES COMPILE_FLAGS "-Wall -Werror") + endif() + endif() + string(REGEX REPLACE "test_(.*).cc" "gurka_\\1" TESTNAME ${FILENAME}) add_executable(${TESTNAME} EXCLUDE_FROM_ALL ${FILENAME}) set_target_properties(${TESTNAME} PROPERTIES FOLDER "Tests") @@ -27,7 +42,7 @@ if(ENABLE_DATA_TOOLS) VALHALLA_BUILD_DIR="${VALHALLA_BUILD_DIR}/") create_source_groups("Source Files" ${FILENAME}) target_link_libraries(${TESTNAME} valhalla_test) - if (LUAJIT_FOUND AND APPLE AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") + if (LuaJIT_FOUND AND APPLE AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") # Using LuaJIT on macOS on Intel processors requires a couple of extra linker flags target_link_options(${TESTNAME} PUBLIC -pagezero_size 10000 -image_base 100000000) endif() diff --git a/test/gurka/README.md b/test/gurka/README.md index f6830a2178..571f4ccbfe 100644 --- a/test/gurka/README.md +++ b/test/gurka/README.md @@ -222,7 +222,7 @@ low-level helper functions available in case you want to do something a little m - `gurka::detail::map_to_coordinates(ascii_map, gridsize);` - calculates coordinates for all the A-Za-z0-9 nodes in the `ascii_map` given the `gridsize` - `gurka::detail::build_pbf(node_locations, ways, nodes, relations, pbf_filename);` - generates an OSM PBF for the nodes, ways, and relations you've defined. The `nodemap` is the result of `gurka::detail::map_to_coordinates` -## Debuging help +## Debugging help You can print your gurka map and visually inspect it at [geojson.io](https://geojson.io/) by dumping out the geojson via `dump_geojson_graph`. diff --git a/test/gurka/gurka.cc b/test/gurka/gurka.cc index 4a883d1cc2..ca0f22bed6 100644 --- a/test/gurka/gurka.cc +++ b/test/gurka/gurka.cc @@ -2,7 +2,6 @@ #include "baldr/graphid.h" #include "baldr/graphreader.h" #include "baldr/rapidjson_utils.h" -#include "filesystem.h" #include "loki/worker.h" #include "midgard/constants.h" #include "midgard/encoded.h" @@ -29,6 +28,7 @@ #include #include +#include #include #include #include @@ -461,9 +461,9 @@ map buildtiles(const nodelayout& layout, throw std::runtime_error("Can't use / for tests, as we need to clean it out first"); } - if (filesystem::exists(workdir)) - filesystem::remove_all(workdir); - filesystem::create_directories(workdir); + if (std::filesystem::exists(workdir)) + std::filesystem::remove_all(workdir); + std::filesystem::create_directories(workdir); auto pbf_filename = workdir + "/map.pbf"; std::cerr << "[ ] generating map PBF at " << pbf_filename << std::endl; @@ -487,6 +487,8 @@ map buildtiles(const nodelayout& layout, * @param way_name the way name you want a directed edge for * @param end_node the node that should be the target of the directed edge you want * @param tile_id optional tile_id to limit the search to + * @param way_id optional way_id to limit the search to + * @param is_shortcut whether we want a shortcut returned * @return the directed edge that matches, or nullptr if there was no match */ std::tuple{tile_id} : reader.GetTileSet(); @@ -509,23 +513,49 @@ findEdge(valhalla::baldr::GraphReader& reader, // Iterate over all directed edges to find one with the name we want for (uint32_t i = 0; i < tile->header()->directededgecount(); i++) { const auto* forward_directed_edge = tile->directededge(i); + // if we requested a shortcut, but it's not, bail + if (forward_directed_edge->is_shortcut() != is_shortcut) { + continue; + } // Now, see if the endnode for this edge is our end_node auto de_endnode = forward_directed_edge->endnode(); graph_tile_ptr reverse_tile = tile; auto de_endnode_coordinates = reader.GetGraphTile(de_endnode, reverse_tile)->get_node_ll(de_endnode); + const auto threshold = 0.00001; // Degrees. About 1m at the equator if (std::abs(de_endnode_coordinates.lng() - end_node_coordinates.lng()) < threshold && std::abs(de_endnode_coordinates.lat() - end_node_coordinates.lat()) < threshold) { - auto names = tile->GetNames(forward_directed_edge); - for (const auto& name : names) { - if (name == way_name) { - auto forward_edge_id = tile_id; - forward_edge_id.set_id(i); - GraphId reverse_edge_id = reader.GetOpposingEdgeId(forward_edge_id, reverse_tile); - auto* reverse_directed_edge = reverse_tile->directededge(reverse_edge_id.id()); - return std::make_tuple(forward_edge_id, forward_directed_edge, reverse_edge_id, - reverse_directed_edge); + + if (way_name.empty()) { + if (way_id != 0) { + if (tile->edgeinfo(forward_directed_edge).wayid() == way_id) { + + // Skip any edges that are not drivable inbound. + if (!(forward_directed_edge->forwardaccess() & kVehicularAccess)) + continue; + + auto forward_edge_id = tile_id; + forward_edge_id.set_id(i); + graph_tile_ptr reverse_tile = nullptr; + GraphId reverse_edge_id = reader.GetOpposingEdgeId(forward_edge_id, reverse_tile); + auto* reverse_directed_edge = reverse_tile->directededge(reverse_edge_id.id()); + return std::make_tuple(forward_edge_id, forward_directed_edge, reverse_edge_id, + reverse_directed_edge); + } + } + } else { + auto names = tile->GetNames(forward_directed_edge); + for (const auto& name : names) { + if (name == way_name) { + auto forward_edge_id = tile_id; + forward_edge_id.set_id(i); + graph_tile_ptr reverse_tile = nullptr; + GraphId reverse_edge_id = reader.GetOpposingEdgeId(forward_edge_id, reverse_tile); + auto* reverse_directed_edge = reverse_tile->directededge(reverse_edge_id.id()); + return std::make_tuple(forward_edge_id, forward_directed_edge, reverse_edge_id, + reverse_directed_edge); + } } } } @@ -543,7 +573,7 @@ findEdge(valhalla::baldr::GraphReader& reader, * @param end_node_name name of the end node * @return the edge_id and its edge */ -std::tuple +std::tuple findEdgeByNodes(valhalla::baldr::GraphReader& reader, const nodelayout& nodes, const std::string& begin_node_name, diff --git a/test/gurka/gurka.h b/test/gurka/gurka.h index c8fc182d23..fa163eeb0f 100644 --- a/test/gurka/gurka.h +++ b/test/gurka/gurka.h @@ -12,7 +12,6 @@ #include "baldr/graphid.h" #include "baldr/graphreader.h" #include "baldr/rapidjson_utils.h" -#include "filesystem.h" #include "loki/worker.h" #include "midgard/constants.h" #include "midgard/encoded.h" @@ -26,7 +25,6 @@ #include "tyr/actor.h" #include "tyr/serializers.h" -#include #include #include @@ -36,7 +34,6 @@ #include #include -#include #include #include @@ -150,6 +147,7 @@ map buildtiles(const nodelayout& layout, * @param way_name the way name you want a directed edge for * @param end_node the node that should be the target of the directed edge you want * @param tile_id optional tile_id to limit the search to + * @param way_id optional way_id to limit the search to * @return the directed edge that matches, or nullptr if there was no match */ std::tuple +std::tuple findEdgeByNodes(valhalla::baldr::GraphReader& reader, const nodelayout& nodes, const std::string& begin_node_name, diff --git a/test/gurka/test_64bit_wayid.cc b/test/gurka/test_64bit_wayid.cc index 552960d48b..ff4e4637e3 100644 --- a/test/gurka/test_64bit_wayid.cc +++ b/test/gurka/test_64bit_wayid.cc @@ -7,13 +7,17 @@ void find_ids(baldr::GraphReader& reader, std::multiset osm_way_ids) { // check all edges have correct edge info for (const auto& tile_id : reader.GetTileSet()) { auto tile = reader.GetGraphTile(tile_id); - for (auto edge = tile_id; edge.id() < tile->header()->directededgecount(); ++edge) { - // we should find every way id in the tile set - auto info = tile->edgeinfo(tile->directededge(edge)); + for (auto edge_id = tile_id; edge_id.id() < tile->header()->directededgecount(); ++edge_id) { + // we should find every way id in the tile set, unless it's a shortcut, no way id there + auto* edge = tile->directededge(edge_id); + if (edge->is_shortcut()) { + continue; + } + auto info = tile->edgeinfo(edge); auto id = info.wayid(); auto found = osm_way_ids.find(id); if (found == osm_way_ids.cend()) { - EXPECT_FALSE(found == osm_way_ids.cend()) << " couldnt find " << info.wayid(); + ASSERT_FALSE(found == osm_way_ids.cend()) << " couldnt find " << info.wayid(); return; } osm_way_ids.erase(found); @@ -72,7 +76,7 @@ TEST(WayIds, way_ids1) { }; // keep one for each directed edge std::multiset osm_way_ids; - for (int i = 0; i < ids.size(); ++i) { + for (size_t i = 0; i < ids.size(); ++i) { const auto& id = ids[i]; osm_way_ids.emplace(std::stoull(id)); osm_way_ids.emplace(std::stoull(id)); diff --git a/test/gurka/test_access.cc b/test/gurka/test_access.cc new file mode 100644 index 0000000000..54cbfca82c --- /dev/null +++ b/test/gurka/test_access.cc @@ -0,0 +1,625 @@ +#include "gurka.h" +#include "test.h" +#include + +#if !defined(VALHALLA_SOURCE_DIR) +#define VALHALLA_SOURCE_DIR +#endif + +using namespace valhalla; + +const std::unordered_map build_config{ + {"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/netherlands_admin.sqlite"}}}; + +const std::vector& costing = {"auto", "taxi", "bus", "truck", + "bicycle", "motor_scooter", "motorcycle", "pedestrian"}; + +TEST(Standalone, AccessPsvWay) { + constexpr double gridsize_metres = 10; + + const std::string ascii_map = R"( + L + | + | + A---B---C---D---E---I---J + | | | + F-------G-------K + | + H + + M------N------O + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "primary"}}}, + {"BC", {{"highway", "primary"}}}, + {"CD", {{"highway", "primary"}}}, + {"DE", {{"highway", "primary"}}}, + {"EG", {{"highway", "primary"}}}, + {"FG", {{"highway", "primary"}}}, + {"CF", + { + {"highway", "primary"}, + {"access", "psv"}, // access key wins over bus or taxi tag + {"bike", "no"}, + {"bus", "no"}, + }}, + {"FH", {{"highway", "primary"}}}, + {"EI", {{"highway", "bus_guideway"}}}, + {"JI", {{"highway", "busway"}}}, + {"GK", {{"highway", "primary"}}}, + {"KJ", {{"highway", "primary"}}}, + {"LI", {{"highway", "primary"}}}, + {"MN", {{"highway", "residential"}, {"access", "no"}, {"bus", "permit"}, {"taxi", "permit"}}}, + {"NO", {{"highway", "residential"}, {"access", "no"}, {"bus", "permit"}, {"taxi", "permit"}}}, + }; + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {5.1079374, 52.0887174}); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_access_psv_way", build_config); + for (auto& c : costing) { + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "H"}, c); + + if (c == "bus" || c == "taxi") + gurka::assert::raw::expect_path(result, {"AB", "BC", "CF", "FH"}); + else + gurka::assert::raw::expect_path(result, {"AB", "BC", "CD", "DE", "EG", "FG", "FH"}); + } + + for (auto& c : costing) { + auto result = gurka::do_action(valhalla::Options::route, map, {"D", "J"}, c); + + if (c == "bus") + gurka::assert::raw::expect_path(result, {"DE", "EI", "JI"}); + else + gurka::assert::raw::expect_path(result, {"DE", "EG", "GK", "KJ"}); + } + + for (auto& c : costing) { + if (c == "bus") + EXPECT_NO_THROW(gurka::do_action(valhalla::Options::route, map, {"D", "L"}, c)); + else + EXPECT_THROW(gurka::do_action(valhalla::Options::route, map, {"D", "L"}, c), + std::runtime_error); + } + + // Test bus=permit overriding access=no + auto result = gurka::do_action(valhalla::Options::route, map, {"M", "O"}, "bus"); + gurka::assert::raw::expect_path(result, {"MN", "NO"}); + + // Test taxi=permit overriding access=no + result = gurka::do_action(valhalla::Options::route, map, {"M", "O"}, "taxi"); + gurka::assert::raw::expect_path(result, {"MN", "NO"}); +} + +TEST(Standalone, AccessPsvNode) { + constexpr double gridsize_metres = 10; + + const std::string ascii_map = R"( + + A---B---C---D---E + | | + F | + | | + H-------G + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "primary"}}}, {"BC", {{"highway", "primary"}}}, + {"CD", {{"highway", "primary"}}}, {"DE", {{"highway", "primary"}}}, + {"EG", {{"highway", "primary"}}}, {"HG", {{"highway", "primary"}}}, + {"CF", {{"highway", "primary"}}}, {"FH", {{"highway", "primary"}}}, + + }; + + const gurka::nodes nodes = {{"F", + { + {"access", "psv"}, // access tag wins over bus or taxi tag + {"taxi", "no"}, + {"bus", "no"}, + }}}; + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {5.1079374, 52.0887174}); + auto map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/gurka_access_psv_way", build_config); + for (auto& c : costing) { + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "H"}, c); + + if (c == "bus" || c == "taxi") + gurka::assert::raw::expect_path(result, {"AB", "BC", "CF", "FH"}); + else + gurka::assert::raw::expect_path(result, {"AB", "BC", "CD", "DE", "EG", "HG"}); + } +} + +class Accessibility : public ::testing::Test { +protected: + static gurka::map map; + + static void SetUpTestSuite() { + + const std::string ascii_map = R"( + A----B----C + | . + D----E----F + | . \ + G----H----I)"; + + // BE and EH are highway=path, so no cars + // EI is a shortcut that's not accessible to bikes + const gurka::ways ways = {{"AB", {{"highway", "primary"}}}, + {"BC", {{"highway", "primary"}}}, + {"DEF", {{"highway", "primary"}}}, + {"GHI", {{"highway", "primary"}}}, + {"ADG", {{"highway", "motorway"}}}, + {"BE", {{"highway", "path"}}}, + {"EI", {{"highway", "path"}, {"bicycle", "no"}}}, + {"EH", {{"highway", "path"}}}}; + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); + + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/accessibility"); + } +}; + +gurka::map Accessibility::map = {}; + +/*************************************************************/ +TEST_F(Accessibility, Auto1) { + auto result = gurka::do_action(valhalla::Options::route, map, {"C", "F"}, "auto"); + gurka::assert::osrm::expect_steps(result, {"BC", "ADG", "DEF"}); + gurka::assert::raw::expect_path(result, {"BC", "AB", "ADG", "DEF", "DEF"}); +} +TEST_F(Accessibility, Auto2) { + auto result = gurka::do_action(valhalla::Options::route, map, {"C", "I"}, "auto"); + gurka::assert::osrm::expect_steps(result, {"BC", "ADG", "GHI"}); + gurka::assert::raw::expect_path(result, {"BC", "AB", "ADG", "ADG", "GHI", "GHI"}); +} +TEST_F(Accessibility, WalkUsesShortcut1) { + auto result = gurka::do_action(valhalla::Options::route, map, {"C", "F"}, "pedestrian"); + gurka::assert::osrm::expect_steps(result, {"BC", "BE", "DEF"}); + gurka::assert::raw::expect_path(result, {"BC", "BE", "DEF"}); +} +TEST_F(Accessibility, WalkUsesBothShortcuts) { + auto result = gurka::do_action(valhalla::Options::route, map, {"C", "I"}, "pedestrian"); + gurka::assert::osrm::expect_steps(result, {"BC", "BE", "EI"}); + gurka::assert::raw::expect_path(result, {"BC", "BE", "EI"}); +} +TEST_F(Accessibility, BikeUsesShortcut) { + auto result = gurka::do_action(valhalla::Options::route, map, {"C", "F"}, "bicycle"); + gurka::assert::osrm::expect_steps(result, {"BC", "BE", "DEF"}); + gurka::assert::raw::expect_path(result, {"BC", "BE", "DEF"}); +} +TEST_F(Accessibility, BikeAvoidsSecondShortcut) { + auto result = gurka::do_action(valhalla::Options::route, map, {"C", "I"}, "bicycle"); + gurka::assert::osrm::expect_steps(result, {"BC", "BE", "GHI"}); + gurka::assert::raw::expect_path(result, {"BC", "BE", "EH", "GHI"}); +} +TEST_F(Accessibility, WalkAvoidsMotorway) { + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, "pedestrian"); + gurka::assert::osrm::expect_steps(result, {"AB", "BE", "GHI"}); + gurka::assert::raw::expect_path(result, {"AB", "BE", "EH", "GHI"}); +} +TEST_F(Accessibility, AutoUsesMotorway) { + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, "auto"); + gurka::assert::osrm::expect_steps(result, {"ADG"}); + gurka::assert::raw::expect_path(result, {"ADG", "ADG"}); +} + +class MultipleBarriers : public ::testing::Test { +protected: + static gurka::nodelayout layout; + static gurka::ways ways; + static void SetUpTestSuite() { + constexpr double gridsize = 100; + + const std::string ascii_map = R"( + C-----2----D + | | + A-----1----B + )"; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + + ways = { + {"AC2", {{"highway", "primary"}}}, + {"2DB", {{"highway", "primary"}}}, + {"A1", {{"highway", "primary"}}}, + {"1B", {{"highway", "primary"}}}, + }; + } + + void check_auto_path(const gurka::map& map, const std::vector& expected_path) { + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "auto"); + gurka::assert::raw::expect_path(result, expected_path); + } +}; + +gurka::nodelayout MultipleBarriers::layout = {}; +gurka::ways MultipleBarriers::ways = {}; + +TEST_F(MultipleBarriers, DeniedBarrierAccess) { + const gurka::nodes nodes = { + {"1", {{"barrier", "gate"}, {"access", "no"}}}, + }; + const gurka::map map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_no_access"); + check_auto_path(map, {"AC2", "2DB"}); +} + +TEST_F(MultipleBarriers, AllowedBarrierAccess) { + const gurka::nodes nodes = { + {"1", {{"barrier", "gate"}, {"access", "yes"}}}, + }; + const gurka::map map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_allowed_access"); + check_auto_path(map, {"A1", "1B"}); +} + +TEST_F(MultipleBarriers, AllowedVehicleBarrierAccess) { + const gurka::nodes nodes = { + {"1", {{"barrier", "gate"}, {"motor_vehicle", "yes"}}}, + }; + const gurka::map map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_vehicle_allowed"); + check_auto_path(map, {"A1", "1B"}); +} + +TEST_F(MultipleBarriers, NoInfoBarrierAccess) { + const gurka::nodes nodes = { + {"1", {{"barrier", "gate"}}}, + }; + const gurka::map map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_no_access_info"); + check_auto_path(map, {"AC2", "2DB"}); +} + +TEST_F(MultipleBarriers, TwoBarriers) { + const gurka::nodes nodes = { + {"1", {{"barrier", "gate"}}}, + {"2", {{"barrier", "gate"}}}, + }; + const gurka::map map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_two_gates"); + check_auto_path(map, {"A1", "1B"}); +} + +TEST_F(MultipleBarriers, ClosedLongRoute) { + const gurka::nodes nodes = { + {"1", {{"barrier", "gate"}, {"motor_vehicle", "no"}}}, + {"2", {{"barrier", "gate"}}}, + }; + const gurka::map map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_closed_long"); + check_auto_path(map, {"AC2", "2DB"}); +} + +TEST_F(MultipleBarriers, ClosedShortRoute) { + const gurka::nodes nodes = { + {"1", {{"barrier", "gate"}}}, + {"2", {{"barrier", "gate"}, {"motor_vehicle", "no"}}}, + }; + const gurka::map map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_closed_short"); + check_auto_path(map, {"A1", "1B"}); +} + +TEST_F(MultipleBarriers, BothClosed) { + const gurka::nodes nodes = { + {"1", {{"barrier", "gate"}, {"motor_vehicle", "no"}}}, + {"2", {{"barrier", "gate"}, {"access", "no"}}}, + }; + const gurka::map map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_both_closed"); + try { + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "auto"); + gurka::assert::raw::expect_path(result, {"Unexpected path found"}); + } catch (const std::runtime_error& e) { + EXPECT_STREQ(e.what(), "No path could be found for input"); + } +} + +TEST_F(MultipleBarriers, BothPrivate) { + const gurka::nodes nodes = { + {"1", {{"barrier", "gate"}, {"access", "private"}}}, + {"2", {{"barrier", "gate"}, {"access", "private"}}}, + }; + const gurka::map map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_both_private"); + check_auto_path(map, {"A1", "1B"}); +} + +TEST_F(MultipleBarriers, ShortestPrivate) { + const gurka::nodes nodes = { + {"1", {{"barrier", "gate"}, {"access", "private"}}}, + {"2", {{"barrier", "gate"}, {"access", "yes"}}}, + }; + const gurka::map map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_one_private"); + check_auto_path(map, {"AC2", "2DB"}); +} + +TEST_F(MultipleBarriers, BollardPrivate) { + const gurka::nodes nodes = { + {"1", {{"barrier", "bollard"}, {"access", "private"}}}, + {"2", {{"barrier", "bollard"}, {"access", "yes"}}}, + }; + const gurka::map map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_bollard_one_private"); + check_auto_path(map, {"AC2", "2DB"}); +} + +TEST_F(MultipleBarriers, BollardPrivateMotorVehicle) { + const gurka::nodes nodes = { + {"1", {{"barrier", "bollard"}, {"motor_vehicle", "private"}}}, + {"2", {{"barrier", "bollard"}, {"access", "yes"}}}, + }; + const gurka::map map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_bollard_private_motor_vehicle"); + check_auto_path(map, {"AC2", "2DB"}); +} + +TEST_F(MultipleBarriers, BollardPrivateAndNoInfo) { + const gurka::nodes nodes = { + {"1", {{"barrier", "bollard"}, {"motor_vehicle", "private"}}}, + {"2", {{"barrier", "bollard"}}}, + }; + const gurka::map map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_bollard_private_no_info"); + check_auto_path(map, {"A1", "1B"}); +} + +TEST_F(MultipleBarriers, BollardBothPrivate) { + const gurka::nodes nodes = { + {"1", {{"barrier", "bollard"}, {"access", "private"}}}, + {"2", {{"barrier", "bollard"}, {"access", "private"}}}, + }; + const gurka::map map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_bollard_both_private"); + check_auto_path(map, {"A1", "1B"}); +} + +TEST_F(MultipleBarriers, BollardNoAccessInformation) { + const gurka::nodes nodes = { + {"1", {{"barrier", "bollard"}, {"access", "no"}}}, + {"2", {{"barrier", "bollard"}}}, + }; + const gurka::map map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_bollard_no_access_info"); + try { + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "auto"); + gurka::assert::raw::expect_path(result, {"Unexpected path found"}); + } catch (const std::runtime_error& e) { + EXPECT_STREQ(e.what(), "No path could be found for input"); + } +} + +class AccessibleBarriers : public ::testing::Test { +protected: + static gurka::map map; + + static void SetUpTestSuite() { + constexpr double gridsize = 100; + + const std::string ascii_map = R"( + B-----1---C + | \ + | \ + M-----2------N + | \ + A-----3--------D + | / + F-----4------E + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "primary"}}}, {"B1C", {{"highway", "primary"}}}, + {"CD", {{"highway", "primary"}}}, {"AM", {{"highway", "primary"}}}, + {"M2", {{"highway", "primary"}}}, {"2N", {{"highway", "primary"}}}, + {"ND", {{"highway", "primary"}}}, {"A3D", {{"highway", "primary"}}}, + {"AF", {{"highway", "primary"}}}, {"F4", {{"highway", "primary"}}}, + {"4E", {{"highway", "primary"}}}, {"ED", {{"highway", "primary"}}}, + }; + + const gurka::nodes nodes = { + // gate is opened + {"1", {{"barrier", "gate"}, {"access", "yes"}}}, + // access is not specified, huge penalty is added. + {"2", {{"barrier", "gate"}}}, + // access is private. Penalty is added because of privateness. + {"3", {{"barrier", "gate"}, {"access", "private"}}}, + // gate is opened for bicycle only. + {"4", {{"barrier", "lift_gate"}, {"access", "no"}, {"bicycle", "yes"}}}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + map = gurka::buildtiles(layout, ways, nodes, {}, "test/data/accessible_barriers"); + } +}; + +gurka::map AccessibleBarriers::map = {}; + +TEST_F(AccessibleBarriers, Auto) { + const std::string cost = "auto"; + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, cost); + gurka::assert::raw::expect_path(result, {"AB", "B1C", "B1C", "CD"}); +} + +TEST_F(AccessibleBarriers, Bicycle) { + const std::string cost = "bicycle"; + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, cost); + gurka::assert::raw::expect_path(result, {"AF", "F4", "4E", "ED"}); +} + +TEST_F(AccessibleBarriers, Pedestrian) { + const std::string cost = "pedestrian"; + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, cost); + gurka::assert::raw::expect_path(result, {"AM", "M2", "2N", "ND"}); +} + +// Check that MTB tags override SAC scale and allow bicycle access +class MtbAccess : public ::testing::Test { +protected: + static gurka::map map; + + static void SetUpTestSuite() { + + // A--B + const std::string ascii_map = R"(A----B----C)"; + const gurka::ways ways = {{"AB", + {{"highway", "cycleway"}, + {"sac_scale", "mountain_hiking"}, + {"mtb:scale:uphill", "2"}, + {"foot", "designated"}}}, + {"BC", + {{"highway", "cycleway"}, + {"sac_scale", "mountain_hiking"}, + {"mtb:scale:uphill", "2"}, + {"foot", "designated"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/mtb_access"); + } +}; + +gurka::map MtbAccess::map = {}; + +/*************************************************************/ + +TEST_F(MtbAccess, CheckMtbAccess) { + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "C"}, "bicycle"); + gurka::assert::osrm::expect_steps(result, {"AB"}); + gurka::assert::raw::expect_path(result, {"AB", "BC"}); +} + +void validate_path(const valhalla::Api& result, const std::vector& expected_names) { + ASSERT_EQ(result.trip().routes(0).legs_size(), 1); + auto leg = result.trip().routes(0).legs(0); + gurka::assert::raw::expect_path(result, expected_names); +} + +TEST(Standalone, NodeAccess) { + const std::string ascii_map = R"( + A----B----C----D------------E + | | | + F G H + | | | + I----J----K------------L + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "residential"}}}, {"BC", {{"highway", "residential"}}}, + {"CD", {{"highway", "residential"}}}, {"DE", {{"highway", "residential"}}}, + {"BF", {{"highway", "residential"}}}, {"DG", {{"highway", "residential"}}}, + {"EH", {{"highway", "residential"}}}, {"FI", {{"highway", "residential"}}}, + {"GK", {{"highway", "residential"}}}, {"HL", {{"highway", "residential"}}}, + {"IJ", {{"highway", "residential"}}}, {"JK", {{"highway", "residential"}}}, + {"KL", {{"highway", "residential"}}}, + }; + + const gurka::nodes nodes = {{"F", {{"motor_vehicle", "no"}}}, {"G", {{"motorcar", "no"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); + auto map = gurka::buildtiles(layout, ways, nodes, {}, "test/data/gurka_node_access"); + + for (auto& c : costing) { + if (c == "auto" || c == "taxi") + validate_path(gurka::do_action(valhalla::Options::route, map, {"A", "I"}, c), + {"AB", "BC", "CD", "DE", "EH", "HL", "KL", "JK", "IJ"}); + else if (c == "bicycle" || c == "pedestrian") + validate_path(gurka::do_action(valhalla::Options::route, map, {"A", "I"}, c), + {"AB", "BF", "FI"}); + else + validate_path(gurka::do_action(valhalla::Options::route, map, {"A", "I"}, c), + {"AB", "BC", "CD", "DG", "GK", "JK", "IJ"}); + } +} + +TEST(Standalone, RouteOnPrivateAccess) { + constexpr double gridsize_metres = 10; + + const std::string ascii_map = R"( + A---B---C---D + | | | + E F G + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "primary"}}}, + {"BC", {{"highway", "primary"}}}, + {"CD", {{"highway", "primary"}}}, + {"BE", {{"highway", "service"}, {"access", "private"}}}, + {"CF", {{"highway", "service"}, {"access", "private"}, {"service", "driveway"}}}, + {"DG", {{"highway", "service"}, {"access", "private"}, {"service", "parking_aisle"}}}, + }; + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {5.1079374, 52.0887174}); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_route_on_private_access", + build_config); + + for (auto& c : costing) { + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "E"}, c); + gurka::assert::raw::expect_path(result, {"AB", "BE"}); + + result = gurka::do_action(valhalla::Options::route, map, {"A", "F"}, c); + gurka::assert::raw::expect_path(result, {"AB", "BC", "CF"}); + + result = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, c); + gurka::assert::raw::expect_path(result, {"AB", "BC", "CD", "DG"}); + } +} + +TEST(Standalone, AccessForwardBackward) { + constexpr double gridsize_metres = 10; + + const std::string ascii_map = R"( + A--B--C-D-E + | | + F | + | | + H---G + )"; + + const gurka::ways ways = { + {"ABCDE", {{"highway", "primary"}}}, + {"CFH", + {{"highway", "primary"}, + {"motor_vehicle:forward", "no"}, + {"vehicle:backward", "yes"}, + {"foot:forward", "no"}, + {"foot:backward", "yes"}, + {"bicycle:forward", "no"}}}, + {"HG", {{"highway", "primary"}}}, + {"EG", + {{"highway", "primary"}, + {"vehicle:forward", "yes"}, + {"motor_vehicle:backward", "no"}, + {"foot:forward", "yes"}, + {"foot:backward", "no"}, + {"bicycle:backward", "no"}}}, + }; + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {5.1079374, 52.0887174}); + auto map = + gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_access_motor_vehicle", build_config); + + for (auto& c : costing) { + // no problem forward for everyone + auto result = gurka::do_action(valhalla::Options::route, map, {"D", "G"}, c); + gurka::assert::raw::expect_path(result, {"ABCDE", "EG"}); + + // reverse need to go around + result = gurka::do_action(valhalla::Options::route, map, {"G", "D"}, c); + gurka::assert::raw::expect_path(result, {"HG", "CFH", "ABCDE"}); + + // no problem reverse for everyone + result = gurka::do_action(valhalla::Options::route, map, {"G", "F"}, c); + gurka::assert::raw::expect_path(result, {"HG", "CFH"}); + + // forward need to go around + result = gurka::do_action(valhalla::Options::route, map, {"F", "G"}, c); + gurka::assert::raw::expect_path(result, {"CFH", "ABCDE", "EG"}); + } +} diff --git a/test/gurka/test_access_psv.cc b/test/gurka/test_access_psv.cc deleted file mode 100644 index e8bfe93069..0000000000 --- a/test/gurka/test_access_psv.cc +++ /dev/null @@ -1,133 +0,0 @@ -#include "gurka.h" -#include - -#if !defined(VALHALLA_SOURCE_DIR) -#define VALHALLA_SOURCE_DIR -#endif - -using namespace valhalla; - -const std::unordered_map build_config{ - {"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/netherlands_admin.sqlite"}}}; - -const std::vector& costing = {"auto", "taxi", "bus", "truck", - "bicycle", "motor_scooter", "motorcycle", "pedestrian"}; - -TEST(Standalone, AccessPsvWay) { - constexpr double gridsize_metres = 10; - - const std::string ascii_map = R"( - L - | - | - A---B---C---D---E---I---J - | | | - F-------G-------K - | - H - - M------N------O - )"; - - const gurka::ways ways = { - {"AB", {{"highway", "primary"}}}, - {"BC", {{"highway", "primary"}}}, - {"CD", {{"highway", "primary"}}}, - {"DE", {{"highway", "primary"}}}, - {"EG", {{"highway", "primary"}}}, - {"FG", {{"highway", "primary"}}}, - {"CF", - { - {"highway", "primary"}, - {"access", "psv"}, // access key wins over bus or taxi tag - {"bike", "no"}, - {"bus", "no"}, - }}, - {"FH", {{"highway", "primary"}}}, - {"EI", {{"highway", "bus_guideway"}}}, - {"JI", {{"highway", "busway"}}}, - {"GK", {{"highway", "primary"}}}, - {"KJ", {{"highway", "primary"}}}, - {"LI", {{"highway", "primary"}}}, - {"MN", {{"highway", "residential"}, {"access", "no"}, {"bus", "permit"}, {"taxi", "permit"}}}, - {"NO", {{"highway", "residential"}, {"access", "no"}, {"bus", "permit"}, {"taxi", "permit"}}}, - }; - - const auto layout = - gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {5.1079374, 52.0887174}); - auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_access_psv_way", build_config); - for (auto& c : costing) { - auto result = gurka::do_action(valhalla::Options::route, map, {"A", "H"}, c); - - if (c == "bus" || c == "taxi") - gurka::assert::raw::expect_path(result, {"AB", "BC", "CF", "FH"}); - else - gurka::assert::raw::expect_path(result, {"AB", "BC", "CD", "DE", "EG", "FG", "FH"}); - } - - for (auto& c : costing) { - auto result = gurka::do_action(valhalla::Options::route, map, {"D", "J"}, c); - - if (c == "bus") - gurka::assert::raw::expect_path(result, {"DE", "EI", "JI"}); - else - gurka::assert::raw::expect_path(result, {"DE", "EG", "GK", "KJ"}); - } - - for (auto& c : costing) { - if (c == "bus") - EXPECT_NO_THROW(gurka::do_action(valhalla::Options::route, map, {"D", "L"}, c)); - else - EXPECT_THROW(gurka::do_action(valhalla::Options::route, map, {"D", "L"}, c), - std::runtime_error); - } - - // Test bus=permit overriding access=no - auto result = gurka::do_action(valhalla::Options::route, map, {"M", "O"}, "bus"); - gurka::assert::raw::expect_path(result, {"MN", "NO"}); - - // Test taxi=permit overriding access=no - result = gurka::do_action(valhalla::Options::route, map, {"M", "O"}, "taxi"); - gurka::assert::raw::expect_path(result, {"MN", "NO"}); -} - -TEST(Standalone, AccessPsvNode) { - constexpr double gridsize_metres = 10; - - const std::string ascii_map = R"( - - A---B---C---D---E - | | - F | - | | - H-------G - )"; - - const gurka::ways ways = { - {"AB", {{"highway", "primary"}}}, {"BC", {{"highway", "primary"}}}, - {"CD", {{"highway", "primary"}}}, {"DE", {{"highway", "primary"}}}, - {"EG", {{"highway", "primary"}}}, {"HG", {{"highway", "primary"}}}, - {"CF", {{"highway", "primary"}}}, {"FH", {{"highway", "primary"}}}, - - }; - - const gurka::nodes nodes = {{"F", - { - {"access", "psv"}, // access tag wins over bus or taxi tag - {"taxi", "no"}, - {"bus", "no"}, - }}}; - - const auto layout = - gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {5.1079374, 52.0887174}); - auto map = - gurka::buildtiles(layout, ways, nodes, {}, "test/data/gurka_access_psv_way", build_config); - for (auto& c : costing) { - auto result = gurka::do_action(valhalla::Options::route, map, {"A", "H"}, c); - - if (c == "bus" || c == "taxi") - gurka::assert::raw::expect_path(result, {"AB", "BC", "CF", "FH"}); - else - gurka::assert::raw::expect_path(result, {"AB", "BC", "CD", "DE", "EG", "HG"}); - } -} diff --git a/test/gurka/test_accessibility.cc b/test/gurka/test_accessibility.cc deleted file mode 100644 index cb2f25d190..0000000000 --- a/test/gurka/test_accessibility.cc +++ /dev/null @@ -1,78 +0,0 @@ -#include "gurka.h" -#include - -using namespace valhalla; - -class Accessibility : public ::testing::Test { -protected: - static gurka::map map; - - static void SetUpTestSuite() { - constexpr double gridsize = 100; - - const std::string ascii_map = R"( - A----B----C - | . - D----E----F - | . \ - G----H----I)"; - - // BE and EH are highway=path, so no cars - // EI is a shortcut that's not accessible to bikes - const gurka::ways ways = {{"AB", {{"highway", "primary"}}}, - {"BC", {{"highway", "primary"}}}, - {"DEF", {{"highway", "primary"}}}, - {"GHI", {{"highway", "primary"}}}, - {"ADG", {{"highway", "motorway"}}}, - {"BE", {{"highway", "path"}}}, - {"EI", {{"highway", "path"}, {"bicycle", "no"}}}, - {"EH", {{"highway", "path"}}}}; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); - - map = gurka::buildtiles(layout, ways, {}, {}, "test/data/accessibility"); - } -}; - -gurka::map Accessibility::map = {}; - -/*************************************************************/ -TEST_F(Accessibility, Auto1) { - auto result = gurka::do_action(valhalla::Options::route, map, {"C", "F"}, "auto"); - gurka::assert::osrm::expect_steps(result, {"BC", "ADG", "DEF"}); - gurka::assert::raw::expect_path(result, {"BC", "AB", "ADG", "DEF", "DEF"}); -} -TEST_F(Accessibility, Auto2) { - auto result = gurka::do_action(valhalla::Options::route, map, {"C", "I"}, "auto"); - gurka::assert::osrm::expect_steps(result, {"BC", "ADG", "GHI"}); - gurka::assert::raw::expect_path(result, {"BC", "AB", "ADG", "ADG", "GHI", "GHI"}); -} -TEST_F(Accessibility, WalkUsesShortcut1) { - auto result = gurka::do_action(valhalla::Options::route, map, {"C", "F"}, "pedestrian"); - gurka::assert::osrm::expect_steps(result, {"BC", "BE", "DEF"}); - gurka::assert::raw::expect_path(result, {"BC", "BE", "DEF"}); -} -TEST_F(Accessibility, WalkUsesBothShortcuts) { - auto result = gurka::do_action(valhalla::Options::route, map, {"C", "I"}, "pedestrian"); - gurka::assert::osrm::expect_steps(result, {"BC", "BE", "EI"}); - gurka::assert::raw::expect_path(result, {"BC", "BE", "EI"}); -} -TEST_F(Accessibility, BikeUsesShortcut) { - auto result = gurka::do_action(valhalla::Options::route, map, {"C", "F"}, "bicycle"); - gurka::assert::osrm::expect_steps(result, {"BC", "BE", "DEF"}); - gurka::assert::raw::expect_path(result, {"BC", "BE", "DEF"}); -} -TEST_F(Accessibility, BikeAvoidsSecondShortcut) { - auto result = gurka::do_action(valhalla::Options::route, map, {"C", "I"}, "bicycle"); - gurka::assert::osrm::expect_steps(result, {"BC", "BE", "GHI"}); - gurka::assert::raw::expect_path(result, {"BC", "BE", "EH", "GHI"}); -} -TEST_F(Accessibility, WalkAvoidsMotorway) { - auto result = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, "pedestrian"); - gurka::assert::osrm::expect_steps(result, {"AB", "BE", "GHI"}); - gurka::assert::raw::expect_path(result, {"AB", "BE", "EH", "GHI"}); -} -TEST_F(Accessibility, AutoUsesMotorway) { - auto result = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, "auto"); - gurka::assert::osrm::expect_steps(result, {"ADG"}); - gurka::assert::raw::expect_path(result, {"ADG", "ADG"}); -} diff --git a/test/gurka/test_accessible_barrier.cc b/test/gurka/test_accessible_barrier.cc deleted file mode 100644 index 128564c37c..0000000000 --- a/test/gurka/test_accessible_barrier.cc +++ /dev/null @@ -1,255 +0,0 @@ -#include "gurka.h" -#include "test.h" - -using namespace valhalla; - -class MultipleBarriers : public ::testing::Test { -protected: - static gurka::nodelayout layout; - static gurka::ways ways; - static void SetUpTestSuite() { - constexpr double gridsize = 100; - - const std::string ascii_map = R"( - C-----2----D - | | - A-----1----B - )"; - - layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); - - ways = { - {"AC2", {{"highway", "primary"}}}, - {"2DB", {{"highway", "primary"}}}, - {"A1", {{"highway", "primary"}}}, - {"1B", {{"highway", "primary"}}}, - }; - } - - void check_auto_path(const gurka::map& map, const std::vector& expected_path) { - auto result = gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "auto"); - gurka::assert::raw::expect_path(result, expected_path); - } -}; - -gurka::nodelayout MultipleBarriers::layout = {}; -gurka::ways MultipleBarriers::ways = {}; - -TEST_F(MultipleBarriers, DeniedBarrierAccess) { - const gurka::nodes nodes = { - {"1", {{"barrier", "gate"}, {"access", "no"}}}, - }; - const gurka::map map = - gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_no_access"); - check_auto_path(map, {"AC2", "2DB"}); -} - -TEST_F(MultipleBarriers, AllowedBarrierAccess) { - const gurka::nodes nodes = { - {"1", {{"barrier", "gate"}, {"access", "yes"}}}, - }; - const gurka::map map = - gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_allowed_access"); - check_auto_path(map, {"A1", "1B"}); -} - -TEST_F(MultipleBarriers, AllowedVehicleBarrierAccess) { - const gurka::nodes nodes = { - {"1", {{"barrier", "gate"}, {"motor_vehicle", "yes"}}}, - }; - const gurka::map map = - gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_vehicle_allowed"); - check_auto_path(map, {"A1", "1B"}); -} - -TEST_F(MultipleBarriers, NoInfoBarrierAccess) { - const gurka::nodes nodes = { - {"1", {{"barrier", "gate"}}}, - }; - const gurka::map map = - gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_no_access_info"); - check_auto_path(map, {"AC2", "2DB"}); -} - -TEST_F(MultipleBarriers, TwoBarriers) { - const gurka::nodes nodes = { - {"1", {{"barrier", "gate"}}}, - {"2", {{"barrier", "gate"}}}, - }; - const gurka::map map = - gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_two_gates"); - check_auto_path(map, {"A1", "1B"}); -} - -TEST_F(MultipleBarriers, ClosedLongRoute) { - const gurka::nodes nodes = { - {"1", {{"barrier", "gate"}, {"motor_vehicle", "no"}}}, - {"2", {{"barrier", "gate"}}}, - }; - const gurka::map map = - gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_closed_long"); - check_auto_path(map, {"AC2", "2DB"}); -} - -TEST_F(MultipleBarriers, ClosedShortRoute) { - const gurka::nodes nodes = { - {"1", {{"barrier", "gate"}}}, - {"2", {{"barrier", "gate"}, {"motor_vehicle", "no"}}}, - }; - const gurka::map map = - gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_closed_short"); - check_auto_path(map, {"A1", "1B"}); -} - -TEST_F(MultipleBarriers, BothClosed) { - const gurka::nodes nodes = { - {"1", {{"barrier", "gate"}, {"motor_vehicle", "no"}}}, - {"2", {{"barrier", "gate"}, {"access", "no"}}}, - }; - const gurka::map map = - gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_both_closed"); - try { - auto result = gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "auto"); - gurka::assert::raw::expect_path(result, {"Unexpected path found"}); - } catch (const std::runtime_error& e) { - EXPECT_STREQ(e.what(), "No path could be found for input"); - } -} - -TEST_F(MultipleBarriers, BothPrivate) { - const gurka::nodes nodes = { - {"1", {{"barrier", "gate"}, {"access", "private"}}}, - {"2", {{"barrier", "gate"}, {"access", "private"}}}, - }; - const gurka::map map = - gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_both_private"); - check_auto_path(map, {"A1", "1B"}); -} - -TEST_F(MultipleBarriers, ShortestPrivate) { - const gurka::nodes nodes = { - {"1", {{"barrier", "gate"}, {"access", "private"}}}, - {"2", {{"barrier", "gate"}, {"access", "yes"}}}, - }; - const gurka::map map = - gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_barrier_one_private"); - check_auto_path(map, {"AC2", "2DB"}); -} - -TEST_F(MultipleBarriers, BollardPrivate) { - const gurka::nodes nodes = { - {"1", {{"barrier", "bollard"}, {"access", "private"}}}, - {"2", {{"barrier", "bollard"}, {"access", "yes"}}}, - }; - const gurka::map map = - gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_bollard_one_private"); - check_auto_path(map, {"AC2", "2DB"}); -} - -TEST_F(MultipleBarriers, BollardPrivateMotorVehicle) { - const gurka::nodes nodes = { - {"1", {{"barrier", "bollard"}, {"motor_vehicle", "private"}}}, - {"2", {{"barrier", "bollard"}, {"access", "yes"}}}, - }; - const gurka::map map = - gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_bollard_private_motor_vehicle"); - check_auto_path(map, {"AC2", "2DB"}); -} - -TEST_F(MultipleBarriers, BollardPrivateAndNoInfo) { - const gurka::nodes nodes = { - {"1", {{"barrier", "bollard"}, {"motor_vehicle", "private"}}}, - {"2", {{"barrier", "bollard"}}}, - }; - const gurka::map map = - gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_bollard_private_no_info"); - check_auto_path(map, {"A1", "1B"}); -} - -TEST_F(MultipleBarriers, BollardBothPrivate) { - const gurka::nodes nodes = { - {"1", {{"barrier", "bollard"}, {"access", "private"}}}, - {"2", {{"barrier", "bollard"}, {"access", "private"}}}, - }; - const gurka::map map = - gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_bollard_both_private"); - check_auto_path(map, {"A1", "1B"}); -} - -TEST_F(MultipleBarriers, BollardNoAccessInformation) { - const gurka::nodes nodes = { - {"1", {{"barrier", "bollard"}, {"access", "no"}}}, - {"2", {{"barrier", "bollard"}}}, - }; - const gurka::map map = - gurka::buildtiles(layout, ways, nodes, {}, "test/data/multiple_bollard_no_access_info"); - try { - auto result = gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "auto"); - gurka::assert::raw::expect_path(result, {"Unexpected path found"}); - } catch (const std::runtime_error& e) { - EXPECT_STREQ(e.what(), "No path could be found for input"); - } -} - -class AccessibleBarriers : public ::testing::Test { -protected: - static gurka::map map; - - static void SetUpTestSuite() { - constexpr double gridsize = 100; - - const std::string ascii_map = R"( - B-----1---C - | \ - | \ - M-----2------N - | \ - A-----3--------D - | / - F-----4------E - )"; - - const gurka::ways ways = { - {"AB", {{"highway", "primary"}}}, {"B1C", {{"highway", "primary"}}}, - {"CD", {{"highway", "primary"}}}, {"AM", {{"highway", "primary"}}}, - {"M2", {{"highway", "primary"}}}, {"2N", {{"highway", "primary"}}}, - {"ND", {{"highway", "primary"}}}, {"A3D", {{"highway", "primary"}}}, - {"AF", {{"highway", "primary"}}}, {"F4", {{"highway", "primary"}}}, - {"4E", {{"highway", "primary"}}}, {"ED", {{"highway", "primary"}}}, - }; - - const gurka::nodes nodes = { - // gate is opened - {"1", {{"barrier", "gate"}, {"access", "yes"}}}, - // access is not specified, huge penalty is added. - {"2", {{"barrier", "gate"}}}, - // access is private. Penalty is added because of privateness. - {"3", {{"barrier", "gate"}, {"access", "private"}}}, - // gate is opened for bicycle only. - {"4", {{"barrier", "lift_gate"}, {"access", "no"}, {"bicycle", "yes"}}}, - }; - - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); - map = gurka::buildtiles(layout, ways, nodes, {}, "test/data/accessible_barriers"); - } -}; - -gurka::map AccessibleBarriers::map = {}; - -TEST_F(AccessibleBarriers, Auto) { - const std::string cost = "auto"; - auto result = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, cost); - gurka::assert::raw::expect_path(result, {"AB", "B1C", "B1C", "CD"}); -} - -TEST_F(AccessibleBarriers, Bicycle) { - const std::string cost = "bicycle"; - auto result = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, cost); - gurka::assert::raw::expect_path(result, {"AF", "F4", "4E", "ED"}); -} - -TEST_F(AccessibleBarriers, Pedestrian) { - const std::string cost = "pedestrian"; - auto result = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, cost); - gurka::assert::raw::expect_path(result, {"AM", "M2", "2N", "ND"}); -} diff --git a/test/gurka/test_admin.cc b/test/gurka/test_admin.cc index 6300433b1e..0964dfe546 100644 --- a/test/gurka/test_admin.cc +++ b/test/gurka/test_admin.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_admin_sidewalk_crossing_override.cc b/test/gurka/test_admin_sidewalk_crossing_override.cc index 3f833f8575..201bf3731b 100644 --- a/test/gurka/test_admin_sidewalk_crossing_override.cc +++ b/test/gurka/test_admin_sidewalk_crossing_override.cc @@ -1,7 +1,8 @@ +#include + #include #include "baldr/admin.h" -#include "filesystem.h" #include "gurka.h" #include "mjolnir/admin.h" #include "mjolnir/adminbuilder.h" @@ -70,8 +71,6 @@ void GetAdminData(const std::string& dbname, std::string sql = "SELECT admin_level, name from admins;"; uint32_t result = 0; - bool dor = true; - bool intersection_name = false; ret = sqlite3_prepare_v2(db_handle, sql.c_str(), sql.length(), &stmt, 0); if (ret == SQLITE_OK || ret == SQLITE_ERROR) { @@ -121,8 +120,8 @@ TEST(AdminTest, TestBuildAdminFromPBF) { // Create test/data/admin/map.pbf const std::string workdir = "test/data/admin_belarus"; - if (!filesystem::exists(workdir)) { - bool created = filesystem::create_directories(workdir); + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); EXPECT_TRUE(created); } diff --git a/test/gurka/test_admin_uk_override.cc b/test/gurka/test_admin_uk_override.cc index 99e7159ddb..e6680b21ee 100644 --- a/test/gurka/test_admin_uk_override.cc +++ b/test/gurka/test_admin_uk_override.cc @@ -1,7 +1,7 @@ +#include #include #include "baldr/admin.h" -#include "filesystem.h" #include "gurka.h" #include "mjolnir/admin.h" #include "mjolnir/adminbuilder.h" @@ -66,8 +66,6 @@ void GetAdminData(const std::string& dbname, std::string sql = "SELECT admin_level, name from admins;"; uint32_t result = 0; - bool dor = true; - bool intersection_name = false; ret = sqlite3_prepare_v2(db_handle, sql.c_str(), sql.length(), &stmt, 0); if (ret == SQLITE_OK || ret == SQLITE_ERROR) { @@ -117,8 +115,8 @@ TEST(AdminTest, TestBuildAdminFromPBF) { // Create test/data/admin/map.pbf const std::string workdir = "test/data/admin_uk"; - if (!filesystem::exists(workdir)) { - bool created = filesystem::create_directories(workdir); + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); EXPECT_TRUE(created); } diff --git a/test/gurka/test_avoids.cc b/test/gurka/test_avoids.cc index c8d6ad8c55..64a9d1547b 100644 --- a/test/gurka/test_avoids.cc +++ b/test/gurka/test_avoids.cc @@ -1,4 +1,5 @@ #include "gurka.h" +#include #include #include #include diff --git a/test/gurka/test_barrier_uturns.cc b/test/gurka/test_barrier_uturns.cc index 247e4563a4..56d140b949 100644 --- a/test/gurka/test_barrier_uturns.cc +++ b/test/gurka/test_barrier_uturns.cc @@ -1,4 +1,5 @@ #include "gurka.h" +#include #include using namespace valhalla; diff --git a/test/gurka/test_bidir_search.cc b/test/gurka/test_bidir_search.cc index ae37ca53a0..55ca43e2eb 100644 --- a/test/gurka/test_bidir_search.cc +++ b/test/gurka/test_bidir_search.cc @@ -40,7 +40,7 @@ TEST(StandAlone, exhaust_reverse_search) { } }); - // Without extending search, the route should not fail due to settting not_thru_pruning_ + // Without extending search, the route should not fail due to setting not_thru_pruning_ // to false on the second pass map.config.put("thor.extended_search", false); auto result = gurka::do_action(valhalla::Options::route, map, {"A", "F"}, "auto", {}); @@ -84,7 +84,7 @@ TEST(StandAlone, exhaust_forward_search) { } }); - // Without extending search, the route should not fail due to settting not_thru_pruning_ + // Without extending search, the route should not fail due to setting not_thru_pruning_ // to false on the second pass map.config.put("thor.extended_search", false); auto result = gurka::do_action(valhalla::Options::route, map, {"A", "F"}, "auto", {}); diff --git a/test/gurka/test_build_admin.cc b/test/gurka/test_build_admin.cc index e30dc14532..97d4f8f267 100644 --- a/test/gurka/test_build_admin.cc +++ b/test/gurka/test_build_admin.cc @@ -1,7 +1,8 @@ +#include + #include #include "baldr/admin.h" -#include "filesystem.h" #include "gurka.h" #include "mjolnir/adminbuilder.h" #include "mjolnir/pbfgraphparser.h" @@ -251,8 +252,6 @@ void GetAdminData(const std::string& dbname, std::string sql = "SELECT admin_level, name from admins;"; uint32_t result = 0; - bool dor = true; - bool intersection_name = false; ret = sqlite3_prepare_v2(db_handle, sql.c_str(), sql.length(), &stmt, 0); if (ret == SQLITE_OK || ret == SQLITE_ERROR) { @@ -302,8 +301,8 @@ TEST(AdminTest, TestBuildAdminFromPBF) { // Create test/data/admin/map.pbf const std::string workdir = "test/data/admin"; - if (!filesystem::exists(workdir)) { - bool created = filesystem::create_directories(workdir); + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); EXPECT_TRUE(created); } diff --git a/test/gurka/test_closure_penalty.cc b/test/gurka/test_closure_penalty.cc index a98a78e082..437a84b4f9 100644 --- a/test/gurka/test_closure_penalty.cc +++ b/test/gurka/test_closure_penalty.cc @@ -1,3 +1,5 @@ +#include + #include "gurka.h" #include "test.h" diff --git a/test/gurka/test_conditional_restrictions.cc b/test/gurka/test_conditional_restrictions.cc index d161168d6b..390c3a48d1 100644 --- a/test/gurka/test_conditional_restrictions.cc +++ b/test/gurka/test_conditional_restrictions.cc @@ -97,8 +97,9 @@ class ConditionalRestrictions : public ::testing::Test { }; const auto layout = gurka::detail::map_to_coordinates(ascii_map, grid_size_meters); - map = gurka::buildtiles(layout, ways, {}, {}, "test/data/conditional_restrictions", - {{"mjolnir.timezone", {"test/data/tz.sqlite"}}}); + map = gurka::buildtiles(layout, ways, {}, {}, + VALHALLA_BUILD_DIR "test/data/conditional_restrictions", + {{"mjolnir.timezone", {VALHALLA_BUILD_DIR "test/data/tz.sqlite"}}}); } }; diff --git a/test/gurka/test_config_speed.cc b/test/gurka/test_config_speed.cc index 9d4e33e05a..105608a297 100644 --- a/test/gurka/test_config_speed.cc +++ b/test/gurka/test_config_speed.cc @@ -2,6 +2,8 @@ #include "src/mjolnir/speed_assigner.h" #include +#include + using namespace valhalla; TEST(Standalone, DefaultSpeedConfig) { @@ -129,9 +131,9 @@ TEST(Standalone, DefaultSpeedConfig) { } } - if (!filesystem::create_directories("test/data")) + if (!std::filesystem::exists("test/data") && !std::filesystem::create_directories("test/data")) throw std::runtime_error("couldn't create directories"); - filesystem::remove("test/data/speed_config.json"); + std::filesystem::remove("test/data/speed_config.json"); { std::ofstream speed_config("test/data/speed_config.json"); @@ -281,9 +283,9 @@ TEST(Standalone, SuburbanSpeedConfig) { } } - if (!filesystem::create_directories("test/data")) + if (!std::filesystem::exists("test/data") && !std::filesystem::create_directories("test/data")) throw std::runtime_error("couldn't create directories"); - filesystem::remove("test/data/speed_config_suburban.json"); + std::filesystem::remove("test/data/speed_config_suburban.json"); { std::ofstream speed_config("test/data/speed_config_suburban.json"); diff --git a/test/gurka/test_costing_type_blind.cc b/test/gurka/test_costing_type_blind.cc new file mode 100644 index 0000000000..c9ecb0d77d --- /dev/null +++ b/test/gurka/test_costing_type_blind.cc @@ -0,0 +1,146 @@ +#include "gurka.h" +#include + +using namespace valhalla; + +// Make sure we get instructions for passing a bridge +TEST(CostingTypeBlind, Bridge) { + + const std::string& ascii_map = R"( + A---B-------C---D + )"; + const gurka::ways ways = { + {"AB", + { + {"name", "normal street"}, + {"highway", "footway"}, + }}, + {"BC", {{"name", ""}, {"highway", "footway"}, {"bridge", "yes"}}}, + {"CD", + { + {"name", "normal street"}, + {"highway", "footway"}, + }}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 50); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/costing_type_blind_bridge"); + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "pedestrian", + {{"/costing_options/pedestrian/type", "blind"}}); + + // Verify instructions for start and end maneuvers + gurka::assert::raw::expect_instructions_at_maneuver_index(result, 1, "Continue on the bridge.", "", + "Continue on the bridge.", + "Continue on the bridge.", + "Continue for 400 meters."); +} + +// Make sure we get instructions for passing a tunnel +TEST(CostingTypeBlind, Tunnel) { + + const std::string& ascii_map = R"( + A---B-------C---D + )"; + const gurka::ways ways = { + {"AB", + { + {"name", "normal street"}, + {"highway", "footway"}, + }}, + {"BC", {{"name", ""}, {"highway", "footway"}, {"tunnel", "yes"}}}, + {"CD", + { + {"name", "normal street"}, + {"highway", "footway"}, + }}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 50); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/costing_type_blind_tunnel"); + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "pedestrian", + {{"/costing_options/pedestrian/type", "blind"}}); + + // Verify instructions for start and end maneuvers + gurka::assert::raw::expect_instructions_at_maneuver_index(result, 1, "Continue on the tunnel.", "", + "Continue on the tunnel.", + "Continue on the tunnel.", + "Continue for 400 meters."); +} + +// Make sure we get instructions for passing a traffic sign +TEST(CostingTypeBlind, TrafficSignal) { + + const std::string& ascii_map = R"( + E + | + A---B-------C---D + | + F + )"; + const gurka::ways ways = { + {"AB", + { + {"name", "normal street"}, + {"highway", "footway"}, + }}, + {"BC", {{"name", ""}, {"highway", "footway"}}}, + {"CD", + { + {"name", "normal street"}, + {"highway", "footway"}, + }}, + {"BE", + { + {"name", "crossing street"}, + {"highway", "secondary"}, + }}, + {"BF", + { + {"name", "crossing street"}, + {"highway", "secondary"}, + }}, + }; + + const gurka::nodes nodes = {{"B", {{"highway", "traffic_signals"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 50); + auto map = gurka::buildtiles(layout, ways, nodes, {}, "test/data/costing_type_blind_traffic_sign"); + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "pedestrian", + {{"/costing_options/pedestrian/type", "blind"}}); + + // Verify instructions for start and end maneuvers + gurka::assert::raw:: + expect_instructions_at_maneuver_index(result, 1, "Pass traffic signals on crossing street.", "", + "", "Pass traffic signals on crossing street.", ""); +} + +TEST(CostingTypeBlind, Bollard) { + + const std::string& ascii_map = R"( + A---B-------C---D + )"; + const gurka::ways ways = { + {"AB", + { + {"name", "normal street"}, + {"highway", "footway"}, + }}, + {"BC", {{"name", "normal street"}, {"highway", "footway"}}}, + {"CD", + { + {"name", "normal street"}, + {"highway", "footway"}, + }}, + }; + + const gurka::nodes nodes = {{"B", {{"barrier", "bollard"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 50); + auto map = gurka::buildtiles(layout, ways, nodes, {}, "test/data/costing_type_blind_bollard"); + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "pedestrian", + {{"/costing_options/pedestrian/type", "blind"}}); + + // Verify instructions for start and end maneuvers + gurka::assert::raw::expect_instructions_at_maneuver_index(result, 1, "Pass the bollards.", "", "", + "Pass the bollards.", ""); +} \ No newline at end of file diff --git a/test/gurka/test_costing_with_traffic.cc b/test/gurka/test_costing_with_traffic.cc index 9e82242ae8..de9232e391 100644 --- a/test/gurka/test_costing_with_traffic.cc +++ b/test/gurka/test_costing_with_traffic.cc @@ -1,5 +1,6 @@ #include "gurka.h" #include "test.h" +#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_destination_only.cc b/test/gurka/test_destination_only.cc index ff4b4309c2..2af7d89231 100644 --- a/test/gurka/test_destination_only.cc +++ b/test/gurka/test_destination_only.cc @@ -26,13 +26,58 @@ TEST(Standalone, DestinationOnly) { }; const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_destination"); - auto result = gurka::do_action(valhalla::Options::route, map, {"A", "I"}, "auto"); - ASSERT_EQ(result.trip().routes(0).legs_size(), 1); - auto leg = result.trip().routes(0).legs(0); + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "I"}, "auto"); gurka::assert::raw::expect_path(result, {"AB", "BC", "CD", "DE", "EF", "FG", "GH", "HI"}); } +TEST(Standalone, DestinationOnlyHGV) { + const std::string ascii_map = R"( + A----B----C----D----E + | | | + | | | + I----H----G----F + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "residential"}}}, + {"BC", {{"highway", "residential"}}}, + {"CD", {{"highway", "residential"}}}, + {"DE", {{"highway", "residential"}}}, + {"EF", {{"highway", "residential"}}}, + {"FG", {{"highway", "residential"}}}, + {"GH", {{"highway", "residential"}}}, + {"HI", {{"highway", "residential"}}}, + {"DG", {{"highway", "residential"}, {"hgv", "destination"}}}, + {"BI", {{"highway", "residential"}, {"access", "private"}}}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_destination_hgv"); + + // hgv route + { + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "I"}, "truck"); + gurka::assert::raw::expect_path(result, {"AB", "BC", "CD", "DE", "EF", "FG", "GH", "HI"}); + } + // hgv matrix + { + auto result = gurka::do_action(valhalla::Options::sources_to_targets, map, {"A"}, {"I"}, "truck"); + EXPECT_EQ(result.matrix().distances(0), 3800); + } + + // car route - doesn't take the hgv=destination road + { + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "I"}, "auto"); + gurka::assert::raw::expect_path(result, {"AB", "BC", "CD", "DG", "GH", "HI"}); + } + // car matrix - doesn't take the hgv=destination road + { + auto result = gurka::do_action(valhalla::Options::sources_to_targets, map, {"A"}, {"I"}, "auto"); + EXPECT_EQ(result.matrix().distances(0), 2800); + } +} + TEST(Standalone, DestinationOnlyDrivewaysMidNoAlternatives) { const std::string ascii_map = R"( A----B----C----D----E diff --git a/test/gurka/test_elevation.cc b/test/gurka/test_elevation.cc new file mode 100644 index 0000000000..d97fc1e274 --- /dev/null +++ b/test/gurka/test_elevation.cc @@ -0,0 +1,233 @@ +#include "gurka.h" +#include "test.h" + +#include "baldr/json.h" +#include "loki/worker.h" +#include "midgard/pointll.h" + +#include + +#include + +using namespace valhalla; +using namespace valhalla::gurka; + +const std::string workdir = "test/data/gurka_elevation"; + +std::string json_escape(const std::string& unescaped) { + std::stringstream ss; + baldr::json::OstreamVisitor v(ss); + v(unescaped); + std::string escaped = ss.str().substr(1); + escaped.pop_back(); + return escaped; +} + +// we need to prove that what we store in the graph is roughly equivalent to pulling it out of the +// elevation tile directly, we dont even care what the values are, just that they are +// approximately similar, the only other thing to do is validate the response format looks +// good but in order to pull the data out to compare you're already doing that +TEST(Standalone, ElevationCompareToSkadi) { + // SKETCH OF THE TEST BELOW: + // call gurka do_action with route action and elevation requested + // pull the route shape out of the route response + // send the route shape to gurka do_action with the height action + // pull the elevation out of the route response + // compare it to the height response + + const std::string ascii_map = R"( + A---B + | | + | C + | + D--E-----F------G + | | + | | + | 1 + H--I-----J + 2 + | + | + K---L-----M + | | + | | + N---O-----P + | | | + | | | + Q---R-----S + )"; + + const gurka::ways ways = { + {"KNQ", {{"highway", "service"}, {"service", "alley"}}}, + {"NOP", {{"highway", "residential"}, {"name", "East Chestnut Street"}}}, + {"QRS", {{"highway", "service"}, {"service", "alley"}}}, + {"OR", {{"highway", "service"}, {"service", "alley"}}}, + {"SPM2J1F", {{"highway", "service"}, {"service", "alley"}}}, + {"KLM", {{"highway", "service"}, {"service", "alley"}, {"name", "East Center Alley"}}}, + {"DEFG", {{"highway", "service"}, {"service", "alley"}, {"name", "North Alley"}}}, + {"DH", {{"highway", "service"}, {"service", "alley"}}}, + {"HIJ", {{"highway", "secondary"}, {"name", "East Main Street"}}}, + {"EABC", {{"highway", "service"}, {"service", "driveway"}}}, + }; + + // Create our layout based on real world data. + // Really the only one we care about is SPM2J1F or omsid 326371867 + using nodelayout = std::map; + nodelayout layout; + + layout.insert({"1", {-76.4945823, 40.6517478}}); + layout.insert({"2", {-76.4945322, 40.6516377}}); + layout.insert({"A", {-76.4951627, 40.6526995}}); + layout.insert({"B", {-76.4948476, 40.6527545}}); + layout.insert({"C", {-76.4948142, 40.6526154}}); + layout.insert({"D", {-76.4956555, 40.6521481}}); + layout.insert({"E", {-76.4951254, 40.6521625}}); + layout.insert({"F", {-76.4946354, 40.6521889}}); + layout.insert({"G", {-76.494053, 40.6522276}}); + layout.insert({"H", {-76.4956165, 40.6516304}}); + layout.insert({"I", {-76.4950803, 40.6516647}}); + layout.insert({"J", {-76.4945597, 40.651698}}); + layout.insert({"K", {-76.4958143, 40.6511348}}); + layout.insert({"L", {-76.4951349, 40.6506908}}); + layout.insert({"M", {-76.4944987, 40.6512172}}); + layout.insert({"N", {-76.4957653, 40.6506541}}); + layout.insert({"O", {-76.4951349, 40.6506908}}); + layout.insert({"P", {-76.4944599, 40.6507301}}); + layout.insert({"Q", {-76.4957235, 40.6502434}}); + layout.insert({"R", {-76.4950865, 40.6501919}}); + layout.insert({"S", {-76.4944069, 40.6502916}}); + + // create a fake elevation tile over the gurka map area + PointLL bottom_left(-77, 40), upper_right(-76, 41); + auto corner_to_corner_dist = bottom_left.Distance(upper_right); + // just a randomly chosen max height that will create reasonable changes in local elevation + double max_height = 9000; + std::vector tile(3601 * 3601, 0); + for (size_t i = 0; i < 3601; ++i) { // latitude pixels + for (size_t j = 0; j < 3601; ++j) { // longitude pixels + // we set the height at each pixel of the srtm tile based on its lat lon. srtm tiles have their + // origin in the south west corner of the tile. our tile is 40, -77, that means the top right + // corner is 41, -76. so that our values dont get too crazy we'll just use the distance a given + // pixel is, from the bottom left corner to pick a height for a given pixel. this will give us a + // full tile of pixels even if the heights are not real they will vary by realistic amounts. the + // bottom left will have a height of 0 meters and the top right will have max_height. height + // will smoothly vary from corner to corner + + // convert pixel to ll + double lon = (static_cast(j) / 3601) - 77; + double lat = (static_cast(i) / 3601) + 40; + // measure distance and use it to scale a max height range + auto dist_ratio = bottom_left.Distance(PointLL(lon, lat)) / corner_to_corner_dist; + int16_t height = std::round(dist_ratio * max_height); + // and set the height in the tile data (flipping to big endian to match the srtm spec) + tile[i * 3601 + j] = ((height & 0xFF) << 8) | ((height >> 8) & 0xFF); + } + } + + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + // actually store it + std::ofstream file(workdir + "/N40W077.hgt", std::ios::binary | std::ios::trunc); + file.write(static_cast(static_cast(tile.data())), + sizeof(int16_t) * tile.size()); + ASSERT_TRUE(file.good()) << "File stream is not good"; + file.close(); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + + valhalla::gurka::map map; + map.nodes = layout; + map.config = test::make_config(workdir, {}); + boost::property_tree::ptree& pt = map.config; + pt.put("mjolnir.tile_dir", workdir + "/tiles"); + pt.put("additional_data.elevation", workdir); + + std::vector input_files = {pbf_filename}; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + // try a bunch of routes + for (const auto& waypoints : std::vector>{ + {"S", "F"}, + {"C", "N"}, + }) { + + // get a route with elevation included + std::string route_json; + auto route = gurka::do_action(valhalla::Options::route, map, waypoints, "bicycle", + {{"/elevation_interval", "30"}}, {}, &route_json); + rapidjson::Document result; + result.Parse(route_json.c_str()); + + // for each leg + for (size_t leg_index = 0; leg_index < waypoints.size() - 1; ++leg_index) { + // pull out the shape from the leg + auto s = + rapidjson::get_child_optional(result, ("/trip/legs/" + std::to_string(leg_index) + "/shape") + .c_str()); + EXPECT_TRUE(s && s->IsString()); + auto shape = json_escape(s->GetString()); + + std::string height_json; + std::string request = + R"({"height_precision":1,"resample_distance":30,"encoded_polyline":")" + shape + R"("})"; + auto height = gurka::do_action(valhalla::Options::height, map, request, {}, &height_json); + + // pull out the elevation from the route result leg + auto elevation = + rapidjson::get_child_optional(result, + ("/trip/legs/" + std::to_string(leg_index) + "/elevation") + .c_str()); + EXPECT_TRUE(elevation && elevation->IsArray()); + std::vector elevation_along_edges; + for (const auto& e : elevation->GetArray()) { + elevation_along_edges.push_back(std::round(e.GetFloat() * 10) / 10); + std::cout << std::round(e.GetFloat() * 10) / 10 << std::endl; + } + + result.Parse(height_json.c_str()); + // pull out the elevation from the height result + elevation = rapidjson::get_child_optional(result, "/height"); + EXPECT_TRUE(elevation && elevation->IsArray()); + std::vector elevation_from_skadi; + for (const auto& e : elevation->GetArray()) { + elevation_from_skadi.push_back(std::round(e.GetFloat() * 10) / 10); + std::cout << std::round(e.GetFloat() * 10) / 10 << std::endl; + } + + EXPECT_EQ(elevation_along_edges.size(), elevation_from_skadi.size()); + for (size_t i = 0; i < elevation_along_edges.size(); ++i) { + EXPECT_NEAR(elevation_along_edges[i], elevation_from_skadi[i], 0.5f); + } + } + } + + // a quick test for when you request a route without elevation that its not there + // get a route without elevation included + for (const auto& waypoints : std::vector>{ + {"S", "F"}, + {"C", "N"}, + }) { + std::string route_json; + auto route = + gurka::do_action(valhalla::Options::route, map, {"S", "F"}, "bicycle", {}, {}, &route_json); + rapidjson::Document result; + result.Parse(route_json.c_str()); + + for (size_t leg_index = 0; leg_index < waypoints.size() - 1; ++leg_index) { + [[maybe_unused]] auto s = + rapidjson::get_child_optional(result, ("/trip/legs/" + std::to_string(leg_index) + "/shape") + .c_str()); + + auto elevation = + rapidjson::get_child_optional(result, + ("/trip/legs/" + std::to_string(leg_index) + "/elevation") + .c_str()); + EXPECT_FALSE(elevation && elevation->IsArray()); + } + } +} diff --git a/test/gurka/test_expansion.cc b/test/gurka/test_expansion.cc index ca1d16f1d0..c504c3e68f 100644 --- a/test/gurka/test_expansion.cc +++ b/test/gurka/test_expansion.cc @@ -1,6 +1,5 @@ #include "gurka.h" #include -#include #include using namespace valhalla; @@ -31,15 +30,18 @@ class ExpansionTest : public ::testing::TestWithParam> expansion_map = gurka::buildtiles(layout, ways, {}, {}, "test/data/expansion"); } - void check_result(const std::string& action, - const std::vector& waypoints, - bool skip_opps, - unsigned exp_feats, - const std::vector& props = {}) { - + valhalla::Api do_expansion_action(std::string* res, + bool skip_opps, + bool dedupe, + std::string action, + const std::vector& props, + const std::vector& waypoints, + const bool pbf) { std::unordered_map options = {{"/skip_opposites", skip_opps ? "1" : "0"}, - {"/action", action}}; + {"/action", action}, + {"/dedupe", dedupe ? "1" : "0"}, + {"/format", pbf ? "pbf" : "json"}}; for (uint8_t i = 0; i < props.size(); i++) { options.insert({{"/expansion_properties/" + std::to_string(i), props[i]}}); } @@ -48,21 +50,101 @@ class ExpansionTest : public ::testing::TestWithParam> } // get the response + valhalla::Api api; + + if (action == "sources_to_targets") { + api = gurka::do_action(Options::expansion, expansion_map, {waypoints[0]}, {waypoints[1]}, + "auto", options, {}, res); + } else { + api = gurka::do_action(Options::expansion, expansion_map, waypoints, "auto", options, {}, res); + } + + return api; + } + void check_results(const std::string& action, + const std::vector& waypoints, + bool skip_opps, + unsigned exp_feats, + const std::vector& props = {}, + bool dedupe = false) { + check_result_json(action, waypoints, skip_opps, dedupe, exp_feats, props); + check_result_pbf(action, waypoints, skip_opps, dedupe, exp_feats, props); + } + void check_result_pbf(const std::string& action, + const std::vector& waypoints, + bool skip_opps, + bool dedupe, + unsigned exp_feats, + const std::vector& props) { std::string res; - auto api = - gurka::do_action(Options::expansion, expansion_map, waypoints, "auto", options, {}, &res); + auto api = do_expansion_action(&res, skip_opps, dedupe, action, props, waypoints, true); + + Api parsed_api; + parsed_api.ParseFromString(res); + + ASSERT_EQ(parsed_api.expansion().geometries_size(), exp_feats); + + bool edge_status = false; + bool distance = false; + bool duration = false; + bool pred_edge_id = false; + bool edge_id = false; + bool cost = false; + bool expansion_type = false; + + const std::unordered_map prop_count; + for (const auto& prop : props) { + if (prop == "edge_status") { + edge_status = true; + } + if (prop == "distance") { + distance = true; + } + if (prop == "duration") { + duration = true; + } + if (prop == "pred_edge_id") { + pred_edge_id = true; + } + if (prop == "edge_id") { + edge_id = true; + } + if (prop == "cost") { + cost = true; + } + if (prop == "expansion_type") { + expansion_type = true; + } + } + ASSERT_EQ(parsed_api.expansion().geometries_size(), exp_feats); + ASSERT_EQ(parsed_api.expansion().edge_status_size(), edge_status ? exp_feats : 0); + ASSERT_EQ(parsed_api.expansion().distances_size(), distance ? exp_feats : 0); + ASSERT_EQ(parsed_api.expansion().durations_size(), duration ? exp_feats : 0); + ASSERT_EQ(parsed_api.expansion().pred_edge_id_size(), pred_edge_id ? exp_feats : 0); + ASSERT_EQ(parsed_api.expansion().edge_id_size(), edge_id ? exp_feats : 0); + ASSERT_EQ(parsed_api.expansion().costs_size(), cost ? exp_feats : 0); + ASSERT_EQ(parsed_api.expansion().expansion_type_size(), expansion_type ? exp_feats : 0); + } + void check_result_json(const std::string& action, + const std::vector& waypoints, + bool skip_opps, + bool dedupe, + unsigned exp_feats, + const std::vector& props) { + std::string res; + auto api = do_expansion_action(&res, skip_opps, dedupe, action, props, waypoints, false); // get the MultiLineString feature rapidjson::Document res_doc; res_doc.Parse(res.c_str()); + ASSERT_EQ(res_doc["features"].GetArray().Size(), exp_feats); auto feat = res_doc["features"][0].GetObject(); - ASSERT_EQ(feat["geometry"]["type"].GetString(), std::string("MultiLineString")); - - auto coords_size = feat["geometry"]["coordinates"].GetArray().Size(); - ASSERT_EQ(coords_size, exp_feats); + ASSERT_EQ(feat["geometry"]["type"].GetString(), std::string("LineString")); + ASSERT_TRUE(feat.HasMember("properties")); + ASSERT_EQ(feat["properties"].MemberCount(), props.size()); for (const auto& prop : props) { - ASSERT_EQ(feat["properties"][prop].GetArray().Size(), coords_size); + ASSERT_TRUE(feat["properties"].HasMember(prop)); } } }; @@ -74,27 +156,63 @@ gurka::map ExpansionTest::expansion_map = {}; TEST_P(ExpansionTest, Isochrone) { // test Dijkstra expansion // 11 because there's a one-way - check_result("isochrone", {"A"}, false, 11, GetParam()); + check_results("isochrone", {"A"}, false, 11, GetParam()); } TEST_P(ExpansionTest, IsochroneNoOpposites) { // test Dijkstra expansion and skip collecting more expensive opposite edges - check_result("isochrone", {"A"}, true, 6, GetParam()); + check_results("isochrone", {"A"}, true, 6, GetParam()); } TEST_P(ExpansionTest, Routing) { // test AStar expansion - check_result("route", {"E", "H"}, false, 23, GetParam()); + check_results("route", {"E", "H"}, false, 23, GetParam()); } TEST_P(ExpansionTest, RoutingNoOpposites) { // test AStar expansion and no opposite edges - check_result("route", {"E", "H"}, true, 16, GetParam()); + check_results("route", {"E", "H"}, true, 16, GetParam()); +} + +TEST_P(ExpansionTest, Matrix) { + check_results("sources_to_targets", {"E", "H"}, false, 48, GetParam()); +} + +TEST_P(ExpansionTest, MatrixNoOpposites) { + check_results("sources_to_targets", {"E", "H"}, true, 23, GetParam()); +} + +TEST_P(ExpansionTest, IsochroneDedupe) { + // test Dijkstra expansion + // 11 because there's a one-way + check_results("isochrone", {"A"}, false, 11, GetParam(), true); +} +TEST_P(ExpansionTest, IsochroneNoOppositesDedupe) { + // test Dijkstra expansion and skip collecting more expensive opposite edges + check_results("isochrone", {"A"}, true, 6, GetParam(), true); +} + +TEST_P(ExpansionTest, RoutingDedupe) { + // test AStar expansion + check_results("route", {"E", "H"}, false, 7, GetParam(), true); +} + +TEST_P(ExpansionTest, RoutingNoOppositesDedupe) { + // test AStar expansion and no opposite edges + check_results("route", {"E", "H"}, true, 5, GetParam(), true); +} + +TEST_P(ExpansionTest, MatrixDedupe) { + check_results("sources_to_targets", {"E", "H"}, false, 11, GetParam(), true); +} + +TEST_P(ExpansionTest, MatrixNoOppositesDedupe) { + check_results("sources_to_targets", {"E", "H"}, true, 6, GetParam(), true); } TEST_F(ExpansionTest, UnsupportedAction) { try { - check_result("status", {"E", "H"}, true, 16); + check_results("status", {"E", "H"}, true, 16); } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 144); } catch (...) { FAIL() << "Expected valhalla_exception_t."; }; @@ -102,7 +220,7 @@ TEST_F(ExpansionTest, UnsupportedAction) { TEST_F(ExpansionTest, UnsupportedPropType) { try { - check_result("route", {"E", "H"}, true, 16, {"foo", "bar"}); + check_results("route", {"E", "H"}, true, 16, {"foo", "bar"}); } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 168); } catch (...) { FAIL() << "Expected valhalla_exception_t."; }; @@ -121,9 +239,26 @@ TEST_F(ExpansionTest, NoAction) { }; } +TEST_F(ExpansionTest, UnsupportedActionDedupe) { + try { + check_results("status", {"E", "H"}, true, 5, {}, true); + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 144); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + }; +} + +TEST_F(ExpansionTest, UnsupportedPropTypeDedupe) { + try { + check_results("route", {"E", "H"}, true, 5, {"foo", "bar"}, true); + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 168); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + }; +} + INSTANTIATE_TEST_SUITE_P(ExpandPropsTest, ExpansionTest, - ::testing::Values(std::vector{"statuses"}, - std::vector{"distances", "durations"}, - std::vector{"edge_ids", "costs"}, + ::testing::Values(std::vector{"edge_status"}, + std::vector{"distance", "duration", + "pred_edge_id", "expansion_type"}, + std::vector{"edge_id", "cost"}, std::vector{})); diff --git a/test/gurka/test_ferry_connections.cc b/test/gurka/test_ferry_connections.cc index e60aa9a5d2..e2b4ae98a1 100644 --- a/test/gurka/test_ferry_connections.cc +++ b/test/gurka/test_ferry_connections.cc @@ -119,12 +119,15 @@ TEST(Standalone, ShortFerry) { } TEST(Standalone, TruckFerryDuration) { - // corresponds to 33.3 m/sec (120 km/h) in the below map - uint32_t ferry_secs = 9; + // corresponds to 21.4 m/sec (~77 km/h) in the below map + uint32_t ferry_secs = 14; const std::string ascii_map = R"( A--B--C--D | | + | | + | | + | | E--------F )"; @@ -151,7 +154,16 @@ TEST(Standalone, TruckFerryDuration) { // verify we took the ferry edge and the duration tag was respected auto ferry_edge = fastest.trip().routes(0).legs(0).node(1).edge(); ASSERT_EQ(ferry_edge.use(), valhalla::TripLeg_Use::TripLeg_Use_kFerryUse); - ASSERT_NEAR(ferry_edge.speed(), ferry_edge.length_km() / (ferry_secs * kHourPerSec), 0.1); + ASSERT_NEAR(ferry_edge.speed(), ferry_edge.length_km() / (ferry_secs * kHourPerSec), 0.2); + ASSERT_NEAR(fastest.directions().routes(0).legs(0).summary().time(), 50, 0.1); + + // pass a higher ferry cost and make sure it's added to the time + valhalla::Api higher_ferry_cost = + gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "truck", + {{"/costing_options/truck/use_ferry", "1"}, + {"/costing_options/truck/ferry_cost", "10"}}); + + ASSERT_NEAR(higher_ferry_cost.directions().routes(0).legs(0).summary().time(), 60, 0.1); } TEST_F(FerryTest, DoNotReclassifyFerryConnection) { @@ -198,7 +210,7 @@ TEST(Standalone, ReclassifyFerryConnectionRouteModes) { {"GH", no_hgv_car}, {"HI", no_hgv_car}}; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 1000); auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_reclassify_ferry_connections"); // working modes @@ -215,7 +227,6 @@ TEST(Standalone, ReclassifyFerryConnectionRouteModes) { }; } } - TEST_P(FerryTest, ReclassifyFerryConnectionPerMode) { // for these values of 'highway' tag edge class is upgraded in order to connect ferry to a // high-class road @@ -226,6 +237,449 @@ TEST_P(FerryTest, ReclassifyFerryConnectionPerMode) { } } +TEST(Standalone, ReclassifyFerryUntagDestOnly) { + // access=customers should be untagged if it's present on connecting edge(s) + // see https://github.com/valhalla/valhalla/issues/3941#issuecomment-1407713742 + // CD is destonly -> should untag BC, CD and take the faster path + // EF is not destonly -> shouldn't untag FG and take the detour + const std::string ascii_map = R"( + A--B--C--D------E--F---G--H--M + | | | | + I--J K---L + )"; + + std::map trunk = {{"highway", "trunk"}}; + std::map destonly = { + {"highway", "residential"}, + {"access", "customers"}, + }; + std::map not_destonly = {{"highway", "residential"}}; + + const gurka::ways ways = { + {"AB", trunk}, + {"BC", destonly}, // destonly should be removed when building the graph + {"BIJC", not_destonly}, + {"CD", destonly}, + {"DE", + {{"motor_vehicle", "yes"}, + {"motorcar", "yes"}, + {"bicycle", "yes"}, + {"moped", "yes"}, + {"bus", "yes"}, + {"hov", "yes"}, + {"taxi", "yes"}, + {"motorcycle", "yes"}, + {"route", "ferry"}}}, + {"EF", not_destonly}, + {"FG", destonly}, // should stay destonly and low-class bcs connecting edge isn't and this will + // be penalized + {"FKLG", not_destonly}, + {"GH", not_destonly}, + {"HM", trunk}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 500); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_reclassify_destonly"); + baldr::GraphReader reader(map.config.get_child("mjolnir")); + + // see if BC was untagged and upclassed + auto tagged = gurka::findEdge(reader, layout, "BC", "C"); + EXPECT_FALSE(std::get<1>(tagged)->destonly()) << "Edge BC shouldn't be destonly"; + EXPECT_TRUE(std::get<1>(tagged)->classification() == valhalla::baldr::RoadClass::kPrimary); + + // see if FX & XG are still tagged and low class + auto untagged = gurka::findEdge(reader, layout, "FG", "G"); + EXPECT_TRUE(std::get<1>(untagged)->destonly()) << "Edge FG should be destonly"; + EXPECT_FALSE(std::get<1>(untagged)->classification() == valhalla::baldr::RoadClass::kPrimary); + + // see if FKLG is upclassed + auto upclassed = gurka::findEdge(reader, layout, "FKLG", "G"); + EXPECT_TRUE(std::get<1>(upclassed)->classification() == valhalla::baldr::RoadClass::kPrimary); + + // we expect to take the shorter route on the left and the detour on the right + std::vector modes = {"auto", "motorcycle", "taxi", "bus", "hov", "truck"}; + for (const auto& mode : modes) { + auto res = gurka::do_action(valhalla::Options::route, map, {"A", "M"}, mode); + gurka::assert::raw::expect_path(res, {"AB", "BC", "CD", "DE", "EF", "FKLG", "GH", "HM"}, + mode + " failed."); + } +} + +TEST(Standalone, ReclassifyFerryNodePair) { + // Test to validate that the shortest path between 2 edges between a node + // pair takes the shorter, higher class road (not the longer, lower class) + + const std::string ascii_map = R"( + A--B--C--D------E + | | + I--J + )"; + + std::map trunk = {{"highway", "trunk"}}; + std::map secondary = {{"highway", "secondary"}}; + std::map residential = {{"highway", "residential"}}; + + const gurka::ways ways = { + {"AB", trunk}, + {"BC", secondary}, + {"BIJC", residential}, + {"CD", secondary}, + {"DE", + {{"motor_vehicle", "yes"}, + {"motorcar", "yes"}, + {"bicycle", "yes"}, + {"moped", "yes"}, + {"bus", "yes"}, + {"hov", "yes"}, + {"taxi", "yes"}, + {"motorcycle", "yes"}, + {"route", "ferry"}}}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 500); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_reclassify_nodepair"); + baldr::GraphReader reader(map.config.get_child("mjolnir")); + + // make sure BC and CD are upclassed + auto upclassed = gurka::findEdge(reader, layout, "BC", "C"); + EXPECT_TRUE(std::get<1>(upclassed)->classification() == valhalla::baldr::RoadClass::kPrimary); + auto upclassed2 = gurka::findEdge(reader, layout, "CD", "D"); + EXPECT_TRUE(std::get<1>(upclassed2)->classification() == valhalla::baldr::RoadClass::kPrimary); + + // make sure edge BIJC is not upclassed + auto not_upclassed = gurka::findEdge(reader, layout, "BIJC", "C"); + EXPECT_TRUE(std::get<1>(not_upclassed)->classification() == + valhalla::baldr::RoadClass::kResidential); +} + +TEST(Standalone, ReclassifyCorrectPath) { + // Test to validate that the shortest path between 2 edges between a node + // pair takes the shorter, higher class road (not the longer, lower class) + + const std::string ascii_map = R"( + A--Z--B--C--D------E + | | + F--G J--K + | | + I--H L--M + )"; + + std::map trunk = {{"highway", "trunk"}}; + std::map secondary = {{"highway", "secondary"}}; + std::map residential = {{"highway", "residential"}}; + std::map driveway = {{"highway", "service"}, {"service", "driveway"}}; + + const gurka::ways ways = { + {"AZ", trunk}, + {"ZB", secondary}, + {"BC", secondary}, + {"CD", secondary}, + {"BG", residential}, + {"GH", residential}, + {"GF", driveway}, + {"HI", driveway}, + {"CJ", residential}, + {"JL", residential}, + {"JK", driveway}, + {"LM", driveway}, + {"DE", + {{"motor_vehicle", "yes"}, + {"motorcar", "yes"}, + {"bicycle", "yes"}, + {"moped", "yes"}, + {"bus", "yes"}, + {"hov", "yes"}, + {"taxi", "yes"}, + {"motorcycle", "yes"}, + {"route", "ferry"}}}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 500); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_reclassify_correct_path"); + baldr::GraphReader reader(map.config.get_child("mjolnir")); + + // make sure ZB, BC and CD are upclassed + auto upclassed1 = gurka::findEdge(reader, layout, "ZB", "B"); + EXPECT_TRUE(std::get<1>(upclassed1)->classification() == valhalla::baldr::RoadClass::kPrimary); + auto upclassed2 = gurka::findEdge(reader, layout, "BC", "C"); + EXPECT_TRUE(std::get<1>(upclassed2)->classification() == valhalla::baldr::RoadClass::kPrimary); + auto upclassed3 = gurka::findEdge(reader, layout, "CD", "D"); + EXPECT_TRUE(std::get<1>(upclassed3)->classification() == valhalla::baldr::RoadClass::kPrimary); + + // make sure other edges are not upclassed + auto not_upclassed1 = gurka::findEdge(reader, layout, "BG", "G"); + EXPECT_TRUE(std::get<1>(not_upclassed1)->classification() == + valhalla::baldr::RoadClass::kResidential); + auto not_upclassed2 = gurka::findEdge(reader, layout, "GH", "H"); + EXPECT_TRUE(std::get<1>(not_upclassed2)->classification() == + valhalla::baldr::RoadClass::kResidential); + auto not_upclassed3 = gurka::findEdge(reader, layout, "CJ", "J"); + EXPECT_TRUE(std::get<1>(not_upclassed3)->classification() == + valhalla::baldr::RoadClass::kResidential); + auto not_upclassed4 = gurka::findEdge(reader, layout, "JL", "L"); + EXPECT_TRUE(std::get<1>(not_upclassed4)->classification() == + valhalla::baldr::RoadClass::kResidential); + auto not_upclassed5 = gurka::findEdge(reader, layout, "GF", "F"); + EXPECT_TRUE(std::get<1>(not_upclassed5)->classification() == + valhalla::baldr::RoadClass::kServiceOther); + auto not_upclassed6 = gurka::findEdge(reader, layout, "HI", "I"); + EXPECT_TRUE(std::get<1>(not_upclassed6)->classification() == + valhalla::baldr::RoadClass::kServiceOther); + auto not_upclassed7 = gurka::findEdge(reader, layout, "JK", "K"); + EXPECT_TRUE(std::get<1>(not_upclassed7)->classification() == + valhalla::baldr::RoadClass::kServiceOther); + auto not_upclassed8 = gurka::findEdge(reader, layout, "LM", "M"); + EXPECT_TRUE(std::get<1>(not_upclassed8)->classification() == + valhalla::baldr::RoadClass::kServiceOther); +} + +TEST(Standalone, ReclassifySeparateInboundAndOutbound) { + // Sometimes ferry path is connected to the separate inbound and outbound pathways. + // Both of them should be reclassified if both of them lead to the high road class. + + const std::string ascii_map = R"( + A + | + | + B + / \ + / \ + C D + / | + E--F---------------G + H-----I------------J + \ / + -- + )"; + + std::map primary = {{"highway", "primary"}, {"oneway", "yes"}}; + std::map service = {{"highway", "service"}, {"oneway", "yes"}}; + + const gurka::ways ways = { + {"AB", + {{"motor_vehicle", "yes"}, + {"motorcar", "yes"}, + {"bicycle", "yes"}, + {"moped", "yes"}, + {"bus", "yes"}, + {"hov", "yes"}, + {"taxi", "yes"}, + {"motorcycle", "yes"}, + {"route", "ferry"}}}, + {"BCF", service}, + {"IDB", service}, + {"EFG", primary}, + {"JIH", primary}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 500); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_reclassify_inbound_outbound"); + baldr::GraphReader reader(map.config.get_child("mjolnir")); + + // make sure that both service roads get reclassified + auto outbound_service = gurka::findEdge(reader, layout, "BCF", "F"); + EXPECT_EQ(std::get<1>(outbound_service)->classification(), valhalla::baldr::RoadClass::kPrimary); + auto inbound_service = gurka::findEdge(reader, layout, "IDB", "B"); + EXPECT_EQ(std::get<1>(inbound_service)->classification(), valhalla::baldr::RoadClass::kPrimary); +} + +TEST(Standalone, ReclassifyInboundOnly) { + const std::string ascii_map = R"( + A + | + | + B + / + / + C + / + E--F---------------G + H-----I------------J + )"; + + std::map primary = {{"highway", "primary"}, {"oneway", "yes"}}; + std::map service = {{"highway", "service"}, {"oneway", "yes"}}; + + const gurka::ways ways = { + {"AB", + {{"motor_vehicle", "yes"}, + {"motorcar", "yes"}, + {"bicycle", "yes"}, + {"moped", "yes"}, + {"bus", "yes"}, + {"hov", "yes"}, + {"taxi", "yes"}, + {"motorcycle", "yes"}, + {"route", "ferry"}}}, + {"BCF", service}, + {"EFG", primary}, + {"JIH", primary}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 500); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_reclassify_outbound_only"); + baldr::GraphReader reader(map.config.get_child("mjolnir")); + + auto outbound_service = gurka::findEdge(reader, layout, "BCF", "F"); + EXPECT_EQ(std::get<1>(outbound_service)->classification(), valhalla::baldr::RoadClass::kPrimary); +} + +TEST(Standalone, ReclassifyOutboundOnly) { + const std::string ascii_map = R"( + A + | + | + B + \ + \ + D + | + E--F---------------G + H-----I------------J + \ / + -- + )"; + + std::map primary = {{"highway", "primary"}, {"oneway", "yes"}}; + std::map service = {{"highway", "service"}, {"oneway", "yes"}}; + + const gurka::ways ways = { + {"AB", + {{"motor_vehicle", "yes"}, + {"motorcar", "yes"}, + {"bicycle", "yes"}, + {"moped", "yes"}, + {"bus", "yes"}, + {"hov", "yes"}, + {"taxi", "yes"}, + {"motorcycle", "yes"}, + {"route", "ferry"}}}, + {"IDB", service}, + {"EFG", primary}, + {"JIH", primary}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 500); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_reclassify_inbound_only"); + baldr::GraphReader reader(map.config.get_child("mjolnir")); + + auto inbound_service = gurka::findEdge(reader, layout, "IDB", "B"); + EXPECT_EQ(std::get<1>(inbound_service)->classification(), valhalla::baldr::RoadClass::kPrimary); +} + +TEST(Standalone, ConsiderBlockedRoads) { + // Nodes that block the path should be considered when the fastest way from ferry to high class road + // is evaluated, as otherwise found shortest path might be not traversable + + const std::string ascii_map = R"( + A-------B M + | | + | | + D---C-1-N + / | + E | + \ | + F-------O + | + P + + )"; + + const gurka::ways ways = { + {"AB", + {{"motor_vehicle", "yes"}, + {"motorcar", "yes"}, + {"bicycle", "yes"}, + {"moped", "yes"}, + {"bus", "yes"}, + {"hov", "yes"}, + {"taxi", "yes"}, + {"motorcycle", "yes"}, + {"route", "ferry"}}}, + + // shorter road that is blocked by block + {"C1N", {{"highway", "service"}}}, + + // longer unblocked road + {"BC", {{"highway", "service"}}}, + {"CD", {{"highway", "service"}}}, + {"DE", {{"highway", "service"}}}, + {"EF", {{"highway", "service"}}}, + {"FO", {{"highway", "service"}}}, + + {"MNOP", {{"highway", "primary"}}}, + }; + + const gurka::nodes nodes = { + {"1", + { + {"barrier", "block"}, + {"motor_vehicle", "no"}, + }}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 500); + auto map = gurka::buildtiles(layout, ways, nodes, {}, "test/data/gurka_reclassify_block"); + + // sanity check that the longer route is taken + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "M"}, "auto"); + gurka::assert::raw::expect_path(result, {"AB", "BC", "CD", "DE", "EF", "FO", "MNOP", "MNOP"}); + + baldr::GraphReader reader(map.config.get_child("mjolnir")); + + // long not blocked road should be reclassified + for (const auto& name : {"BC", "CD", "DE", "EF", "FO"}) { + const auto way = gurka::findEdge(reader, layout, name, name + 1); + EXPECT_EQ(std::get<1>(way)->classification(), valhalla::baldr::RoadClass::kPrimary); + } + + // short blocked route is not reclassified + auto blocked = gurka::findEdge(reader, layout, "C1N", "N"); + EXPECT_EQ(std::get<1>(blocked)->classification(), valhalla::baldr::RoadClass::kServiceOther); +} + +TEST(Standalone, ReclassifyNothingReclassified) { + // Test to validate that if no edges are found with the target classification + // nothing gets reclassified. + + const std::string ascii_map = R"( + A--B--C--D------E + )"; + + std::map secondary = {{"highway", "secondary"}}; + std::map tertiary = {{"highway", "tertiary"}}; + + const gurka::ways ways = { + {"AB", secondary}, + {"BC", secondary}, + {"CD", tertiary}, + {"DE", + {{"motor_vehicle", "yes"}, + {"motorcar", "yes"}, + {"bicycle", "yes"}, + {"moped", "yes"}, + {"bus", "yes"}, + {"hov", "yes"}, + {"taxi", "yes"}, + {"motorcycle", "yes"}, + {"route", "ferry"}}}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 500); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_reclassify_nothing"); + baldr::GraphReader reader(map.config.get_child("mjolnir")); + + // make sure no edges are upclassed + auto not_upclassed1 = gurka::findEdge(reader, layout, "AB", "B"); + EXPECT_TRUE(std::get<1>(not_upclassed1)->classification() == + valhalla::baldr::RoadClass::kSecondary); + auto not_upclassed2 = gurka::findEdge(reader, layout, "BC", "C"); + EXPECT_TRUE(std::get<1>(not_upclassed2)->classification() == + valhalla::baldr::RoadClass::kSecondary); + auto not_upclassed3 = gurka::findEdge(reader, layout, "CD", "D"); + EXPECT_TRUE(std::get<1>(not_upclassed3)->classification() == valhalla::baldr::RoadClass::kTertiary); +} + INSTANTIATE_TEST_SUITE_P(FerryConnectionTest, FerryTest, ::testing::Values("motorcar", "hgv", "moped", "motorcycle", "taxi", "bus")); diff --git a/test/gurka/test_filter.cc b/test/gurka/test_filter.cc new file mode 100644 index 0000000000..5c129dda4c --- /dev/null +++ b/test/gurka/test_filter.cc @@ -0,0 +1,82 @@ +#include "baldr/graphreader.h" +#include "mjolnir/util.h" + +#include "gurka.h" +#include "test/test.h" + +using namespace valhalla; +using namespace valhalla::baldr; +using namespace valhalla::gurka; + +class FilterData : public ::testing::Test { +protected: + static gurka::map the_map; + + static void SetUpTestSuite() { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + A--------B--------C + | + | + | + D + )"; + + /* + * Notice that we have a different name in the forward + * and backward direction. This test confirms that + * if we filter out footways (i.e., pedestrian edges), + * the names are still created correctly in the forward + * and backward direction + */ + const gurka::ways ways = { + {"ABC", + {{"highway", "residential"}, + {"name:forward", "name in forward dir"}, + {"name:backward", "name in backward dir"}, + {"osm_id", "100"}}}, + {"BD", {{"highway", "footway"}}}, + }; + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-82.68811, 40.22535}); + + the_map = + gurka::buildtiles(layout, ways, {}, {}, VALHALLA_BUILD_DIR "test/data/gurka_filter", + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "false"}}); + } +}; + +gurka::map FilterData::the_map = {}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(FilterData, CheckStreetNamesAfterFilter) { + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId ABC_edge_id; + const DirectedEdge* ABC_edge = nullptr; + GraphId CBA_edge_id; + const DirectedEdge* CBA_edge = nullptr; + std::tie(ABC_edge_id, ABC_edge, CBA_edge_id, CBA_edge) = + findEdge(graph_reader, the_map.nodes, "", "C", baldr::GraphId{}, 100); + EXPECT_NE(ABC_edge, nullptr); + EXPECT_NE(CBA_edge, nullptr); + + GraphId node_id = ABC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(ABC_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 1); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "name in forward dir"); + + node_id = CBA_edge->endnode(); + tile = graph_reader.GetGraphTile(node_id); + edgeinfo = tile->edgeinfo(CBA_edge); + + names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 1); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "name in backward dir"); +} diff --git a/test/gurka/test_fixed_speed.cc b/test/gurka/test_fixed_speed.cc index 0e22c84a18..84528f53df 100644 --- a/test/gurka/test_fixed_speed.cc +++ b/test/gurka/test_fixed_speed.cc @@ -1,7 +1,6 @@ #include "baldr/graphconstants.h" #include "gurka.h" #include "test.h" -#include #include using namespace valhalla; diff --git a/test/gurka/test_graphfilter.cc b/test/gurka/test_graphfilter.cc new file mode 100644 index 0000000000..75c541c241 --- /dev/null +++ b/test/gurka/test_graphfilter.cc @@ -0,0 +1,582 @@ +#include "baldr/graphreader.h" +#include "mjolnir/util.h" + +#include "gurka.h" +#include "test/test.h" + +using namespace valhalla; +using namespace valhalla::baldr; +using namespace valhalla::gurka; + +const std::string work_dir = {VALHALLA_BUILD_DIR "test/data/gurka_graphfilter"}; + +TEST(Standalone, SimpleFilter) { + constexpr double gridsize_metres = 10; + + const std::string ascii_map = R"( + + M P R W Y + | | | | | + | | | | | + A----B----C----D----E----F----G----H----I----J----K----L + | | | | | | + | | Q----S----T | | + N O | V X + | + U + )"; + + const gurka::ways ways = { + {"ABCDEFGHIJKL", {{"highway", "residential"}, {"osm_id", "100"}, {"name", "East Fort Avenue"}}}, + {"BM", {{"highway", "residential"}, {"osm_id", "101"}, {"name", "Hull Street"}}}, + {"CN", + {{"highway", "residential"}, + {"osm_id", "102"}, + {"name", "Latrobe Park Terrace"}, + {"oneway", "yes"}}}, + {"DO", {{"highway", "footway"}, {"osm_id", "103"}, {"foot", "yes"}}}, + {"EP", + {{"highway", "residential"}, + {"osm_id", "104"}, + {"name", "Cooksie Street"}, + {"oneway", "yes"}}}, + {"FQ", {{"highway", "footway"}, {"osm_id", "105"}, {"foot", "yes"}}}, + {"QST", {{"highway", "footway"}, {"osm_id", "106"}, {"foot", "yes"}}}, + {"SU", {{"highway", "footway"}, {"osm_id", "107"}, {"foot", "yes"}}}, + {"GR", {{"highway", "residential"}, {"osm_id", "108"}, {"name", "Towson Street"}}}, + {"HT", {{"highway", "footway"}, {"osm_id", "109"}, {"foot", "yes"}}}, + {"IV", {{"highway", "footway"}, {"osm_id", "110"}, {"foot", "yes"}}}, + {"JW", {{"highway", "residential"}, {"osm_id", "111"}, {"name", "Richardson Street"}}}, + {"YKX", {{"highway", "residential"}, {"osm_id", "112"}, {"name", "Andre Street"}}}, + }; + + auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-76.59223, 39.26825}); + auto map = + gurka::buildtiles(layout, ways, {}, {}, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "false"}}); + + // all footways will be deleted. check for DO + { + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + GraphId DO_edge_id; + const DirectedEdge* DO_edge = nullptr; + GraphId OD_edge_id; + const DirectedEdge* OD_edge = nullptr; + std::tie(DO_edge_id, DO_edge, OD_edge_id, OD_edge) = + findEdge(graph_reader, map.nodes, "DO", "O", baldr::GraphId{}); + EXPECT_EQ(DO_edge, nullptr); + EXPECT_EQ(OD_edge, nullptr); + } + + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "L"}, "auto"); + gurka::assert::raw::expect_path(result, {"East Fort Avenue", "East Fort Avenue", "East Fort Avenue", + "East Fort Avenue", "East Fort Avenue", "East Fort Avenue", + "East Fort Avenue"}); + + // reconvert again, but include pedestrian edges + map = gurka::buildtiles(layout, ways, {}, {}, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "true"}}); + + // all footways will not be deleted + { + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + GraphId DO_edge_id; + const DirectedEdge* DO_edge = nullptr; + GraphId OD_edge_id; + const DirectedEdge* OD_edge = nullptr; + std::tie(DO_edge_id, DO_edge, OD_edge_id, OD_edge) = + findEdge(graph_reader, map.nodes, "DO", "O", baldr::GraphId{}); + EXPECT_NE(DO_edge, nullptr); + EXPECT_NE(OD_edge, nullptr); + } + + // more edges will exist + result = gurka::do_action(valhalla::Options::route, map, {"A", "L"}, "auto"); + gurka::assert::raw::expect_path(result, {"East Fort Avenue", "East Fort Avenue", "East Fort Avenue", + "East Fort Avenue", "East Fort Avenue", "East Fort Avenue", + "East Fort Avenue", "East Fort Avenue", "East Fort Avenue", + "East Fort Avenue", "East Fort Avenue"}); +} + +TEST(Standalone, SimpleFilter2) { + + constexpr double gridsize_metres = 10; + + const std::string ascii_map = R"( + + J----K----L----M----------------------N + | | | | | + | H I | | + | | | | | + A----B----C----D----E------------F----G + | | \ / | + | | \ / | + O P Q------R S + )"; + + const gurka::ways ways = { + {"JKLMN", + {{"highway", "secondary"}, + {"osm_id", "100"}, + {"bicycle", "no"}, + {"foot", "no"}, + {"name", "Vondellaan"}, + {"oneway", "yes"}}}, + {"ABCDEFG", {{"highway", "cycleway"}, {"osm_id", "101"}, {"oneway", "yes"}}}, + {"JAO", + {{"highway", "residential"}, + {"osm_id", "102"}, + {"bicycle", "no"}, + {"foot", "no"}, + {"name", "Croesestraat"}, + {"oneway", "yes"}, + {"oneway:bicycle", "no"}}}, + {"BHK", {{"highway", "cycleway"}, {"osm_id", "103"}, {"oneway", "yes"}}}, + {"CIL", {{"highway", "footway"}, {"osm_id", "104"}}}, + {"MDP", {{"highway", "service"}, {"osm_id", "105"}, {"access", "private"}}}, + {"EQRF", + {{"highway", "service"}, {"osm_id", "106"}, {"access", "delivery"}, {"oneway", "yes"}}}, + {"NG", {{"highway", "unclassified"}, {"osm_id", "107"}, {"name", "Oosterkade"}}}, + {"GS", {{"highway", "cycleway"}, {"osm_id", "108"}}}, + }; + + const gurka::nodes nodes = { + {"H", {{"highway", "traffic_signals"}}}, + {"I", {{"highway", "traffic_signals"}, {"traffic_signals:direction", "backward"}}}, + }; + + auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {5.11668, 52.07826}); + auto map = + gurka::buildtiles(layout, ways, nodes, {}, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "false"}}); + + // CIL should be deleted + { + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + GraphId CL_edge_id; + const DirectedEdge* CL_edge = nullptr; + GraphId LC_edge_id; + const DirectedEdge* LC_edge = nullptr; + std::tie(CL_edge_id, CL_edge, LC_edge_id, LC_edge) = + findEdge(graph_reader, map.nodes, "CIL", "L", baldr::GraphId{}); + EXPECT_EQ(CL_edge, nullptr); + EXPECT_EQ(LC_edge, nullptr); + } + + auto result = gurka::do_action(valhalla::Options::route, map, {"J", "N"}, "auto"); + gurka::assert::raw::expect_path(result, {"Vondellaan", "Vondellaan", "Vondellaan"}); + + // reconvert again, but include pedestrian edges + map = gurka::buildtiles(layout, ways, nodes, {}, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "true"}}); + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + + // CIL should not be deleted + { + GraphId CL_edge_id; + const DirectedEdge* CL_edge = nullptr; + GraphId LC_edge_id; + const DirectedEdge* LC_edge = nullptr; + std::tie(CL_edge_id, CL_edge, LC_edge_id, LC_edge) = + findEdge(graph_reader, map.nodes, "CIL", "L", baldr::GraphId{}); + EXPECT_NE(CL_edge, nullptr); + EXPECT_NE(LC_edge, nullptr); + } + + // should have an extra edge since CIL was not deleted + result = gurka::do_action(valhalla::Options::route, map, {"J", "N"}, "auto"); + gurka::assert::raw::expect_path(result, {"Vondellaan", "Vondellaan", "Vondellaan", "Vondellaan"}); +} + +TEST(Standalone, FilterTestComplexRestrictionsSignals) { + constexpr double gridsize_metres = 10; + + const std::string ascii_map = R"( + L + | + | + M--N + | | + | | + A------------------B--C----D-----------E + | | | + F------------------G--H----I-----------J + | + | + K + )"; + + const gurka::ways ways = { + {"ED", + {{"highway", "primary"}, + {"osm_id", "100"}, + {"oneway", "yes"}, + {"name", "East Houston Street"}}}, + {"DCB", + {{"highway", "primary"}, + {"osm_id", "101"}, + {"oneway", "yes"}, + {"name", "East Houston Street"}}}, + {"BA", + {{"highway", "primary"}, + {"osm_id", "102"}, + {"oneway", "yes"}, + {"name", "East Houston Street"}}}, + {"AF", {{"highway", "tertiary"}, {"osm_id", "103"}}}, + {"FG", + {{"highway", "primary"}, + {"osm_id", "104"}, + {"oneway", "yes"}, + {"name", "East Houston Street"}}}, + {"GHI", + {{"highway", "primary"}, + {"osm_id", "105"}, + {"oneway", "yes"}, + {"name", "East Houston Street"}}}, + {"IJ", + {{"highway", "primary"}, + {"osm_id", "106"}, + {"oneway", "yes"}, + {"name", "East Houston Street"}}}, + {"LMB", {{"highway", "tertiary"}, {"osm_id", "107"}, {"name", "Avenue B"}}}, + {"BG", {{"highway", "tertiary"}, {"osm_id", "108"}, {"name", "Avenue B"}}}, + {"GK", {{"highway", "tertiary"}, {"osm_id", "109"}, {"name", "Avenue B"}}}, + {"MN", {{"highway", "footway"}, {"osm_id", "110"}, {"foot", "yes"}}}, + {"NCH", {{"highway", "footway"}, {"osm_id", "111"}, {"foot", "yes"}}}, + }; + + const gurka::relations relations = { + {{ + {gurka::way_member, "DCB", "from"}, + {gurka::way_member, "BG", "via"}, + {gurka::way_member, "GHI", "to"}, + }, + { + {"type", "restriction"}, + {"restriction", "no_u_turn"}, + }}, + }; + + const gurka::nodes nodes = { + {"M", {{"highway", "traffic_signals"}}}, + }; + + auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-73.98311, 40.72122}); + auto map = + gurka::buildtiles(layout, ways, nodes, relations, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "false"}}); + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + + // even though we filitered pedestrian edges, make sure the restriction is not deleted. + auto result = gurka::do_action(valhalla::Options::route, map, {"E", "I"}, "auto"); + gurka::assert::raw::expect_path(result, {"East Houston Street", "East Houston Street", + "East Houston Street", "East Houston Street", "AF", + "East Houston Street", "East Houston Street", + "East Houston Street"}); + + // even though we filitered pedestrian edges, make sure we don't split at the traffic signal + result = gurka::do_action(valhalla::Options::route, map, {"L", "K"}, "auto"); + gurka::assert::raw::expect_path(result, {"Avenue B", "Avenue B", "Avenue B", "Avenue B"}); +} + +TEST(Standalone, FilterTestSimpleRestrictions) { + constexpr double gridsize_metres = 10; + + const std::string ascii_map = R"( + + + H----------D----------I + / \ + / \ + J-------E-----C-------K + / \ + F B + | | + | | + | | + G A + )"; + + const gurka::ways ways = { + {"ABCD", + {{"highway", "primary"}, {"osm_id", "100"}, {"oneway", "yes"}, {"name", "Washington Avenue"}}}, + {"DEFG", + {{"highway", "primary"}, {"osm_id", "101"}, {"oneway", "yes"}, {"name", "Washington Avenue"}}}, + {"HD", {{"highway", "tertiary"}, {"osm_id", "102"}, {"name", "State Street"}}}, + {"DI", {{"highway", "tertiary"}, {"osm_id", "103"}, {"name", "T Street"}}}, + {"JECK", {{"highway", "footway"}, {"osm_id", "104"}, {"foot", "yes"}}}, + }; + + const gurka::relations relations = { + {{ + {gurka::way_member, "ABCD", "from"}, + {gurka::node_member, "D", "via"}, + {gurka::way_member, "DEFG", "to"}, + }, + { + {"type", "restriction"}, + {"restriction", "no_u_turn"}, + }}, + }; + + auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-73.94913, 42.81490}); + auto map = + gurka::buildtiles(layout, ways, {}, relations, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "false"}}); + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + + // even though we filitered pedestrian edges, make sure the simple restriction is not deleted. We + // make a uturn at the H node + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, "auto"); + gurka::assert::raw::expect_path(result, {"Washington Avenue", "Washington Avenue", "State Street", + "State Street", "Washington Avenue"}); + + // JECK should be deleted + { + GraphId JK_edge_id; + const DirectedEdge* JK_edge = nullptr; + GraphId KJ_edge_id; + const DirectedEdge* KJ_edge = nullptr; + std::tie(JK_edge_id, JK_edge, KJ_edge_id, KJ_edge) = + findEdge(graph_reader, map.nodes, "JECK", "K", baldr::GraphId{}); + EXPECT_EQ(JK_edge, nullptr); + EXPECT_EQ(KJ_edge, nullptr); + } +} + +TEST(Standalone, FilterTestNodeTypeSignals) { + constexpr double gridsize_metres = 10; + + const std::string ascii_map = R"( + + F + | + G--H + | | + | | + A----------B--C-------D + | + | + E + + )"; + + const gurka::ways ways = { + {"ABCD", {{"highway", "secondary"}, {"osm_id", "100"}}}, + {"FGBE", {{"highway", "secondary"}, {"osm_id", "101"}}}, + {"GHC", {{"highway", "footway"}, {"osm_id", "102"}, {"foot", "yes"}}}, + }; + + const gurka::nodes nodes = { + {"C", {{"highway", "traffic_signals"}}}, + {"G", {{"barrier", "gate"}}}, + }; + + auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-73.94913, 42.81490}); + auto map = + gurka::buildtiles(layout, ways, nodes, {}, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "false"}}); + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + + // even though we filitered pedestrian edges there should be 3 edges as ABCD has a signal set on the + // edge + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "auto"); + gurka::assert::raw::expect_path(result, {"ABCD", "ABCD", "ABCD"}); + + // even though we filitered pedestrian edges there should be 3 edges as G is a gate + result = gurka::do_action(valhalla::Options::route, map, {"F", "E"}, "auto"); + gurka::assert::raw::expect_path(result, {"FGBE", "FGBE", "FGBE"}); + + // GHC should be deleted + { + GraphId GC_edge_id; + const DirectedEdge* GC_edge = nullptr; + GraphId CG_edge_id; + const DirectedEdge* CG_edge = nullptr; + std::tie(GC_edge_id, GC_edge, CG_edge_id, CG_edge) = + findEdge(graph_reader, map.nodes, "GHC", "C", baldr::GraphId{}); + EXPECT_EQ(GC_edge, nullptr); + EXPECT_EQ(CG_edge, nullptr); + } +} + +TEST(Standalone, FilterTestMultipleEdges) { + constexpr double gridsize_metres = 50; + + const std::string ascii_map = R"( + + A---B---C---D---E---F---G---H---I + | | | | | | | + | | | | | | | + J K L M N O P + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "secondary"}, {"osm_id", "100"}}}, + {"BC", {{"highway", "secondary"}, {"osm_id", "101"}}}, + {"CD", {{"highway", "secondary"}, {"osm_id", "102"}}}, + {"DE", {{"highway", "secondary"}, {"osm_id", "103"}}}, + {"EF", {{"highway", "secondary"}, {"osm_id", "104"}}}, + // note these next 2 have same way id + {"FG", {{"highway", "secondary"}, {"osm_id", "105"}}}, + {"GH", {{"highway", "secondary"}, {"osm_id", "105"}}}, + {"HI", {{"highway", "secondary"}, {"osm_id", "106"}}}, + {"BJ", {{"highway", "residential"}, {"osm_id", "107"}}}, + {"CK", {{"highway", "residential"}, {"osm_id", "108"}}}, + {"DL", {{"highway", "footway"}, {"osm_id", "109"}, {"foot", "yes"}}}, + {"EM", {{"highway", "residential"}, {"osm_id", "110"}}}, + {"FN", {{"highway", "residential"}, {"osm_id", "111"}}}, + {"GO", {{"highway", "footway"}, {"osm_id", "112"}, {"foot", "yes"}}}, + {"HP", {{"highway", "residential"}, {"osm_id", "113"}}}, + + }; + + auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-73.94913, 42.81490}); + auto map = + gurka::buildtiles(layout, ways, {}, {}, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "false"}}); + + // there should be 7 edges from A to I as even though DJ is a footway, CD and DE have different way + // ids FG and GH will be aggregated as they have the same way id and are separated by a footway. + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "I"}, "auto"); + gurka::assert::raw::expect_path(result, {"AB", "BC", "CD", "DE", "EF", "FG", "HI"}); + + { + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + + // DL should be deleted + GraphId DL_edge_id; + const DirectedEdge* DL_edge = nullptr; + GraphId LD_edge_id; + const DirectedEdge* LD_edge = nullptr; + std::tie(DL_edge_id, DL_edge, LD_edge_id, LD_edge) = + findEdge(graph_reader, map.nodes, "DL", "L", baldr::GraphId{}); + EXPECT_EQ(DL_edge, nullptr); + EXPECT_EQ(LD_edge, nullptr); + + // GO should be deleted + GraphId GO_edge_id; + const DirectedEdge* GO_edge = nullptr; + GraphId OG_edge_id; + const DirectedEdge* OG_edge = nullptr; + std::tie(GO_edge_id, GO_edge, OG_edge_id, OG_edge) = + findEdge(graph_reader, map.nodes, "GO", "O", baldr::GraphId{}); + EXPECT_EQ(GO_edge, nullptr); + EXPECT_EQ(OG_edge, nullptr); + + // FG should be deleted as it was aggregated + GraphId FG_edge_id; + const DirectedEdge* FG_edge = nullptr; + GraphId GF_edge_id; + const DirectedEdge* GF_edge = nullptr; + std::tie(FG_edge_id, FG_edge, GF_edge_id, GF_edge) = + findEdge(graph_reader, map.nodes, "FG", "G", baldr::GraphId{}); + EXPECT_EQ(FG_edge, nullptr); + EXPECT_EQ(GF_edge, nullptr); + + // GH should be deleted as it was aggregated + GraphId GH_edge_id; + const DirectedEdge* GH_edge = nullptr; + GraphId HG_edge_id; + const DirectedEdge* HG_edge = nullptr; + std::tie(GH_edge_id, GH_edge, HG_edge_id, HG_edge) = + findEdge(graph_reader, map.nodes, "GH", "H", baldr::GraphId{}); + EXPECT_EQ(GH_edge, nullptr); + EXPECT_EQ(HG_edge, nullptr); + + // FH is the new aggregated edge. Make sure is equal to wayid 105 + GraphId FH_edge_id; + const DirectedEdge* FH_edge = nullptr; + GraphId HF_edge_id; + const DirectedEdge* HF_edge = nullptr; + std::tie(FH_edge_id, FH_edge, HF_edge_id, HF_edge) = + findEdge(graph_reader, map.nodes, "FG", "H", baldr::GraphId{}, 105); + EXPECT_NE(FH_edge, nullptr); + EXPECT_NE(HF_edge, nullptr); + + auto FH = gurka::findEdgeByNodes(graph_reader, layout, "F", "H"); + auto tile = graph_reader.GetGraphTile(std::get<1>(FH)->endnode()); + EXPECT_EQ(tile->edgeinfo(std::get<1>(FH)).wayid(), 105); + } + + // save and check later + auto shape = result.trip().routes(0).legs().begin()->shape(); + + // Now run again with include_pedestrian = true. all footways should be back. + map = gurka::buildtiles(layout, ways, {}, {}, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "true"}}); + + { + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + + // there should be 8 edges from A to I + result = gurka::do_action(valhalla::Options::route, map, {"A", "I"}, "auto"); + gurka::assert::raw::expect_path(result, {"AB", "BC", "CD", "DE", "EF", "FG", "GH", "HI"}); + // DL should not be deleted + + GraphId DL_edge_id; + const DirectedEdge* DL_edge = nullptr; + GraphId LD_edge_id; + const DirectedEdge* LD_edge = nullptr; + std::tie(DL_edge_id, DL_edge, LD_edge_id, LD_edge) = + findEdge(graph_reader, map.nodes, "DL", "L", baldr::GraphId{}); + EXPECT_NE(DL_edge, nullptr); + EXPECT_NE(LD_edge, nullptr); + + // GO should not be deleted + GraphId GO_edge_id; + const DirectedEdge* GO_edge = nullptr; + GraphId OG_edge_id; + const DirectedEdge* OG_edge = nullptr; + std::tie(GO_edge_id, GO_edge, OG_edge_id, OG_edge) = + findEdge(graph_reader, map.nodes, "GO", "O", baldr::GraphId{}); + EXPECT_NE(GO_edge, nullptr); + EXPECT_NE(OG_edge, nullptr); + + // FG should not be deleted + GraphId FG_edge_id; + const DirectedEdge* FG_edge = nullptr; + GraphId GF_edge_id; + const DirectedEdge* GF_edge = nullptr; + std::tie(FG_edge_id, FG_edge, GF_edge_id, GF_edge) = + findEdge(graph_reader, map.nodes, "FG", "G", baldr::GraphId{}); + EXPECT_NE(FG_edge, nullptr); + EXPECT_NE(GF_edge, nullptr); + + auto tile = graph_reader.GetGraphTile(FG_edge->endnode()); + EXPECT_EQ(tile->edgeinfo(FG_edge).wayid(), 105); + + tile = graph_reader.GetGraphTile(GF_edge->endnode()); + EXPECT_EQ(tile->edgeinfo(GF_edge).wayid(), 105); + + // GH should not be deleted + GraphId GH_edge_id; + const DirectedEdge* GH_edge = nullptr; + GraphId HG_edge_id; + const DirectedEdge* HG_edge = nullptr; + std::tie(GH_edge_id, GH_edge, HG_edge_id, HG_edge) = + findEdge(graph_reader, map.nodes, "GH", "H", baldr::GraphId{}); + EXPECT_NE(GH_edge, nullptr); + EXPECT_NE(HG_edge, nullptr); + + tile = graph_reader.GetGraphTile(GH_edge->endnode()); + EXPECT_EQ(tile->edgeinfo(GH_edge).wayid(), 105); + + tile = graph_reader.GetGraphTile(HG_edge->endnode()); + EXPECT_EQ(tile->edgeinfo(HG_edge).wayid(), 105); + + // FH should not exist + ASSERT_THROW(gurka::findEdgeByNodes(graph_reader, layout, "F", "H"), std::runtime_error); + } + // the shape between including the pedestrian edges or not, should match + EXPECT_EQ(shape, result.trip().routes(0).legs().begin()->shape()); +} diff --git a/test/gurka/test_graphreader.cc b/test/gurka/test_graphreader.cc new file mode 100644 index 0000000000..9616ebd72c --- /dev/null +++ b/test/gurka/test_graphreader.cc @@ -0,0 +1,169 @@ +#include "gurka.h" +#include + +using namespace valhalla; + +TEST(GraphReader, ModifyTilePointerByReference) { + // 2km long way crosses a tile boundary at the prime meridian due to 1km offset from null island + const std::string ascii_map = R"(A------------------B)"; + const gurka::ways ways = {{"AB", {{"highway", "motorway"}, {"name", "81"}}}}; + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100, {-.01, -.01}); + const auto map = + gurka::buildtiles(layout, ways, {}, {}, "test/data/graphreader_tileboundary", + { + {"mjolnir.incident_dir", "test/data/graphreader_tileboundary"}, + {"mjolnir.incident_log", "test/data/graphreader_tileboundary/log"}, + }); + + // both sets of edges that cross the tile boundary from left to right and right to left + baldr::GraphReader reader(map.config.get_child("mjolnir")); + auto ltr = gurka::findEdgeByNodes(reader, layout, "A", "B"); + auto rtl = gurka::findEdgeByNodes(reader, layout, "B", "A"); + auto l = gurka::findNode(reader, layout, "A"); + auto r = gurka::findNode(reader, layout, "B"); + + // all methods that use/modify a reference to tile pointer with two calls for cache miss and hit + for (size_t i = 0; i < 2; ++i) { + baldr::GraphId edge_id, opp_edge_id; + const baldr::DirectedEdge *edge, *opp_edge; + std::tie(edge_id, edge) = ltr; + std::tie(opp_edge_id, opp_edge) = rtl; + auto begin_node_id = l; + auto end_node_id = r; + + { + baldr::graph_tile_ptr tile; + reader.GetGraphTile(begin_node_id, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + reader.GetGraphTile(begin_node_id, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + } + + { + baldr::graph_tile_ptr tile; + reader.GetOpposingEdgeId(edge_id, tile); + ASSERT_EQ(opp_edge_id.Tile_Base(), tile->id()); + reader.GetOpposingEdgeId(edge_id, tile); + ASSERT_EQ(opp_edge_id.Tile_Base(), tile->id()); + } + + { + baldr::graph_tile_ptr tile; + const baldr::DirectedEdge* actual_op_edge; + reader.GetOpposingEdgeId(edge_id, actual_op_edge, tile); + ASSERT_EQ(opp_edge_id.Tile_Base(), tile->id()); + ASSERT_EQ(opp_edge, actual_op_edge); + reader.GetOpposingEdgeId(edge_id, tile); + ASSERT_EQ(opp_edge_id.Tile_Base(), tile->id()); + ASSERT_EQ(opp_edge, actual_op_edge); + } + + { + baldr::graph_tile_ptr tile; + reader.GetOpposingEdge(edge_id, tile); + ASSERT_EQ(opp_edge_id.Tile_Base(), tile->id()); + reader.GetOpposingEdge(edge_id, tile); + ASSERT_EQ(opp_edge_id.Tile_Base(), tile->id()); + } + + { + baldr::graph_tile_ptr tile; + reader.GetOpposingEdge(edge, tile); + ASSERT_EQ(opp_edge_id.Tile_Base(), tile->id()); + reader.GetOpposingEdge(edge, tile); + ASSERT_EQ(opp_edge_id.Tile_Base(), tile->id()); + } + + { + baldr::graph_tile_ptr tile; + reader.GetEndNode(edge, tile); + ASSERT_EQ(end_node_id.Tile_Base(), tile->id()); + reader.GetEndNode(edge, tile); + ASSERT_EQ(end_node_id.Tile_Base(), tile->id()); + } + + { + baldr::graph_tile_ptr tile; + reader.GetBeginNodeId(edge, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + reader.GetBeginNodeId(edge, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + } + + { + baldr::graph_tile_ptr tile; + reader.AreEdgesConnectedForward(edge_id, opp_edge_id, tile); + ASSERT_EQ(end_node_id.Tile_Base(), tile->id()); + reader.AreEdgesConnectedForward(edge_id, opp_edge_id, tile); + ASSERT_EQ(end_node_id.Tile_Base(), tile->id()); + } + + { + baldr::graph_tile_ptr tile; + reader.nodeinfo(begin_node_id, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + reader.nodeinfo(begin_node_id, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + } + + { + baldr::graph_tile_ptr tile; + reader.directededge(edge_id, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + reader.directededge(edge_id, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + } + + { + baldr::graph_tile_ptr tile; + reader.GetDirectedEdgeNodes(edge_id, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + reader.GetDirectedEdgeNodes(edge_id, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + } + + { + baldr::graph_tile_ptr tile; + reader.edge_endnode(edge_id, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + reader.edge_endnode(edge_id, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + } + + { + baldr::graph_tile_ptr tile; + reader.edge_startnode(edge_id, tile); + ASSERT_EQ(end_node_id.Tile_Base(), tile->id()); + reader.edge_startnode(edge_id, tile); + ASSERT_EQ(end_node_id.Tile_Base(), tile->id()); + } + + { + baldr::graph_tile_ptr tile; + reader.edgeinfo(edge_id, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + reader.edgeinfo(edge_id, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + } + + { + baldr::graph_tile_ptr tile; + reader.GetTimezone(begin_node_id, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + reader.GetTimezone(begin_node_id, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + } + + { + baldr::graph_tile_ptr tile; + reader.GetIncidents(begin_node_id, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + reader.GetIncidents(begin_node_id, tile); + ASSERT_EQ(begin_node_id.Tile_Base(), tile->id()); + } + + // swap left and right + std::swap(ltr, rtl); + std::swap(l, r); + } +} \ No newline at end of file diff --git a/test/gurka/test_gtfs.cc b/test/gurka/test_gtfs.cc index 1538386674..7ad51bac8c 100644 --- a/test/gurka/test_gtfs.cc +++ b/test/gurka/test_gtfs.cc @@ -492,7 +492,7 @@ TEST(GtfsExample, WriteGtfs) { auto stops = f1_reader.get_stops(); EXPECT_EQ(stops.size(), 6); - EXPECT_EQ(stops[0].stop_id, st1_id + "_egress"); + EXPECT_EQ(stops[0].stop_id, st1_id); auto shapes = f1_reader.get_shapes(); EXPECT_EQ(shapes.size(), 6); @@ -847,7 +847,7 @@ TEST(GtfsExample, MakeTile) { } EXPECT_EQ(transit_nodes, 15); - EXPECT_EQ(uses[Use::kRoad], 12); + EXPECT_EQ(uses[Use::kRoad], 14); // + 2 shortcuts EXPECT_EQ(uses[Use::kTransitConnection], 20); EXPECT_EQ(uses[Use::kPlatformConnection], 10); EXPECT_EQ(uses[Use::kEgressConnection], 10); @@ -856,21 +856,13 @@ TEST(GtfsExample, MakeTile) { } TEST(GtfsExample, route_trip1) { - // here we request with the relative current time for tmrw 05:50 am - auto req_time = DateTime::iso_date_time(DateTime::get_tz_db().from_index(145)); - size_t tmrw_int = std::stoul(std::string(&req_time[8], &req_time[10])) + 1; - // wrap around the next month if it's getting critical - if (tmrw_int > 27) { - auto new_month = std::to_string(std::stoul(std::string(&req_time[5], &req_time[7])) + 1); - new_month = std::string(2 - std::min(2UL, new_month.length()), '0') + new_month; - req_time.replace(req_time.find('T') - 5, 2, new_month); - tmrw_int -= 20; - } - auto tmrw_str = std::to_string(tmrw_int); - tmrw_str = std::string(2 - std::min(2UL, tmrw_str.length()), '0') + tmrw_str; - // replace the day and the time - req_time.replace(req_time.find('T') - 2, 2, tmrw_str); - req_time.replace(req_time.find('T') + 1, 5, "05:50"); + // here we request with tmrw 05:50 am + const auto tmrw_time = + date::floor(std::chrono::system_clock::now() + std::chrono::hours(24)) + + std::chrono::hours(5) + std::chrono::minutes(50); + std::ostringstream iso_date_time; + iso_date_time << date::format("%FT%R", tmrw_time); + std::string req_time = iso_date_time.str(); std::string res_json; valhalla::Api res = @@ -928,15 +920,27 @@ TEST(GtfsExample, route_trip1) { EXPECT_FALSE(ti_json["transit_stops"][0].HasMember("arrival_date_time")); EXPECT_FALSE(ti_json["transit_stops"][2].HasMember("departure_date_time")); - // determine the right day - req_time.append("-04:00"); // TODO: why -04:00, not -05:00?? - req_time.replace(req_time.find('T') + 1, 5, "07:00"); - EXPECT_EQ(transit_info.transit_stops(0).departure_date_time(), req_time); - EXPECT_EQ(ti_json["transit_stops"][0]["departure_date_time"].GetString(), req_time); + // determine the right departure datetimes, keep the timezone "+" + req_time.replace(req_time.find('T') + 1, 5, "07:00-"); + + auto dep_time = transit_info.transit_stops(0).departure_date_time(); + dep_time.erase(dep_time.rfind('-') + 1); + std::string dep_time_json = ti_json["transit_stops"][0]["departure_date_time"].GetString(); + dep_time_json.erase(dep_time_json.rfind('-') + 1); + + EXPECT_EQ(dep_time, req_time); + EXPECT_EQ(dep_time_json, req_time); + + // determine the right arrival datetimes, keep the timezone "+" + req_time.replace(req_time.find('T') + 1, 6, "07:06-"); + + auto arr_time = transit_info.transit_stops(2).arrival_date_time(); + arr_time.erase(arr_time.rfind('-') + 1); + std::string arr_time_json = ti_json["transit_stops"][2]["arrival_date_time"].GetString(); + arr_time_json.erase(arr_time_json.rfind('-') + 1); - req_time.replace(req_time.find('T') + 1, 5, "07:06"); - EXPECT_EQ(transit_info.transit_stops(2).arrival_date_time(), req_time); - EXPECT_EQ(ti_json["transit_stops"][2]["arrival_date_time"].GetString(), req_time); + EXPECT_EQ(arr_time_json, req_time); + EXPECT_EQ(arr_time, req_time); } TEST(GtfsExample, route_trip4) { diff --git a/test/gurka/test_guidance_views.cc b/test/gurka/test_guidance_views.cc index 9031a022c3..2a3b4fb366 100644 --- a/test/gurka/test_guidance_views.cc +++ b/test/gurka/test_guidance_views.cc @@ -12,7 +12,6 @@ class GuidanceViews : public ::testing::Test { static gurka::map map; static void SetUpTestSuite() { - constexpr double gridsize = 100; // A--B-BASE-C--D-OVERLAY-E--F const std::string ascii_map = R"( diff --git a/test/gurka/test_guidance_views_signposts.cc b/test/gurka/test_guidance_views_signposts.cc index 87456bd236..4cdcc79b13 100644 --- a/test/gurka/test_guidance_views_signposts.cc +++ b/test/gurka/test_guidance_views_signposts.cc @@ -12,7 +12,6 @@ class GuidanceViews_Signboards : public ::testing::Test { static gurka::map map; static void SetUpTestSuite() { - constexpr double gridsize = 100; // A--B-C-SIGNBOARD-D-E--F const std::string ascii_map = R"( diff --git a/test/gurka/test_hov.cc b/test/gurka/test_hov.cc index b77d9ebdff..d8a313f83f 100644 --- a/test/gurka/test_hov.cc +++ b/test/gurka/test_hov.cc @@ -1,5 +1,6 @@ #include "gurka.h" #include "test.h" +#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_ignore_restrictions.cc b/test/gurka/test_ignore_restrictions.cc new file mode 100644 index 0000000000..6ccf458cba --- /dev/null +++ b/test/gurka/test_ignore_restrictions.cc @@ -0,0 +1,180 @@ + +#include "gurka.h" +#include "test.h" +#include + +using namespace valhalla; + +std::string get_access_mode(const std::string& costing_mode) { + if (costing_mode == "auto") { + return "motorcar"; + } else if (costing_mode == "truck") { + return "hgv"; + } else if (costing_mode == "motorcycle") { + return "motorcycle"; + } else if (costing_mode == "taxi") { + return "taxi"; + } else if (costing_mode == "bus") { + return "bus"; + } else if (costing_mode == "motor_scooter") { + return "moped"; + } else if (costing_mode == "bicycle") { + return "bicycle"; + } + + throw std::runtime_error("unexpected costing mode " + costing_mode + "."); +} + +class CommonRestrictionTest : public ::testing::TestWithParam { +protected: + static gurka::nodelayout layout; + + static void SetUpTestSuite() { + constexpr double gridsize = 500; + + const std::string map = R"( + A----------B-----C----D + | + E + | + | + F + )"; + + layout = gurka::detail::map_to_coordinates(map, gridsize); + } +}; +gurka::nodelayout CommonRestrictionTest::layout = {}; + +TEST_P(CommonRestrictionTest, IgnoreCommonRestrictions) { + const std::string& costing = GetParam(); + const gurka::ways ways = { + {"AB", {{"highway", "secondary"}}}, + {"BC", {{"highway", "secondary"}}}, + {"CD", {{"highway", "secondary"}}}, + {"AE", {{"highway", "secondary"}}}, + {"EF", + {{"highway", "secondary"}, {get_access_mode(costing) + ":conditional", "no @ (09:00-18:00)"}}}, + }; + const gurka::relations relations = {{{ + {gurka::way_member, "AB", "from"}, + {gurka::way_member, "BC", "to"}, + {gurka::node_member, "B", "via"}, + }, + {{"type", "restriction"}, {"restriction", "no_straight_on"}}}}; + gurka::map map = + gurka::buildtiles(layout, ways, {}, relations, "test/data/ignore_non_vehicular_restrictions", + {{"mjolnir.timezone", {VALHALLA_BUILD_DIR "test/data/tz.sqlite"}}}); + // first, route through turn restriction, should fail... + try { + valhalla::Api route = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, costing, {}); + FAIL() << "Expected valhalla_exception_t."; + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + } + + // ...but succeed with ignore_non_vehicular_restrictions + valhalla::Api route = + gurka::do_action(valhalla::Options::route, map, {"A", "D"}, costing, + {{"/costing_options/" + costing + "/ignore_non_vehicular_restrictions", "1"}}); + gurka::assert::raw::expect_path(route, {"AB", "BC", "CD"}); + + // second, route through time based access restrictions, should fail... + try { + valhalla::Api route = + gurka::do_action(valhalla::Options::route, map, {"A", "F"}, costing, + {{"/date_time/type", "1"}, {"/date_time/value", "2020-10-10T13:00"}}); + FAIL() << "Expected route to fail."; + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { + FAIL() << "Expected different error code."; + } + + //...but succeed with ignore_non_vehicular_restrictions + valhalla::Api route_succeed = + gurka::do_action(valhalla::Options::route, map, {"A", "F"}, costing, + {{"/costing_options/" + costing + "/ignore_non_vehicular_restrictions", "1"}, + {"/date_time/type", "1"}, + {"/date_time/value", "2020-10-10T13:00"}}); + gurka::assert::raw::expect_path(route_succeed, {"AE", "EF"}); +} + +// check that dimensional restrictions are not affected +TEST_P(CommonRestrictionTest, IgnoreCommonRestrictionsFail) { + const std::string& costing = GetParam(); + + if (costing == "motorcycle" || costing == "bicycle" || costing == "motor_scooter") + return; // no height restrictions for these + + const gurka::ways ways = { + {"AB", {{"highway", "secondary"}}}, {"BC", {{"highway", "secondary"}, {"maxheight", "2.5"}}}, + {"CD", {{"highway", "secondary"}}}, {"AE", {{"highway", "secondary"}}}, + {"EF", {{"highway", "secondary"}}}, + }; + gurka::map map = + gurka::buildtiles(layout, ways, {}, {}, "test/data/ignore_non_vehicular_restrictions", + {{"mjolnir.timezone", {VALHALLA_BUILD_DIR "test/data/tz.sqlite"}}}); + // should fail, too low + try { + valhalla::Api route = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, costing, + {{"/costing_options/" + costing + "/height", "3"}}); + FAIL() << "Expected valhalla_exception_t."; + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + } + + // still too low + try { + valhalla::Api route = + gurka::do_action(valhalla::Options::route, map, {"A", "D"}, costing, + {{"/costing_options/" + costing + "/ignore_non_vehicular_restrictions", "1"}, + {"/costing_options/" + costing + "/height", "3"}}); + FAIL() << "Expected valhalla_exception_t."; + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + } +} +INSTANTIATE_TEST_SUITE_P( + CommonRestrictionsTest, + CommonRestrictionTest, + ::testing::Values("auto", "truck", "motorcycle", "taxi", "bus", "bicycle", "motor_scooter")); + +// make sure truck weight restrictions are not affected by the request parameter +TEST(CommonRestrictionsFail, Truck) { + + constexpr double gridsize = 500; + + const std::string ascii_map = R"( + A----------B-----C----D + )"; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + const gurka::ways ways = {{"AB", {{"highway", "residential"}}}, + {"BC", {{"highway", "residential"}, {"maxheight", "2.5"}}}, + {"CD", {{"highway", "residential"}}}}; + + gurka::map map = + gurka::buildtiles(layout, ways, {}, {}, "test/data/ignore_non_vehicular_restrictions_truck", + {{"mjolnir.timezone", {VALHALLA_BUILD_DIR "test/data/tz.sqlite"}}}); + + // too long + try { + valhalla::Api route = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "truck", + {{"/costing_options/truck/height", "3"}}); + + FAIL() << "Expected valhalla_exception_t."; + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { + FAIL() << "Expected to fail with a different error code."; + } + + // ...still too long + try { + valhalla::Api route = + gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "truck", + {{"/costing_options/truck/ignore_non_vehicular_restrictions", "1"}, + {"/costing_options/truck/height", "3"}}); + FAIL() << "Expected no route to be found."; + + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { + FAIL() << "Expected to fail with a different error code."; + } +} \ No newline at end of file diff --git a/test/gurka/test_indoor.cc b/test/gurka/test_indoor.cc index 41e05a723d..eaa2a6e60e 100644 --- a/test/gurka/test_indoor.cc +++ b/test/gurka/test_indoor.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_instructions_long_street_names.cc b/test/gurka/test_instructions_long_street_names.cc index 0231315694..d04c0a4c65 100644 --- a/test/gurka/test_instructions_long_street_names.cc +++ b/test/gurka/test_instructions_long_street_names.cc @@ -1,7 +1,6 @@ #include "baldr/rapidjson_utils.h" #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_instructions_named_roundabout.cc b/test/gurka/test_instructions_named_roundabout.cc index 9003e90037..27b591f79c 100644 --- a/test/gurka/test_instructions_named_roundabout.cc +++ b/test/gurka/test_instructions_named_roundabout.cc @@ -68,15 +68,17 @@ TEST_F(InstructionsNamedRoundabout, RoundaboutEnterOnly) { gurka::assert::raw::expect_maneuvers(result, {DirectionsLeg_Maneuver_Type_kStart, DirectionsLeg_Maneuver_Type_kRoundaboutEnter, DirectionsLeg_Maneuver_Type_kDestination}); - int maneuver_index = 1; // TODO: known issue - future update to end on a roundabout // Verify the enter_roundabout instructions - // gurka::assert::raw::expect_instructions_at_maneuver_index(result, maneuver_index, - // "Enter the roundabout.", - // "Enter Dupont Circle.", - // "Enter Dupont Circle.", - // "Enter Dupont Circle.", ""); +#if 0 + int maneuver_index = 1; + gurka::assert::raw::expect_instructions_at_maneuver_index(result, maneuver_index, + "Enter the roundabout.", + "Enter Dupont Circle.", + "Enter Dupont Circle.", + "Enter Dupont Circle.", ""); +#endif } // enter_roundabout_verbal diff --git a/test/gurka/test_instructions_roundabout.cc b/test/gurka/test_instructions_roundabout.cc index ace5f143d0..02325886cd 100644 --- a/test/gurka/test_instructions_roundabout.cc +++ b/test/gurka/test_instructions_roundabout.cc @@ -66,15 +66,16 @@ TEST_F(InstructionsRoundabout, RoundaboutEnterOnly) { gurka::assert::raw::expect_maneuvers(result, {DirectionsLeg_Maneuver_Type_kStart, DirectionsLeg_Maneuver_Type_kRoundaboutEnter, DirectionsLeg_Maneuver_Type_kDestination}); - int maneuver_index = 1; // TODO: known issue - future update to end on a roundabout // Verify the enter_roundabout instructions - // gurka::assert::raw::expect_instructions_at_maneuver_index(result, maneuver_index, - // "Enter the roundabout.", "Enter - // the roundabout.", "Enter the - // roundabout.", "Enter the - // roundabout.", ""); +#if 0 + gurka::assert::raw::expect_instructions_at_maneuver_index(result, maneuver_index, + "Enter the roundabout.", + "Enter the roundabout.", + "Enter the roundabout.", + "Enter the roundabout.", ""); +#endif } // enter_roundabout_verbal diff --git a/test/gurka/test_instructions_roundabout_multicue.cc b/test/gurka/test_instructions_roundabout_multicue.cc index 65db5bf747..564c79b360 100644 --- a/test/gurka/test_instructions_roundabout_multicue.cc +++ b/test/gurka/test_instructions_roundabout_multicue.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_landmarks.cc b/test/gurka/test_landmarks.cc new file mode 100644 index 0000000000..eecefd512c --- /dev/null +++ b/test/gurka/test_landmarks.cc @@ -0,0 +1,628 @@ +#include +#include +#include +#include + +#include "baldr/graphreader.h" +#include "baldr/landmark.h" +#include "gurka.h" +#include "mjolnir/graphtilebuilder.h" +#include "mjolnir/landmarks.h" +#include "odin/enhancedtrippath.h" +#include "test/test.h" + +#include + +using namespace valhalla; +using namespace valhalla::baldr; +using namespace valhalla::gurka; +using namespace valhalla::mjolnir; + +// config for the first three tests +const std::string workdir = VALHALLA_BUILD_DIR "test/data/landmarks/landmarks"; +const std::string db_path = workdir + "/landmarks.sqlite"; +const std::string pbf_filename = workdir + "/map.pbf"; +valhalla::gurka::map landmark_map; + +// config for TestAddLandmarksToTiles +const std::string workdir_tiles = VALHALLA_BUILD_DIR "test/data/landmarks/landmarks_tile"; +const std::string db_path_tile_test = workdir_tiles + "/landmarks.sqlite"; +const std::string pbf_filename_tile_test = workdir_tiles + "/map.pbf"; +valhalla::gurka::map landmark_map_tile_test; + +namespace { +inline void DisplayLandmark(const Landmark& landmark) { + std::cout << "landmark: id = " << landmark.id << ", name = " << landmark.name + << ", type = " << static_cast(landmark.type) << ", lng = " << landmark.lng + << ", lat = " << landmark.lat << std::endl; +} + +void BuildPBF() { + const std::string ascii_map = R"( + a-----b-----c---d + A B C D E + )"; + + const gurka::nodes nodes = { + {"A", {{"amenity", "bar"}}}, + {"B", {{"name", "hai di lao"}, {"amenity", "restaurant"}}}, + {"C", {{"name", "ke ji lu"}}}, // no amenity, shouldn't be stored + {"D", {{"name", "wan da"}, {"amenity", "cinema"}}}, + {"E", {{"name", "zhong lou"}, {"amenity", "monument"}}}, // not in list, shouldn't be stored + // non-landmark nodes + {"a", {{"name", "gong ce"}, {"amenity", "toilets"}}}, + {"b", {{"name", "la ji tong"}, {"amenity", "waste_basket"}}}, + {"c", {{"name", "hua yuan"}, {"place", "city"}}}, + {"d", {{"traffic_signal", "signal"}}}, + }; + + const gurka::ways ways = { + {"ab", {{"highway", "residential"}}}, + {"bc", {{"highway", "motorway"}}}, + {"cd", {{"highway", "residential"}, {"maxspeed", "60"}}}, + }; + + constexpr double gridsize = 100; + landmark_map.nodes = gurka::detail::map_to_coordinates(ascii_map, gridsize, {-.01, 0}); + + detail::build_pbf(landmark_map.nodes, ways, nodes, {}, pbf_filename, 0, false); +} + +void BuildPBFAddLandmarksToTiles() { + const std::string ascii_map = R"( + A B E + a------b-----c----d K + | C | D + F | | + | | + | | + | G | + | | + | | + | | + | | + | | H + e------------f + I J + )"; + + const gurka::nodes nodes = { + {"A", {{"name", "lv_mo_li"}, {"amenity", "bar"}}}, + {"B", {{"name", "hai_di_lao"}, {"amenity", "restaurant"}}}, + {"C", {{"name", "McDonalds"}, {"amenity", "fast_food"}}}, + {"D", {{"name", "wan_da"}, {"amenity", "cinema"}}}, + {"E", {{"name", "sheng_ren_min_yi_yuan"}, {"amenity", "hospital"}}}, + {"F", {{"name", "gong_shang_yin_hang"}, {"amenity", "bank"}}}, + {"G", {{"name", "ju_yuan"}, {"amenity", "theatre"}}}, + {"H", {{"name", "pizza_hut"}, {"amenity", "restaurant"}}}, + {"I", {{"name", "shell"}, {"amenity", "fuel"}}}, + {"J", {{"name", "starbucks"}, {"amenity", "cafe"}}}, + {"K", {{"name", "you_zheng"}, {"amenity", "post_office"}}}, + // non-landmark nodes + {"a", {{"junction", "yes"}, {"name", "you_yi_lu_kou"}}}, + {"b", {{"traffic_signal", "signal"}}}, + {"c", {{"junction", "yes"}, {"name", "gao_xin_lu_kou"}}}, + {"d", {{"barrier", "gate"}}}, + {"e", {{"name", "hua_yuan"}, {"place", "city"}}}, + {"f", {{"name", "gong_ce"}, {"amenity", "toilets"}}}, + }; + + const gurka::ways ways = { + {"ae", // length 55, associated with ABFI + {{"highway", "primary"}, {"name", "G999"}, {"driving_side", "right"}, {"maxspeed", "120"}}}, + {"ab", // length 35, associated with ABCFG + {{"highway", "secondary"}, {"name", "S1"}, {"driving_side", "right"}}}, + {"bc", // length 30, associated with CDG + {{"highway", "secondary"}, {"name", "S2"}, {"lanes", "2"}, {"driving_side", "right"}}}, + {"cd", {{"highway", "motorway"}, {"maxspeed", "100"}}}, // length 25, associated with CDEK + {"cf", {{"highway", "residential"}, {"maxspeed", "30"}}}, // length 55, associated with CDH + {"ef", {{"highway", "residential"}, {"maxspeed", "30"}}}, // length 65, associated with I + }; + + constexpr double gridsize = 5; + landmark_map_tile_test.nodes = gurka::detail::map_to_coordinates(ascii_map, gridsize, {0, 0}); + + detail::build_pbf(landmark_map_tile_test.nodes, ways, nodes, {}, pbf_filename_tile_test, 0, false); +} + +void CheckLandmarksInTiles(GraphReader& reader, const GraphId& graphid) { + LOG_INFO("Checking tiles of level " + std::to_string(graphid.level()) + "..."); + + const std::map, std::vector> expected_landmarks_tiles = { + {{0, 55}, + std::vector{"gong_shang_yin_hang", "hai_di_lao", "lv_mo_li", "shell"}}, // ae + {{0, 25}, + std::vector{"McDonalds", "sheng_ren_min_yi_yuan", "wan_da", "you_zheng"}}, // cd + {{1, 35}, + std::vector{"McDonalds", "gong_shang_yin_hang", "hai_di_lao", "ju_yuan", + "lv_mo_li"}}, // ab + {{1, 30}, std::vector{"McDonalds", "ju_yuan", "wan_da"}}, // bc + {{2, 55}, std::vector{"McDonalds", "pizza_hut", "wan_da"}}, // cf + {{2, 65}, std::vector{"shell"}}, // ef + }; + + auto tile = reader.GetGraphTile(graphid); + for (const auto& e : tile->GetDirectedEdges()) { + if (e.is_shortcut()) { + continue; + } + auto ei = tile->edgeinfo(&e); + auto tagged_values = ei.GetTags(); + + std::vector landmark_names{}; + for (const auto& value : tagged_values) { + if (value.first != baldr::TaggedValue::kLandmark) + continue; + landmark_names.push_back(Landmark(value.second).name); + } + + // use graph level and edge length as map key + const auto expected_result = expected_landmarks_tiles.find({graphid.level(), e.length()}); + if (expected_result == expected_landmarks_tiles.end()) { + throw std::runtime_error("Failed to find the edge in the expected landmarks, level = " + + std::to_string(graphid.level()) + + ", edge length = " + std::to_string(e.length())); + } + + std::sort(landmark_names.begin(), landmark_names.end()); + EXPECT_EQ(expected_result->second, landmark_names); + } +} + +void DisplayLandmarksInTiles(GraphReader& reader, const GraphId& graphid) { + LOG_INFO("Checking tiles of level " + std::to_string(graphid.level()) + "..."); + + auto tile = reader.GetGraphTile(graphid); + for (const auto& e : tile->GetDirectedEdges()) { + auto ei = tile->edgeinfo(&e); + auto tagged_values = ei.GetTags(); + + LOG_INFO("edge endnode: " + std::to_string(e.endnode().id()) + + ", length: " + std::to_string(e.length())); + + for (const auto& value : tagged_values) { + if (value.first != baldr::TaggedValue::kLandmark) + continue; + DisplayLandmark(Landmark(value.second)); + } + } +} + +// struct to represent a landmark in maneuvers in test +struct LandmarkInManeuver { + std::string _name; + uint8_t _type; + double _distance; + bool _right; + + LandmarkInManeuver(const std::string& name, uint8_t type, double distance, bool right) + : _name(name), _type(type), _distance(distance), _right(right) { + } + + bool operator<(const LandmarkInManeuver& other) const { + if (_name != other._name) { + return _name < other._name; + } else { + return _distance < other._distance; + } + } + + bool operator==(const LandmarkInManeuver& other) const { + if (_name == other._name && _type == other._type && _distance == other._distance && + _right == other._right) { + return true; + } + return false; + } +}; + +bool operator==(const Landmark& a, const Landmark& b) { + return a.id == b.id && a.name == b.name && a.type == b.type && + PointLL(a.lng, a.lat).ApproximatelyEqual(PointLL(b.lng, b.lat)); +} +} // namespace + +TEST(LandmarkTest, TestBuildDatabase) { + // insert test data + { + LandmarkDatabase db_ini(db_path, false); + + ASSERT_NO_THROW( + db_ini.insert_landmark("Statue of Liberty", LandmarkType::theatre, -74.044548, 40.689253)); + ASSERT_NO_THROW(db_ini.insert_landmark("Eiffel Tower", LandmarkType::cafe, 2.294481, 48.858370)); + ASSERT_NO_THROW(db_ini.insert_landmark("A", LandmarkType::bank, 40., 40.)); + ASSERT_NO_THROW(db_ini.insert_landmark("B", LandmarkType::fire_station, 30., 30.)); + } + + // test + LandmarkDatabase db(db_path, true); + + std::vector landmarks{}; + EXPECT_NO_THROW({ landmarks = db.get_landmarks_by_bbox(30, 30, 40, 40); }); + EXPECT_EQ(landmarks.size(), 2); // A and B + + LOG_INFO("Get " + std::to_string(landmarks.size()) + " rows"); + + landmarks.clear(); + EXPECT_NO_THROW({ landmarks = db.get_landmarks_by_bbox(0, 0, 50, 50); }); + EXPECT_EQ(landmarks.size(), 3); // A, B, Eiffel Tower +} + +TEST(LandmarkTest, TestParseLandmarks) { + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + // parse and store + BuildPBF(); + boost::property_tree::ptree& pt = landmark_map.config; + pt.put("mjolnir.landmarks", db_path); + + std::vector input_files = {workdir + "/map.pbf"}; + + EXPECT_TRUE(BuildLandmarkFromPBF(pt.get_child("mjolnir"), input_files)); + + // check + std::vector landmarks{}; + LandmarkDatabase db(db_path, true); + + EXPECT_NO_THROW({ landmarks = db.get_landmarks_by_bbox(-.1, -5, 10, 0); }); + EXPECT_EQ(landmarks.size(), 3); // A, B, D + + LOG_INFO("Get " + std::to_string(landmarks.size()) + " rowsmjolnir"); + + EXPECT_TRUE(landmarks[0].type == LandmarkType::bar); // A + EXPECT_TRUE(landmarks[0].name == "A"); + EXPECT_TRUE(landmarks[1].type == LandmarkType::restaurant); // B + EXPECT_TRUE(landmarks[1].name == "hai di lao"); + EXPECT_TRUE(landmarks[2].type == LandmarkType::cinema); // D + EXPECT_TRUE(landmarks[2].name == "wan da"); + + // check getting multiple landmarks by ids + EXPECT_NO_THROW({ landmarks = db.get_landmarks_by_ids({1, 2, 3, 4}); }); + EXPECT_EQ(landmarks.size(), 3); + + EXPECT_NO_THROW({ landmarks = db.get_landmarks_by_ids({0, 937, 45, 15, 200, 353, 2386}); }); + EXPECT_EQ(landmarks.size(), 0); + + EXPECT_NO_THROW({ landmarks = db.get_landmarks_by_ids({2, 3, 5, 7, 11, 13, 17, 19, 23, 29}); }); + EXPECT_EQ(landmarks.size(), 2); + + EXPECT_NO_THROW({ landmarks = db.get_landmarks_by_ids({4}); }); + EXPECT_EQ(landmarks.size(), 0); + + EXPECT_NO_THROW({ landmarks = db.get_landmarks_by_ids({3, 2, 1}); }); + EXPECT_EQ(landmarks.size(), 3); + + // test round trip encoding + for (auto& expected : landmarks) { + expected.id = 0; + Landmark actual(expected.to_str()); + EXPECT_TRUE(expected == actual); + } +} + +TEST(LandmarkTest, TestTileStoreLandmarks) { + BuildPBF(); + landmark_map.config = + test::make_config(workdir, {{"mjolnir.landmarks_db", db_path}}, + {{"additional_data", "mjolnir.traffic_extract", "mjolnir.tile_extract"}}); + + // build regular graph tiles from the pbf that we have already made, there wont be landmarks in them + mjolnir::build_tile_set(landmark_map.config, {pbf_filename}, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + // load one of the graphtiles + GraphId tile_id("2/519119/0"); + GraphId edge_id = tile_id; + GraphTileBuilder tb(workdir, tile_id, true); + + auto invalid_landmark = static_cast(LandmarkType::casino) + 1; + // add flexible landmarks for the edges + for (const auto& e : tb.directededges()) { + std::vector shape = tb.edgeinfo(&e).shape(); + auto point = shape[shape.size() / 2]; + auto ltype = static_cast((edge_id.id() + 1) % invalid_landmark); + Landmark landmark(0, std::to_string(edge_id), ltype, point.first, point.second); + tb.AddLandmark(edge_id, landmark); + ++edge_id; + } + + tb.StoreTileData(); + + // instantiate a graphreader using the config + GraphReader gr(landmark_map.config.get_child("mjolnir")); + auto tile = gr.GetGraphTile(tile_id); + + auto check_landmark = [&](const auto& landmark, const auto& point) { + auto ltype = static_cast((edge_id.id() + 1) % invalid_landmark); + + const double rounding_error = 5e-7; + // we dont store this in the tile + EXPECT_EQ(landmark.id, 0); + // opposing edges will have a copy of the edge we associated it to so we need to check it + bool skip = false; + if (landmark.name != std::to_string(edge_id)) { + auto opp = gr.GetOpposingEdgeId(edge_id); + ASSERT_EQ(landmark.name, std::to_string(opp)) << "Unexpected landmark name or edge/opp_edge"; + skip = true; + } + // we dont do deep check on opposing edge records + if (!skip) { + EXPECT_EQ(landmark.type, ltype); + EXPECT_NEAR(landmark.lng, point.first, rounding_error); + EXPECT_NEAR(landmark.lat, point.second, rounding_error); + } + }; + + // we support up to 6 decimal precision for landmark lng/lat, so max rounding error is 5e-7 + + edge_id.set_id(0); + for (const auto& e : tile->GetDirectedEdges()) { + auto ei = tile->edgeinfo(&e); + + // test EdgeInfo:GetTags + auto tagged_values = ei.GetTags(); + for (const auto& value : tagged_values) { + if (value.first != baldr::TaggedValue::kLandmark) + continue; + Landmark landmark(value.second); + + // check data correctness + std::vector shape = ei.shape(); + auto point = shape[shape.size() / 2]; + check_landmark(landmark, point); + } + + // test EdgeInfo:GetTaggedValues + auto values = ei.GetTaggedValues(); + for (const std::string& v : values) { + if (static_cast(v[0]) != baldr::TaggedValue::kLandmark) { + continue; + } + Landmark landmark(v.substr(1)); + + // check data correctness + std::vector shape = ei.shape(); + auto point = shape[shape.size() / 2]; + check_landmark(landmark, point); + } + + ++edge_id; + } +} + +TEST(LandmarkTest, TestAddLandmarksToTiles) { + if (!std::filesystem::exists(workdir_tiles)) { + bool created = std::filesystem::create_directories(workdir_tiles); + EXPECT_TRUE(created); + } + + BuildPBFAddLandmarksToTiles(); + + landmark_map_tile_test.config = + test::make_config(workdir_tiles, {{"mjolnir.landmarks_db", db_path_tile_test}}, + {{"additional_data", "mjolnir.traffic_extract", "mjolnir.tile_extract"}}); + + // build regular graph tiles from the pbf that we have already made, there wont be landmarks in them + mjolnir::build_tile_set(landmark_map_tile_test.config, {pbf_filename_tile_test}, + mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, false); + + // build landmark database and parse landmarks + EXPECT_TRUE(BuildLandmarkFromPBF(landmark_map_tile_test.config.get_child("mjolnir"), + {pbf_filename_tile_test})); + + // add landmarks from db to tiles + AddLandmarks(landmark_map_tile_test.config); + + // check data + GraphReader gr(landmark_map_tile_test.config.get_child("mjolnir")); + + CheckLandmarksInTiles(gr, GraphId("0/002025/0")); + CheckLandmarksInTiles(gr, GraphId("1/032220/0")); + CheckLandmarksInTiles(gr, GraphId("2/517680/0")); +} + +// TODO: This is an example test to show the underlying bugs or problems in the current code. +// Right now the problem is we cannot add landmarks to a graph tile twice. +// The test will return "[ERROR] Failed to build GraphTile. Error: GraphId level exceeds tile +// hierarchy max level", and "Could not compute FileSuffix for GraphId with invalid tile +// id:0/245760/0". We need to fix it in the future. +TEST(LandmarkTest, DISABLED_ErrorTest) { + if (!std::filesystem::exists(workdir_tiles)) { + bool created = std::filesystem::create_directories(workdir_tiles); + EXPECT_TRUE(created); + } + + BuildPBFAddLandmarksToTiles(); + + landmark_map_tile_test.config = + test::make_config(workdir_tiles, {{"mjolnir.landmarks_db", db_path_tile_test}}, + {{"additional_data", "mjolnir.traffic_extract", "mjolnir.tile_extract"}}); + + // build regular graph tiles from the pbf that we have already made, there wont be landmarks in them + mjolnir::build_tile_set(landmark_map_tile_test.config, {pbf_filename_tile_test}, + mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, false); + + // build landmark database and parse landmarks + EXPECT_TRUE(BuildLandmarkFromPBF(landmark_map_tile_test.config.get_child("mjolnir"), + {pbf_filename_tile_test})); + + // add landmarks from db to tiles + AddLandmarks(landmark_map_tile_test.config); + // add again, but this will result in errors + AddLandmarks(landmark_map_tile_test.config); + + // check data (cannot reach here yet) + GraphReader gr(landmark_map_tile_test.config.get_child("mjolnir")); + + DisplayLandmarksInTiles(gr, GraphId("0/002025/0")); + DisplayLandmarksInTiles(gr, GraphId("1/032220/0")); + DisplayLandmarksInTiles(gr, GraphId("2/517680/0")); +} + +TEST(LandmarkTest, TestLandmarksInManeuvers) { + const std::string ascii_map = R"( + A B C D + a-------b-------c------d + E | F |G + | | + e | I J + H f-------g + K + )"; + + const gurka::nodes nodes = { + // landmarks + {"A", {{"name", "lv_mo_li"}, {"amenity", "bar"}}}, + {"B", {{"name", "hai_di_lao"}, {"amenity", "restaurant"}}}, + {"C", {{"name", "McDonalds"}, {"amenity", "fast_food"}}}, + {"D", {{"name", "wan_da"}, {"amenity", "cinema"}}}, + {"E", {{"name", "sheng_ren_min_yi_yuan"}, {"amenity", "hospital"}}}, + {"F", {{"name", "gong_shang_yin_hang"}, {"amenity", "bank"}}}, + {"G", {{"name", "ju_yuan"}, {"amenity", "theatre"}}}, + {"H", {{"name", "pizza_hut"}, {"amenity", "restaurant"}}}, + {"I", {{"name", "shell"}, {"amenity", "fuel"}}}, + {"J", {{"name", "starbucks"}, {"amenity", "cafe"}}}, + {"K", {{"name", "you_zheng"}, {"amenity", "post_office"}}}, + // non-landmark nodes + {"a", {{"junction", "yes"}, {"name", "you_yi_lu_kou"}}}, + {"b", {{"traffic_signal", "signal"}}}, + {"c", {{"junction", "yes"}, {"name", "gao_xin_lu_kou"}}}, + {"d", {{"barrier", "gate"}}}, + {"e", {{"name", "hua_yuan"}, {"place", "city"}}}, + {"f", {{"name", "gong_ce"}, {"amenity", "toilets"}}}, + {"g", {{"barrier", "gate"}, {"access", "yes"}}}, + }; + + const gurka::ways ways = { + {"ab", {{"highway", "secondary"}, {"name", "S1"}, {"maxspeed", "80"}}}, + {"bc", {{"highway", "secondary"}, {"name", "S2"}, {"driving_side", "right"}}}, + {"cd", {{"highway", "secondary"}, {"lanes", "2"}, {"driving_side", "right"}}}, + {"be", {{"highway", "secondary"}, {"maxspeed", "60"}}}, + {"cf", {{"highway", "secondary"}, {"maxspeed", "50"}}}, + {"fg", {{"highway", "secondary"}, {"maxspeed", "50"}}}, + }; + + const std::string workdir = VALHALLA_BUILD_DIR "test/data/landmarks/maneuvers"; + const std::string db_path = workdir + "/landmarks.sqlite"; + const std::string pbf = workdir + "/map.pbf"; + + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + valhalla::gurka::map map{}; + map.nodes = gurka::detail::map_to_coordinates(ascii_map, 10, {0, 0}); + detail::build_pbf(map.nodes, ways, nodes, {}, pbf, 0, false); + + map.config = + test::make_config(workdir, {{"mjolnir.landmarks_db", db_path}}, + {{"additional_data", "mjolnir.traffic_extract", "mjolnir.tile_extract"}}); + + // build regular graph tiles from the pbf, and add landmarks to it + mjolnir::build_tile_set(map.config, {pbf}, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + // build landmark database and import landmarks to it + EXPECT_TRUE(BuildLandmarkFromPBF(map.config.get_child("mjolnir"), {pbf})); + // add landmarks to graphtile from the landmark database + AddLandmarks(map.config); + + // get routing result from point a to g + auto result = gurka::do_action(valhalla::Options::route, map, {"a", "g"}, "auto"); + + ASSERT_EQ(result.trip().routes_size(), 1); + ASSERT_EQ(result.trip().routes(0).legs_size(), 1); + auto leg = result.trip().routes(0).legs(0); + + const std::map> expected_landmarks_tripleg = { + {"S1", std::vector{"hai_di_lao", "lv_mo_li", "sheng_ren_min_yi_yuan"}}, + {"S2", std::vector{"McDonalds", "gong_shang_yin_hang", "ju_yuan", + "sheng_ren_min_yi_yuan"}}, + {"cf", std::vector{"McDonalds", "ju_yuan", "you_zheng"}}, + {"fg", std::vector{"shell", "starbucks", "you_zheng"}}, + }; + + // Check tripLeg + for (const auto& node : leg.node()) { + // skip the point in trip leg, check edges + if (node.edge().name_size() == 0) { + continue; + } + // get edge name + ASSERT_EQ(node.edge().name_size(), 1); + const std::string edge_name = node.edge().name(0).value(); + + // get landmarks in the edge + std::vector landmark_names{}; + for (const auto& tag : node.edge().tagged_value()) { + if (tag.type() == TaggedValue_Type_kLandmark) { + Landmark landmark(tag.value()); + landmark_names.push_back(landmark.name); + } + } + // compare landmarks with expected results + auto expected_result = expected_landmarks_tripleg.find(edge_name); + if (expected_result == expected_landmarks_tripleg.end()) { + throw std::runtime_error("Failed to find the edge in the expected landmarks, edge name = " + + edge_name); + } + + std::sort(landmark_names.begin(), landmark_names.end()); + EXPECT_EQ(expected_result->second, landmark_names); + } + + // Check maneuver + ASSERT_EQ(result.directions().routes_size(), 1); + ASSERT_EQ(result.directions().routes(0).legs_size(), 1); + auto directions_leg = result.directions().routes(0).legs(0); + + // expected landmark results in the maneuvers + std::vector cf_maneuver{LandmarkInManeuver("hai_di_lao", 6, 140, 0), + LandmarkInManeuver("lv_mo_li", 12, 160, 0), + LandmarkInManeuver("sheng_ren_min_yi_yuan", 13, 100, 1), + LandmarkInManeuver("gong_shang_yin_hang", 9, 30, 1), + LandmarkInManeuver("ju_yuan", 16, 0, 1), + LandmarkInManeuver("sheng_ren_min_yi_yuan", 13, 80, 1), + LandmarkInManeuver("McDonalds", 7, 20, 0)}; + std::vector fg_maneuver{LandmarkInManeuver("ju_yuan", 16, 30, 0), + LandmarkInManeuver("you_zheng", 2, 0, 0), + LandmarkInManeuver("McDonalds", 7, 40, 1)}; + std::vector end_point_maneuver{LandmarkInManeuver("shell", 1, 30, 0), + LandmarkInManeuver("you_zheng", 2, 60, 1), + LandmarkInManeuver("starbucks", 8, 0, 0)}; + std::sort(cf_maneuver.begin(), cf_maneuver.end()); + std::sort(fg_maneuver.begin(), fg_maneuver.end()); + std::sort(end_point_maneuver.begin(), end_point_maneuver.end()); + + std::map> expected_landmarks_maneuver = { + {"S1", std::vector{}}, + {"cf", cf_maneuver}, + {"fg", fg_maneuver}, + {"end_point", end_point_maneuver}, + }; + + // check landmarks with expectations one by one + for (const auto& man : directions_leg.maneuver()) { + std::string street_name = man.street_name_size() == 1 ? man.street_name(0).value() : "end_point"; + + std::vector result_landmarks{}; + for (const auto& l : man.landmarks()) { + result_landmarks.emplace_back(LandmarkInManeuver(l.name(), static_cast(l.type()), + std::round(l.distance()), l.right())); + } + std::sort(result_landmarks.begin(), result_landmarks.end()); + + auto expected = expected_landmarks_maneuver.find(street_name); + if (expected == expected_landmarks_maneuver.end()) { + throw std::runtime_error( + "Checking landmarks in maneuver failed: cannot find the maneuver in the expected result!"); + } + ASSERT_EQ(result_landmarks.size(), expected->second.size()); + for (size_t i = 0; i < result_landmarks.size(); ++i) { + EXPECT_EQ(result_landmarks[i], expected->second[i]); + } + } +} diff --git a/test/gurka/test_languages.cc b/test/gurka/test_languages.cc new file mode 100644 index 0000000000..09b937e343 --- /dev/null +++ b/test/gurka/test_languages.cc @@ -0,0 +1,6882 @@ +#include "baldr/graphreader.h" +#include "mjolnir/util.h" + +#include "gurka.h" +#include "test/test.h" + +#if !defined(VALHALLA_SOURCE_DIR) +#define VALHALLA_SOURCE_DIR +#endif + +using namespace valhalla; +using namespace valhalla::baldr; +using namespace valhalla::gurka; + +valhalla::gurka::map the_map = {}; + +class RouteWithStreetnameAndSign_en_UnitedStates : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + J + | + | + | + I + /|\ + / | \ + / | \ + L----K-------------H----G + A----B-------------E----F + \ | / + \ | / + \|/ + C + | + | + | + D + O------PM------Q + | + | + | + N + + )"; + + const gurka::ways ways = { + {"ABEF", {{"highway", "motorway"}, {"name", ""}, {"ref", "I 70"}, {"oneway", "yes"}}}, + {"GHKL", {{"highway", "motorway"}, {"name", ""}, {"ref", "I 70"}, {"oneway", "yes"}}}, + {"JICDMN", + {{"highway", "primary"}, + {"osm_id", "100"}, + {"name", "6th Avenue"}, + {"name:ru", "6-я авеню"}, + {"ref", "SR 37"}}}, + {"BC", + {{"highway", "motorway_link"}, + {"osm_id", "101"}, + {"name", ""}, + {"oneway", "yes"}, + {"junction:ref", "126B"}, + {"destination", "York;Lancaster"}, + {"destination:lang:ru", "Йорк;Ланкастер"}, + {"destination:street", "6th Avenue"}, + {"destination:street:lang:ru", "6-я авеню"}, + {"destination:ref", "SR 37"}}}, + {"CE", + {{"highway", "motorway_link"}, + {"name", ""}, + {"oneway", "yes"}, + {"destination:ref", "I 70 East"}, + {"destination:ref:lang:ru", "Я 70 Восток"}}}, + {"HI", + {{"highway", "motorway_link"}, + {"osm_id", "102"}, + {"name", ""}, + {"oneway", "yes"}, + {"junction:ref", "126B"}, + {"destination:street:to", "Main Street"}, + {"destination:street:to:lang:ru", "Главная улица"}, + {"destination:ref:to", "I 80"}, + {"destination:ref:to:lang:ru", "Я 80"}}}, + {"IK", + {{"highway", "motorway_link"}, + {"name", ""}, + {"oneway", "yes"}, + {"destination:ref", "I 70 West"}, + {"destination:ref:lang:ru", "Я 70 Запад"}}}, + {"OPMQ", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", ""}, + {"name:en", "West 8th Street"}, + {"name:ru", "Западная 8-я стрит"}}}, + {"DP", + {{"highway", "secondary_link"}, + {"osm_id", "104"}, + {"name", ""}, + {"oneway", "yes"}, + {"destination", "York"}, + {"destination:street", "West 8th Street"}, + {"destination:street:lang:ru", "Западная 8-я стрит"}}}, + }; + + const gurka::nodes nodes = {{"M", {{"highway", "traffic_signals"}, {"name", "M Junction"}}}}; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-82.68811, 40.22535}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, nodes, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_UnitedStates, CheckStreetNamesAndSigns1) { + + const std::string workdir = "test/data/gurka_language_with_streetname_and_sign_en_UnitedStates"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"A", "D"}, "auto"); + gurka::assert::raw::expect_path(result, {"I 70", "", "6th Avenue/SR 37"}); + + // Verify starting on I 70 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 1); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "I 70"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets_size(), + 2); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(0) + .text(), + "SR 37"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(1) + .text(), + "6th Avenue"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations_size(), + 2); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(0) + .text(), + "York"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(1) + .text(), + "Lancaster"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, the_map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId JICDMN_edge_id; + const DirectedEdge* JICDMN_edge = nullptr; + GraphId NMDCIJ_edge_id; + const DirectedEdge* NMDCIJ_edge = nullptr; + std::tie(JICDMN_edge_id, JICDMN_edge, NMDCIJ_edge_id, NMDCIJ_edge) = + findEdge(graph_reader, the_map.nodes, "", "N", baldr::GraphId{}, 100); + EXPECT_NE(JICDMN_edge, nullptr); + EXPECT_NE(NMDCIJ_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(BC_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(BC_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 5); + ASSERT_EQ(linguistics.size(), 5); + + ASSERT_EQ(edge_signs.at(0).text(), "126B"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(1).text(), "SR 37"); + iter = linguistics.find(1); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(2).text(), "6th Avenue"); + iter = linguistics.find(2); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(3).text(), "York"); + iter = linguistics.find(3); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(4).text(), "Lancaster"); + iter = linguistics.find(4); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ++maneuver_index; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 2); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "6th Avenue"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "SR 37"); + + node_id = JICDMN_edge->endnode(); + tile = graph_reader.GetGraphTile(node_id); + edgeinfo = tile->edgeinfo(JICDMN_edge); + types.clear(); + names_and_types = edgeinfo.GetNamesAndTypes(true); + std::unordered_map> name_linguistics = + edgeinfo.GetLinguisticMap(); + ; + + ASSERT_EQ(names_and_types.size(), 2); + + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "6th Avenue"); + std::unordered_map>::const_iterator lang_iter = + name_linguistics.find(0); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "SR 37"); + lang_iter = name_linguistics.find(1); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_UnitedStates, CheckStreetNamesAndSigns2) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"G", "J"}, "auto"); + gurka::assert::raw::expect_path(result, {"I 70", "", "6th Avenue/SR 37"}); + + // Verify starting on I 70 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 1); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "I 70"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations_size(), + 2); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(0) + .text(), + "I 80"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(1) + .text(), + "Main Street"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId HI_edge_id; + const DirectedEdge* HI_edge = nullptr; + GraphId IH_edge_id; + const DirectedEdge* IH_edge = nullptr; + std::tie(HI_edge_id, HI_edge, IH_edge_id, IH_edge) = + findEdge(graph_reader, the_map.nodes, "", "I", baldr::GraphId{}, 102); + EXPECT_NE(HI_edge, nullptr); + EXPECT_NE(IH_edge, nullptr); + + GraphId node_id = HI_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(HI_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(HI_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 3); + ASSERT_EQ(linguistics.size(), 3); + + ASSERT_EQ(edge_signs.at(0).text(), "126B"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(1).text(), "I 80"); + iter = linguistics.find(1); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(2).text(), "Main Street"); + iter = linguistics.find(2); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_UnitedStates, CheckGuideSigns) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"J", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"6th Avenue/SR 37", "6th Avenue/SR 37", "6th Avenue/SR 37", + "", "West 8th Street"}); + + // Verify starting on 6th Avenue/SR 37 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 2); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "6th Avenue"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "SR 37"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets_size(), + 1); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets(0) + .text(), + "West 8th Street"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_toward_locations_size(), + 1); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_toward_locations(0) + .text(), + "York"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId DP_edge_id; + const DirectedEdge* DP_edge = nullptr; + GraphId PD_edge_id; + const DirectedEdge* PD_edge = nullptr; + std::tie(DP_edge_id, DP_edge, PD_edge_id, PD_edge) = + findEdge(graph_reader, the_map.nodes, "", "P", baldr::GraphId{}, 104); + EXPECT_NE(DP_edge, nullptr); + EXPECT_NE(PD_edge, nullptr); + + GraphId node_id = PD_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(PD_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(DP_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "West 8th Street"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_NE(iter, linguistics.end()); + + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(1).text(), "York"); + iter = linguistics.find(1); + ASSERT_NE(iter, linguistics.end()); + + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_UnitedStates, CheckNonJunctionName) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"J", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, {"6th Avenue/SR 37", "6th Avenue/SR 37", "6th Avenue/SR 37", + "6th Avenue/SR 37", "West 8th Street"}); + + // Verify starting on 6th Avenue/SR 37 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 2); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "6th Avenue"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "SR 37"); + + ++maneuver_index; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 1); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "West 8th Street"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 1); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "West 8th Street"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + // No junction should exist here. Named junctions are not allowed in US + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .junction_names_size(), + 0); +} + +class RouteWithStreetnameAndSign_fr_nl_BrusselsBelgium : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + J + | + | + | + I + /|\ + / | \ + / | \ + L----K-------------H----G + A----B-------------E----F + \ | / + \ | / + \|/ + C + | + | + | + D + O------PM------Q + | + | + | + N + + )"; + + const gurka::ways ways = { + {"ABEF", {{"highway", "motorway"}, {"name", ""}, {"ref", "E40"}, {"oneway", "yes"}}}, + {"GHKL", {{"highway", "motorway"}, {"name", ""}, {"ref", "E40"}, {"oneway", "yes"}}}, + {"JICDMN", + {{"highway", "primary"}, + {"osm_id", "100"}, + {"name", "Rue Bodenbroek - Bodenbroekstraat"}, + {"name:fr", "Rue Bodenbroek"}, + {"name:nl", "Bodenbroekstraat"}, + {"ref", "N6"}}}, + {"BC", + {{"highway", "motorway_link"}, + {"osm_id", "101"}, + {"name", ""}, + {"oneway", "yes"}, + {"junction:ref", "12"}, + {"destination", "Brussel;Namen"}, + {"destination:street", "Bodenbroekstraat"}, + {"destination:street:lang:fr", "Rue Bodenbroek"}, + {"destination:street:lang:nl", "Bodenbroekstraat"}, + {"destination:ref", "N6"}}}, + {"CE", + {{"highway", "motorway_link"}, {"name", ""}, {"oneway", "yes"}, {"destination:ref", "E40"}}}, + {"HI", + {{"highway", "motorway_link"}, + {"osm_id", "102"}, + {"name", ""}, + {"oneway", "yes"}, + {"junction:ref", "12"}, + {"destination:street:to", "Koningsstraat"}, + {"destination:street:to:lang:fr", "Rue Royale"}, + {"destination:street:to:lang:nl", "Koningsstraat"}, + {"destination:ref:to", "E19"}}}, + {"IK", + {{"highway", "motorway_link"}, {"name", ""}, {"oneway", "yes"}, {"destination:ref", "E40"}}}, + {"OPMQ", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", "Rue Lebeau - Lebeaustraat"}, + {"name:fr", "Rue Lebeau"}, + {"name:nl", "Lebeaustraat"}}}, + {"DP", + {{"highway", "secondary_link"}, + {"osm_id", "104"}, + {"name", ""}, + {"oneway", "yes"}, + {"destination", "Brussel"}, + {"destination:street", "Lebeaustraat"}, + {"destination:street:lang:fr", "Rue Lebeau"}, + {"destination:street:lang:nl", "Lebeaustraat"}}}, + }; + + const gurka::nodes nodes = {{"M", {{"highway", "traffic_signals"}, {"name", "Zaventem"}}}}; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {4.3516970, 50.8465573}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, nodes, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_fr_nl_BrusselsBelgium, CheckStreetNamesAndSigns1) { + + const std::string workdir = + "test/data/gurka_language_with_streetname_and_sign_fr_nl_BrusselsBelgium"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"A", "D"}, "auto"); + gurka::assert::raw::expect_path(result, {"E40", "", "Rue Bodenbroek/Bodenbroekstraat/N6"}); + + // Verify starting on E40 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 1); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "E40"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets_size(), + 3); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(0) + .text(), + "N6"); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(1) + .text(), + "Bodenbroekstraat"); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(2) + .text(), + "Rue Bodenbroek"); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations_size(), + 2); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(0) + .text(), + "Brussel"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(1) + .text(), + "Namen"); + + ++maneuver_index; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Rue Bodenbroek"); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Bodenbroekstraat"); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "N6"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, the_map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId JICDMN_edge_id; + const DirectedEdge* JICDMN_edge = nullptr; + GraphId NMDCIJ_edge_id; + const DirectedEdge* NMDCIJ_edge = nullptr; + std::tie(JICDMN_edge_id, JICDMN_edge, NMDCIJ_edge_id, NMDCIJ_edge) = + findEdge(graph_reader, the_map.nodes, "", "N", baldr::GraphId{}, 100); + EXPECT_NE(JICDMN_edge, nullptr); + EXPECT_NE(NMDCIJ_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(BC_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(BC_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 6); + ASSERT_EQ(linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "12"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(1).text(), "N6"); + iter = linguistics.find(1); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(2).text(), "Bodenbroekstraat"); + iter = linguistics.find(2); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "nl"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(3).text(), "Rue Bodenbroek"); + iter = linguistics.find(3); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(4).text(), "Brussel"); + iter = linguistics.find(4); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(5).text(), "Namen"); + iter = linguistics.find(5); + ASSERT_EQ(iter, linguistics.end()); + + node_id = JICDMN_edge->endnode(); + tile = graph_reader.GetGraphTile(node_id); + edgeinfo = tile->edgeinfo(JICDMN_edge); + types.clear(); + names_and_types = edgeinfo.GetNamesAndTypes(true); + std::unordered_map> name_linguistics = + edgeinfo.GetLinguisticMap(); + ; + + ASSERT_EQ(names_and_types.size(), 3); + + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Rue Bodenbroek"); + std::unordered_map>::const_iterator lang_iter = + name_linguistics.find(0); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Bodenbroekstraat"); + lang_iter = name_linguistics.find(1); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "nl"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(2)), "N6"); + lang_iter = name_linguistics.find(2); + ASSERT_EQ(lang_iter, name_linguistics.end()); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_fr_nl_BrusselsBelgium, CheckStreetNamesAndSigns2) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"G", "J"}, "auto"); + gurka::assert::raw::expect_path(result, {"E40", "", "Rue Bodenbroek/Bodenbroekstraat/N6"}); + + // Verify starting on E40 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 1); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "E40"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations_size(), + 3); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(0) + .text(), + "E19"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(1) + .text(), + "Koningsstraat"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(2) + .text(), + "Rue Royale"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId HI_edge_id; + const DirectedEdge* HI_edge = nullptr; + GraphId IH_edge_id; + const DirectedEdge* IH_edge = nullptr; + std::tie(HI_edge_id, HI_edge, IH_edge_id, IH_edge) = + findEdge(graph_reader, the_map.nodes, "", "I", baldr::GraphId{}, 102); + EXPECT_NE(HI_edge, nullptr); + EXPECT_NE(IH_edge, nullptr); + + GraphId node_id = HI_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(HI_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(HI_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 4); + ASSERT_EQ(linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "12"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(1).text(), "E19"); + iter = linguistics.find(1); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(2).text(), "Koningsstraat"); + iter = linguistics.find(2); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "nl"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(3).text(), "Rue Royale"); + iter = linguistics.find(3); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_fr_nl_BrusselsBelgium, CheckGuideSigns) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"J", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"Rue Bodenbroek/Bodenbroekstraat/N6", + "Rue Bodenbroek/Bodenbroekstraat/N6", + "Rue Bodenbroek/Bodenbroekstraat/N6", "", + "Rue Lebeau/Lebeaustraat"}); + + // Verify starting on Rue Bodenbroek/Bodenbroekstraat/N6 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Rue Bodenbroek"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Bodenbroekstraat"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "N6"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets_size(), + 2); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets(0) + .text(), + "Lebeaustraat"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets(1) + .text(), + "Rue Lebeau"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_toward_locations_size(), + 1); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_toward_locations(0) + .text(), + "Brussel"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId DP_edge_id; + const DirectedEdge* DP_edge = nullptr; + GraphId PD_edge_id; + const DirectedEdge* PD_edge = nullptr; + std::tie(DP_edge_id, DP_edge, PD_edge_id, PD_edge) = + findEdge(graph_reader, the_map.nodes, "", "P", baldr::GraphId{}, 104); + EXPECT_NE(DP_edge, nullptr); + EXPECT_NE(PD_edge, nullptr); + + GraphId node_id = PD_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(PD_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(DP_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 3); + ASSERT_EQ(linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "Lebeaustraat"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "nl"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(1).text(), "Rue Lebeau"); + iter = linguistics.find(1); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(2).text(), "Brussel"); + iter = linguistics.find(2); + ASSERT_EQ(iter, linguistics.end()); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_fr_nl_BrusselsBelgium, CheckNonJunctionName) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"J", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, + {"Rue Bodenbroek/Bodenbroekstraat/N6", + "Rue Bodenbroek/Bodenbroekstraat/N6", + "Rue Bodenbroek/Bodenbroekstraat/N6", + "Rue Bodenbroek/Bodenbroekstraat/N6", "Rue Lebeau/Lebeaustraat"}); + + // Verify starting on Rue Bodenbroek/Bodenbroekstraat/N6 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Rue Bodenbroek"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Bodenbroekstraat"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "N6"); + + ++maneuver_index; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 2); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Rue Lebeau"); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Lebeaustraat"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Rue Lebeau"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + lang_iter = linguistics.find(1); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Lebeaustraat"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "nl"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + // No junction should exist here. Named junctions are not allowed in Belgium + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .junction_names_size(), + 0); +} + +class RouteWithStreetnameAndSign_ru_be_MinskBelarus : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + J + | + | + | + I + /|\ + / | \ + / | \ + L----K-------------H----G + A----B-------------E----F + \ | / + \ | / + \|/ + C + | + | + | + D + O------PM------Q + | + | + | + N + + )"; + + const gurka::ways ways = { + {"ABEF", {{"highway", "motorway"}, {"name", ""}, {"ref", "М2"}, {"oneway", "yes"}}}, + {"GHKL", {{"highway", "motorway"}, {"name", ""}, {"ref", "М2"}, {"oneway", "yes"}}}, + {"JICDMN", + {{"highway", "primary"}, + {"osm_id", "100"}, + {"name", "МКАД, 1-й километр"}, + {"name:ru", "МКАД, 1-й километр"}, + {"name:be", "1-ы кіламетр МКАД"}, + {"ref", "M9"}}}, + {"BC", + {{"highway", "motorway_link"}, + {"osm_id", "101"}, + {"name", ""}, + {"oneway", "yes"}, + {"junction:ref", "12"}, + {"destination", "Гомель;Слуцк"}, + {"destination:street", "1-ы кіламетр МКАД"}, + {"destination:street:lang:ru", "МКАД, 1-й километр"}, + {"destination:street:lang:be", "1-ы кіламетр МКАД"}, + {"destination:ref", "M9"}}}, + {"CE", + {{"highway", "motorway_link"}, {"name", ""}, {"oneway", "yes"}, {"destination:ref", "М2"}}}, + {"HI", + {{"highway", "motorway_link"}, + {"osm_id", "102"}, + {"name", ""}, + {"oneway", "yes"}, + {"junction:ref", "12"}, + {"destination:street:to", "Партизанский проспект"}, + {"destination:street:to:lang:ru", "Партизанский проспект"}, + {"destination:street:to:lang:be", "Партызанскі праспект"}, + {"destination:ref:to", "M4"}}}, + {"IK", + {{"highway", "motorway_link"}, {"name", ""}, {"oneway", "yes"}, {"destination:ref", "М2"}}}, + {"OPMQ", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", "Днепровская улица"}, + {"name:ru", "Днепровская улица"}, + {"name:be", "Дняпроўская вуліца"}}}, + {"DP", + {{"highway", "secondary_link"}, + {"osm_id", "104"}, + {"name", ""}, + {"oneway", "yes"}, + {"destination", "Гомель"}, + {"destination:street", "Днепровская улица"}, + {"destination:street:lang:ru", "Днепровская улица"}, + {"destination:street:lang:be", "Дняпроўская вуліца"}}}, + }; + + const gurka::nodes nodes = {{"M", {{"highway", "traffic_signals"}, {"name", "Zaventem"}}}}; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {27.56191, 53.90246}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, nodes, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_ru_be_MinskBelarus, CheckStreetNamesAndSigns1) { + + const std::string workdir = "test/data/gurka_language_with_streetname_and_sign_ru_be_MinskBelarus"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"A", "D"}, "auto"); + gurka::assert::raw::expect_path(result, {"М2", "", "МКАД, 1-й километр/1-ы кіламетр МКАД/M9"}); + + // Verify starting on М2 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 1); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "М2"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets_size(), + 3); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(0) + .text(), + "M9"); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(1) + .text(), + "1-ы кіламетр МКАД"); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(2) + .text(), + "МКАД, 1-й километр"); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations_size(), + 2); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(0) + .text(), + "Гомель"); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(1) + .text(), + "Слуцк"); + + ++maneuver_index; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "МКАД, 1-й километр"); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "1-ы кіламетр МКАД"); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "M9"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, the_map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId JICDMN_edge_id; + const DirectedEdge* JICDMN_edge = nullptr; + GraphId NMDCIJ_edge_id; + const DirectedEdge* NMDCIJ_edge = nullptr; + std::tie(JICDMN_edge_id, JICDMN_edge, NMDCIJ_edge_id, NMDCIJ_edge) = + findEdge(graph_reader, the_map.nodes, "", "N", baldr::GraphId{}, 100); + EXPECT_NE(JICDMN_edge, nullptr); + EXPECT_NE(NMDCIJ_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(BC_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(BC_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 6); + ASSERT_EQ(linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "12"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(1).text(), "M9"); + iter = linguistics.find(1); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(2).text(), "1-ы кіламетр МКАД"); + iter = linguistics.find(2); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "be"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(3).text(), "МКАД, 1-й километр"); + iter = linguistics.find(3); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "ru"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(4).text(), "Гомель"); + iter = linguistics.find(4); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(5).text(), "Слуцк"); + iter = linguistics.find(5); + ASSERT_EQ(iter, linguistics.end()); + + node_id = JICDMN_edge->endnode(); + tile = graph_reader.GetGraphTile(node_id); + edgeinfo = tile->edgeinfo(JICDMN_edge); + types.clear(); + names_and_types = edgeinfo.GetNamesAndTypes(true); + std::unordered_map> name_linguistics = + edgeinfo.GetLinguisticMap(); + ; + + ASSERT_EQ(names_and_types.size(), 3); + + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "МКАД, 1-й километр"); + std::unordered_map>::const_iterator lang_iter = + name_linguistics.find(0); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "ru"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "1-ы кіламетр МКАД"); + lang_iter = name_linguistics.find(1); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "be"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(2)), "M9"); + lang_iter = name_linguistics.find(2); + ASSERT_EQ(lang_iter, name_linguistics.end()); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_ru_be_MinskBelarus, CheckStreetNamesAndSigns2) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"G", "J"}, "auto"); + gurka::assert::raw::expect_path(result, {"М2", "", "МКАД, 1-й километр/1-ы кіламетр МКАД/M9"}); + + // Verify starting on М2 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 1); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "М2"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations_size(), + 3); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(0) + .text(), + "M4"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(1) + .text(), + "Партизанский проспект"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(2) + .text(), + "Партызанскі праспект"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId HI_edge_id; + const DirectedEdge* HI_edge = nullptr; + GraphId IH_edge_id; + const DirectedEdge* IH_edge = nullptr; + std::tie(HI_edge_id, HI_edge, IH_edge_id, IH_edge) = + findEdge(graph_reader, the_map.nodes, "", "I", baldr::GraphId{}, 102); + EXPECT_NE(HI_edge, nullptr); + EXPECT_NE(IH_edge, nullptr); + + GraphId node_id = HI_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(HI_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(HI_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 4); + ASSERT_EQ(linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "12"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(1).text(), "M4"); + iter = linguistics.find(1); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(2).text(), "Партизанский проспект"); + iter = linguistics.find(2); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "ru"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(3).text(), "Партызанскі праспект"); + iter = linguistics.find(3); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "be"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_ru_be_MinskBelarus, CheckGuideSigns) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"J", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"МКАД, 1-й километр/1-ы кіламетр МКАД/M9", + "МКАД, 1-й километр/1-ы кіламетр МКАД/M9", + "МКАД, 1-й километр/1-ы кіламетр МКАД/M9", "", + "Днепровская улица/Дняпроўская вуліца"}); + + // Verify starting on МКАД, 1-й километр/1-ы кіламетр МКАД/M9 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "МКАД, 1-й километр"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "1-ы кіламетр МКАД"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "M9"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets_size(), + 2); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets(0) + .text(), + "Днепровская улица"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets(1) + .text(), + "Дняпроўская вуліца"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_toward_locations_size(), + 1); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_toward_locations(0) + .text(), + "Гомель"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId DP_edge_id; + const DirectedEdge* DP_edge = nullptr; + GraphId PD_edge_id; + const DirectedEdge* PD_edge = nullptr; + std::tie(DP_edge_id, DP_edge, PD_edge_id, PD_edge) = + findEdge(graph_reader, the_map.nodes, "", "P", baldr::GraphId{}, 104); + EXPECT_NE(DP_edge, nullptr); + EXPECT_NE(PD_edge, nullptr); + + GraphId node_id = PD_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(PD_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(DP_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 3); + ASSERT_EQ(linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "Днепровская улица"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "ru"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(1).text(), "Дняпроўская вуліца"); + iter = linguistics.find(1); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "be"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(2).text(), "Гомель"); + iter = linguistics.find(2); + ASSERT_EQ(iter, linguistics.end()); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_ru_be_MinskBelarus, CheckNonJunctionName) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"J", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, {"МКАД, 1-й километр/1-ы кіламетр МКАД/M9", + "МКАД, 1-й километр/1-ы кіламетр МКАД/M9", + "МКАД, 1-й километр/1-ы кіламетр МКАД/M9", + "МКАД, 1-й километр/1-ы кіламетр МКАД/M9", + "Днепровская улица/Дняпроўская вуліца"}); + + // Verify starting on МКАД, 1-й километр/1-ы кіламетр МКАД/M9 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "МКАД, 1-й километр"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "1-ы кіламетр МКАД"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "M9"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Днепровская улица"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "ru"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + lang_iter = linguistics.find(1); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Дняпроўская вуліца"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "be"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + // No junction should exist here. Named junctions are not allowed in Belarus + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .junction_names_size(), + 0); +} + +class RouteWithStreetnameAndSign_cy_en_Wales : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + J + | + | + | + I + /|\ + / | \ + / | \ + L----K-------------H----G + A----B-------------E----F + \ | / + \ | / + \|/ + C + | + | + | + D + O------PM------Q + | + | + | + N + + )"; + + const gurka::ways ways = { + {"ABEF", + {{"highway", "motorway"}, + {"name", "Gwibffordd Gogledd Cymru / North Wales Expressway"}, + {"name:en", "North Wales Expressway"}, + {"name:cy", "Gwibffordd Gogledd Cymru"}, + {"ref", "A55"}, + {"oneway", "yes"}}}, + {"GHKL", + {{"highway", "motorway"}, + {"name", "Gwibffordd Gogledd Cymru / North Wales Expressway"}, + {"name:en", "North Wales Expressway"}, + {"name:cy", "Gwibffordd Gogledd Cymru"}, + {"ref", "A55"}, + {"oneway", "yes"}}}, + {"JICDMN", + {{"highway", "primary"}, + {"osm_id", "100"}, + {"name", "Caernarfon Road"}, + {"name:cy", "Ffordd Caernarfon"}, + {"ref", "A4087"}}}, + {"BC", + {{"highway", "motorway_link"}, + {"osm_id", "101"}, + {"name", ""}, + {"oneway", "yes"}, + {"junction:ref", "26B"}, + {"destination", "Newport;"}, + {"destination:lang:cy", "Casnewydd"}, + {"destination:street", "Caernarfon Road"}, + {"destination:street:lang:cy", "Ffordd Caernarfon"}, + {"destination:ref", "A4087"}}}, + {"CE", + {{"highway", "motorway_link"}, {"name", ""}, {"oneway", "yes"}, {"destination:ref", "A55"}}}, + {"HI", + {{"highway", "motorway_link"}, + {"osm_id", "102"}, + {"name", ""}, + {"oneway", "yes"}, + {"junction:ref", "26B"}, + {"destination:street:to", "Ainon Road"}, + {"destination:street:to:lang:cy", "Ffordd Ainion"}, + {"destination:ref:to", "M4"}}}, + {"IK", + {{"highway", "motorway_link"}, {"name", ""}, {"oneway", "yes"}, {"destination:ref", "A55"}}}, + {"OPMQ", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", "North Street"}, + {"name:en", "Penchwintan Road"}, + {"name:cy", "Ffordd Penchwintan"}}}, + {"DP", + {{"highway", "secondary_link"}, + {"osm_id", "104"}, + {"name", ""}, + {"oneway", "yes"}, + {"destination", "Newport"}, + {"destination:street", "Penchwintan Road"}, + {"destination:street:lang:cy", "Ffordd Penchwintan"}}}, + }; + + const gurka::nodes nodes = {{"M", {{"highway", "traffic_signals"}, {"name", "M Junction"}}}}; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-3.73895, 52.29282}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, nodes, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_cy_en_Wales, CheckStreetNamesAndSigns1) { + + const std::string workdir = "test/data/gurka_language_with_streetname_and_sign_cy_en_Wales"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"A", "D"}, "auto"); + gurka::assert::raw::expect_path(result, {"A55/Gwibffordd Gogledd Cymru/North Wales Expressway", "", + "Caernarfon Road/Ffordd Caernarfon/A4087"}); + + // Verify starting on A55 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "A55"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Gwibffordd Gogledd Cymru"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "North Wales Expressway"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets_size(), + 3); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(0) + .text(), + "A4087"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(1) + .text(), + "Caernarfon Road"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations_size(), + 2); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(0) + .text(), + "Newport"); + + ++maneuver_index; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Caernarfon Road"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Ffordd Caernarfon"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "A4087"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, the_map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId JICDMN_edge_id; + const DirectedEdge* JICDMN_edge = nullptr; + GraphId NMDCIJ_edge_id; + const DirectedEdge* NMDCIJ_edge = nullptr; + std::tie(JICDMN_edge_id, JICDMN_edge, NMDCIJ_edge_id, NMDCIJ_edge) = + findEdge(graph_reader, the_map.nodes, "", "N", baldr::GraphId{}, 100); + EXPECT_NE(JICDMN_edge, nullptr); + EXPECT_NE(NMDCIJ_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(BC_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(BC_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 6); + ASSERT_EQ(linguistics.size(), 4); + + ASSERT_EQ(edge_signs.at(0).text(), "26B"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(1).text(), "A4087"); + iter = linguistics.find(1); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(2).text(), "Caernarfon Road"); + iter = linguistics.find(2); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(3).text(), "Ffordd Caernarfon"); + iter = linguistics.find(3); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "cy"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(4).text(), "Newport"); + iter = linguistics.find(4); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(5).text(), "Casnewydd"); + iter = linguistics.find(5); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "cy"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + node_id = JICDMN_edge->endnode(); + tile = graph_reader.GetGraphTile(node_id); + edgeinfo = tile->edgeinfo(JICDMN_edge); + types.clear(); + names_and_types = edgeinfo.GetNamesAndTypes(true); + std::unordered_map> name_linguistics = + edgeinfo.GetLinguisticMap(); + ; + + ASSERT_EQ(names_and_types.size(), 3); + + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Caernarfon Road"); + std::unordered_map>::const_iterator lang_iter = + name_linguistics.find(0); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Ffordd Caernarfon"); + lang_iter = name_linguistics.find(1); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "cy"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(2)), "A4087"); + lang_iter = name_linguistics.find(2); + ASSERT_EQ(lang_iter, name_linguistics.end()); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_cy_en_Wales, CheckStreetNamesAndSigns2) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"G", "J"}, "auto"); + gurka::assert::raw::expect_path(result, {"A55/Gwibffordd Gogledd Cymru/North Wales Expressway", "", + "Caernarfon Road/Ffordd Caernarfon/A4087"}); + + // Verify starting on A55 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "A55"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Gwibffordd Gogledd Cymru"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "North Wales Expressway"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations_size(), + 3); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(0) + .text(), + "M4"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(1) + .text(), + "Ainon Road"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId HI_edge_id; + const DirectedEdge* HI_edge = nullptr; + GraphId IH_edge_id; + const DirectedEdge* IH_edge = nullptr; + std::tie(HI_edge_id, HI_edge, IH_edge_id, IH_edge) = + findEdge(graph_reader, the_map.nodes, "", "I", baldr::GraphId{}, 102); + EXPECT_NE(HI_edge, nullptr); + EXPECT_NE(IH_edge, nullptr); + + GraphId node_id = HI_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(HI_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(HI_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 4); + ASSERT_EQ(linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "26B"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(1).text(), "M4"); + iter = linguistics.find(1); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(2).text(), "Ainon Road"); + iter = linguistics.find(2); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(3).text(), "Ffordd Ainion"); + iter = linguistics.find(3); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "cy"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_cy_en_Wales, CheckGuideSigns) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"J", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"Caernarfon Road/Ffordd Caernarfon/A4087", + "Caernarfon Road/Ffordd Caernarfon/A4087", + "Caernarfon Road/Ffordd Caernarfon/A4087", "", + "North Street/Ffordd Penchwintan/Penchwintan Road"}); + + // Verify starting on Caernarfon Road/A4087 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Caernarfon Road"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Ffordd Caernarfon"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "A4087"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets_size(), + 2); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets(0) + .text(), + "Penchwintan Road"); + // GREG + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets(1) + .text(), + "Ffordd Penchwintan"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_toward_locations_size(), + 1); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_toward_locations(0) + .text(), + "Newport"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId DP_edge_id; + const DirectedEdge* DP_edge = nullptr; + GraphId PD_edge_id; + const DirectedEdge* PD_edge = nullptr; + std::tie(DP_edge_id, DP_edge, PD_edge_id, PD_edge) = + findEdge(graph_reader, the_map.nodes, "", "P", baldr::GraphId{}, 104); + EXPECT_NE(DP_edge, nullptr); + EXPECT_NE(PD_edge, nullptr); + + GraphId node_id = PD_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(PD_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(DP_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 3); + ASSERT_EQ(linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "Penchwintan Road"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(1).text(), "Ffordd Penchwintan"); + iter = linguistics.find(1); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "cy"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(2).text(), "Newport"); + iter = linguistics.find(2); + ASSERT_EQ(iter, linguistics.end()); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_cy_en_Wales, CheckNonJunctionName) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"J", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, {"Caernarfon Road/Ffordd Caernarfon/A4087", + "Caernarfon Road/Ffordd Caernarfon/A4087", + "Caernarfon Road/Ffordd Caernarfon/A4087", + "Caernarfon Road/Ffordd Caernarfon/A4087", + "North Street/Ffordd Penchwintan/Penchwintan Road"}); + + // Verify starting on Caernarfon Road/A4087 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Caernarfon Road"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Ffordd Caernarfon"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "A4087"); + + // Verify street name language tag + ++maneuver_index; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "North Street"); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Ffordd Penchwintan"); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "Penchwintan Road"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 3); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_EQ(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "North Street"); + + lang_iter = linguistics.find(1); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Ffordd Penchwintan"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "cy"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + lang_iter = linguistics.find(2); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(2)), "Penchwintan Road"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + // No junction should exist here. Named junctions are not allowed in UK + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .junction_names_size(), + 0); +} + +class RouteWithStreetnameAndSign_fr_nl_BrusselsBelgiumRightLeft : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + O------PM------Q + )"; + + const gurka::ways ways = { + {"OPMQ", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", "Chaussée de Gand - Steenweg op Gent/Gentsesteenweg"}, + {"name:fr", "Chaussée de Gand"}, + {"name:left", "Chaussée de Gand - Gentsesteenweg"}, + {"name:left:nl", "Gentsesteenweg"}, + {"name:right", "Chaussée de Gand - Steenweg op Gent"}, + {"name:right:nl", "Steenweg op Gent"}}}, + }; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {4.3516970, 50.8465573}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_fr_nl_BrusselsBelgiumRightLeft, CheckRightNames) { + + const std::string workdir = + "test/data/gurka_language_with_streetname_and_sign_fr_nl_BrusselsBelgiumRightLeft"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"O", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, {"Chaussée de Gand/Steenweg op Gent"}); + + // Verify starting on Chaussée de Gand/Steenweg op Gent + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 2); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Chaussée de Gand"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Steenweg op Gent"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Chaussée de Gand"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + lang_iter = linguistics.find(1); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Steenweg op Gent"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "nl"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_fr_nl_BrusselsBelgiumRightLeft, CheckLeftNames) { + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"Q", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"Chaussée de Gand/Gentsesteenweg"}); + + // Verify starting on Chaussée de Gand/Gentsesteenweg + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 2); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Chaussée de Gand"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Gentsesteenweg"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = QMPO_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(QMPO_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Chaussée de Gand"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + lang_iter = linguistics.find(1); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Gentsesteenweg"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "nl"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); +} + +class RouteWithStreetnameAndSign_en_USForwardBackwardWithName : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + O------PM------Q + )"; + + const gurka::ways ways = { + {"OPMQ", + { + {"highway", "secondary"}, + {"osm_id", "103"}, + {"name", "Waltonville Road"}, + {"name:forward", "Waltonville Road"}, + {"name:backward", "Quarry Road"}, + {"ref", "C-1;A"}, + {"ref:right", "C-1"}, + {"ref:left", "A"}, + }}, + }; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-76.69980, 40.25882}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_USForwardBackwardWithName, CheckForwardNames) { + + const std::string workdir = + "test/data/gurka_language_with_streetname_and_sign_en_USForwardBackwardwithName"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"O", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, {"Waltonville Road/C-1"}); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Waltonville Road"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + lang_iter = linguistics.find(1); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "C-1"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_USForwardBackwardWithName, CheckBackwardNames) { + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"Q", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"Quarry Road/A"}); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = QMPO_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(QMPO_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Quarry Road"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + lang_iter = linguistics.find(1); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "A"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); +} + +class RouteWithStreetnameAndSign_en_USForwardBackwardNoName : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + O------PM------Q + )"; + + const gurka::ways ways = { + {"OPMQ", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", ""}, + {"name:forward", "Waltonville Road"}, + {"name:backward", "Quarry Road"}}}, + }; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-76.69980, 40.25882}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_USForwardBackwardNoName, CheckForwardNames) { + + const std::string workdir = + "test/data/gurka_language_with_streetname_and_sign_en_USForwardBackwardwithName"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"O", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, {"Waltonville Road"}); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 1); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Waltonville Road"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_USForwardBackwardNoName, CheckBackwardNames) { + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"Q", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"Quarry Road"}); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = QMPO_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(QMPO_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 1); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Quarry Road"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); +} + +/* TODO Fix this edge case. This works while using degrees (boost) for points but it is too slow. +class RouteWithStreetnameAndSign_fr_nl_MesenBelgiumRightLeft : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + O------PM------Q + )"; + + const gurka::ways ways = { + {"QMPO", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", "Komenstraat - Chemin des Quatre Rois"}, + {"name:left", "Chemin des Quatre Rois"}, + {"name:left:fr", "Chemin des Quatre Rois"}, + {"name:left:nl", "Vier Koningenweg"}, + {"name:right", "Komenstraat"}}}, + }; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + constexpr double gridsize = 100; + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {2.9305333, 50.7672572}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_fr_nl_MesenBelgiumRightLeft, CheckRightNames) { + + // Supports name:left when traveling O to Q. Supports name:right when traveling Q to O + // See https://www.openstreetmap.org/relation/90348#map=15/50.7660/2.9469 and + // https://www.openstreetmap.org/way/30126046#map=17/50.76759/2.93026 note the way has been flipped. + // starts at O and ends at Q Bail on language as the way left and right and name info is not 100% + // correct and we are traversing on the polygon perimeters + + const std::string workdir = + "test/data/gurka_language_with_streetname_and_sign_fr_nl_MesenBelgiumRightLeft"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"Q", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"Komenstraat"}); + + // Verify starting on Komenstraat + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 1); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Komenstraat"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = QMPO_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(QMPO_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 1); + + std::unordered_map> linguistics = +edgeinfo.GetLinguisticMap(); std::unordered_map>::const_iterator lang_iter = linguistics.find(0); ASSERT_EQ(lang_iter, +linguistics.end()); ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Komenstraat"); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_fr_nl_MesenBelgiumRightLeft, CheckLeftNames) { + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"O", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, {"Chemin des Quatre Rois"}); + + // Verify starting on Chaussée de Gand/Steenweg op Gent + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 1); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Chemin des Quatre Rois"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 1); + + std::unordered_map> linguistics = +edgeinfo.GetLinguisticMap(); std::unordered_map>::const_iterator lang_iter = linguistics.find(0); ASSERT_EQ(lang_iter, +linguistics.end()); ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Chemin des Quatre Rois"); +} +*/ + +class RouteWithStreetnameAndSign_fr_de_FribourgSwitzerlandMulti : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + O------PM------Q + )"; + + const gurka::ways ways = { + {"QMPO", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", "Route des Alpes / Alpenstrasse"}, + {"name:de", "Alpenstrasse"}}}, + }; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {7.159328, 46.805244}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_fr_de_FribourgSwitzerlandMulti, CheckForwardNames) { + + const std::string workdir = + "test/data/gurka_language_with_streetname_and_sign_fr_de_FribourgSwitzerlandMulti"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"Q", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"Route des Alpes/Alpenstrasse"}); + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = QMPO_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(QMPO_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Route des Alpes"); + + lang_iter = linguistics.find(1); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "de"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Alpenstrasse"); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_fr_de_FribourgSwitzerlandMulti, CheckBackwardNames) { + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"O", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, {"Route des Alpes/Alpenstrasse"}); + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Route des Alpes"); + + lang_iter = linguistics.find(1); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "de"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Alpenstrasse"); +} + +class RouteWithStreetnameAndSign_rm_de_BivioSwitzerland : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + O------PM------Q + )"; + + const gurka::ways ways = { + {"OPMQ", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", "Vea del Giulia"}, + {"name:it", "Vea del Giulia"}, + {"name:de", "Julierstrasse"}, + {"name:rm", "Via digl Gelgia"}}}, + }; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {9.65035, 46.46977}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_rm_de_BivioSwitzerland, CheckForwardNames) { + + // language IT should be dropped, but since in name tag we don't toss. + // Lingustic polys/default linguistics in this area is rm and de. + // actual hotel address: + // Hotel Post, Julierstrasse 64 CH-7457 Bivio + const std::string workdir = + "test/data/gurka_language_with_streetname_and_sign_rm_de_BivioSwitzerland"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"O", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, {"Vea del Giulia/Via digl Gelgia/Julierstrasse"}); + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 3); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_EQ(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Vea del Giulia"); + + lang_iter = linguistics.find(1); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "rm"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Via digl Gelgia"); + + lang_iter = linguistics.find(2); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "de"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + ASSERT_EQ(std::get<0>(names_and_types.at(2)), "Julierstrasse"); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_rm_de_BivioSwitzerland, CheckBackwardNames) { + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"Q", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"Vea del Giulia/Via digl Gelgia/Julierstrasse"}); + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = QMPO_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(QMPO_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 3); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_EQ(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Vea del Giulia"); + + lang_iter = linguistics.find(1); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "rm"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Via digl Gelgia"); + + lang_iter = linguistics.find(2); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "de"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + ASSERT_EQ(std::get<0>(names_and_types.at(2)), "Julierstrasse"); +} + +class RouteWithStreetnameAndSign_de_ZurichSwitzerland : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + O------PM------Q + )"; + + const gurka::ways ways = { + {"OPMQ", {{"highway", "secondary"}, {"osm_id", "103"}, {"name", "Werdstrasse"}}}, + }; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {8.5355, 47.3726}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_de_ZurichSwitzerland, CheckForwardNames) { + + const std::string workdir = + "test/data/gurka_language_with_streetname_and_sign_de_ZurichSwitzerland"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"O", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, {"Werdstrasse"}); + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 1); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "de"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Werdstrasse"); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_de_ZurichSwitzerland, CheckBackwardNames) { + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"Q", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"Werdstrasse"}); + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = QMPO_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(QMPO_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 1); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "de"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Werdstrasse"); +} + +class RouteWithStreetnameAndSign_fr_nl_EupenBelgium : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + D + O------PM------Q + + )"; + + const gurka::ways ways = { + {"OPMQ", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", "Bergstraße"}, + {"name:de", "Bergstraße"}, + {"name:fr", "Rue de la Montagne"}}}, + {"DP", + {{"highway", "secondary_link"}, + {"osm_id", "104"}, + {"name", ""}, + {"oneway", "yes"}, + {"destination", "Eupen"}, + {"destination:street", "Bergstraße"}, + {"destination:street:lang:fr", "Rue de la Montagne"}, + {"destination:street:lang:nl", "Lebeaustraat"}}}, + }; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {6.03475, 50.62766}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_fr_nl_EupenBelgium, CheckLingusticPoly) { + + const std::string workdir = "test/data/gurka_language_with_streetname_and_sign_fr_nl_EupenBelgium"; + + // Supports DE and FR. DE is a higher priority as it is a German community + // See https://www.openstreetmap.org/relation/2425209#map=10/50.4440/6.1805 and + // https://www.openstreetmap.org/relation/90348 + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"O", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, + {"Bergstraße/Rue de la Montagne", "Bergstraße/Rue de la Montagne"}); + + // Verify starting on Chaussée de Gand/Steenweg op Gent + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 2); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Bergstraße"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Rue de la Montagne"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Bergstraße"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "de"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + lang_iter = linguistics.find(1); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Rue de la Montagne"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + GraphId DP_edge_id; + const DirectedEdge* DP_edge = nullptr; + GraphId PD_edge_id; + const DirectedEdge* PD_edge = nullptr; + std::tie(DP_edge_id, DP_edge, PD_edge_id, PD_edge) = + findEdge(graph_reader, the_map.nodes, "", "P", baldr::GraphId{}, 104); + EXPECT_NE(DP_edge, nullptr); + EXPECT_NE(PD_edge, nullptr); + + node_id = PD_edge->endnode(); + tile = graph_reader.GetGraphTile(node_id); + edgeinfo = tile->edgeinfo(PD_edge); + types.clear(); + names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> sign_linguistics; + + std::vector edge_signs = tile->GetSigns(DP_edge_id.id(), sign_linguistics); + + ASSERT_EQ(edge_signs.size(), 3); + ASSERT_EQ(sign_linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "Bergstraße"); + std::unordered_map>::const_iterator iter = + sign_linguistics.find(0); + ASSERT_NE(iter, sign_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "de"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(1).text(), "Rue de la Montagne"); + iter = sign_linguistics.find(1); + ASSERT_NE(iter, sign_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(2).text(), "Eupen"); + iter = sign_linguistics.find(2); + ASSERT_EQ(iter, sign_linguistics.end()); +} + +class RouteWithStreetnameAndSign_ja_en_Japan : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + J + | + | + | + I + /|\ + / | \ + / | \ + L----K-------------H----G + A----B-------------E----F + \ | / + \ | / + \|/ + C + | + | + | + D + O------PM------Q + | + | + | + N + + )"; + + const gurka::ways ways = { + {"ABEF", + {{"highway", "motorway"}, + {"name", "首都高速6号向島線"}, + {"name:en", "Shuto Expressway Route 6 Mukojima Line"}, + {"name:es", "Ruta 6 Mukojima de la Autopista Shuto"}, + {"name:ja", "首都高速6号向島線"}, + {"name:ru", "Шоссе Мукодзима"}, + {"ref", "6"}, + {"oneway", "yes"}}}, + {"GHKL", + {{"highway", "motorway"}, + {"name", "首都高速6号向島線"}, + {"name:en", "Shuto Expressway Route 6 Mukojima Line"}, + {"name:es", "Ruta 6 Mukojima de la Autopista Shuto"}, + {"name:ja", "首都高速6号向島線"}, + {"name:ru", "Шоссе Мукодзима"}, + {"ref", "6"}, + {"oneway", "yes"}}}, + {"JICDMN", + {{"highway", "primary"}, + {"osm_id", "100"}, + {"name", "常磐道;東北道"}, + {"name:en", "Joban Expressway;Tohoku Expressway"}, + {"ref", "E6;E4"}}}, + {"BC", + {{"highway", "motorway_link"}, + {"osm_id", "101"}, + {"name", ""}, + {"oneway", "yes"}, + {"junction:ref", "26B"}, + {"destination", "常磐道;東北道;"}, + {"destination:lang:en", "Joban Expressway;Tohoku Expressway"}, + {"destination:street", "清洲橋通り"}, + {"destination:street:lang:en", "Kiyosubashi-dori Avenue"}, + {"destination:street:lang:es", "Calle Kiyosubashi"}, + {"destination:street:lang:ja", "清洲橋通り"}, + {"destination:street:lang:ja_rm", "Kiyosubashi Dōri"}, + {"destination:ref", "E6;E4"}}}, + {"CE", + {{"highway", "motorway_link"}, + {"name", ""}, + {"oneway", "yes"}, + {"destination:ref", "E6;E4"}}}, + {"HI", + {{"highway", "motorway_link"}, + {"osm_id", "102"}, + {"name", ""}, + {"oneway", "yes"}, + {"junction:ref", "26B"}, + {"destination:street:to", "清澄通り"}, + {"destination:street:to:lang:en", "Kiyosumi-dori"}, + {"destination:street:to:lang:es", "Calle Kiyosumi"}, + {"destination:street:to:lang:ja_kana", "きよすみどおり"}, + {"destination:ref:to", "M4"}}}, + {"IK", + {{"highway", "motorway_link"}, + {"name", ""}, + {"oneway", "yes"}, + {"destination:ref", "E6;E4"}}}, + {"OPMQ", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", "国技館通り"}, + {"name:en", "Kokugikan-dori"}, + {"name:es", "Calle Kokugikan"}, + {"name:ja_kana", "こくぎかんどおり"}}}, + {"DP", + {{"highway", "secondary_link"}, + {"osm_id", "104"}, + {"name", ""}, + {"oneway", "yes"}, + {"destination", "大和街道"}, + {"destination:street", "国技館通り"}, + {"destination:street:lang:en", "Kokugikan-dori"}, + {"destination:street:lang:es", "Calle Kokugikan"}, + {"destination:street:lang:ja_kana", "こくぎかんどおり"}}}, + }; + + const gurka::nodes nodes = { + {"M", + {{"highway", "traffic_signals"}, {"name", "両国二丁目"}, {"name:en", "Ryogoku 2-chome"}}}}; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {139.79079, 35.69194}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, nodes, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_ja_en_Japan, CheckStreetNamesAndSigns1) { + + const std::string workdir = "test/data/gurka_language_with_streetname_and_sign_ja_en_Japan"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"A", "D"}, "auto"); + gurka::assert::raw::expect_path(result, + {"6/首都高速6号向島線/Shuto Expressway Route 6 Mukojima Line", "", + "常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4"}); + + // Verify starting on 6 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "6"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "首都高速6号向島線"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "Shuto Expressway Route 6 Mukojima Line"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets_size(), + 4); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(0) + .text(), + "E6"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(1) + .text(), + "E4"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(2) + .text(), + "清洲橋通り"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(3) + .text(), + "Kiyosubashi-dori Avenue"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations_size(), + 4); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(0) + .text(), + "常磐道"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(1) + .text(), + "東北道"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(2) + .text(), + "Joban Expressway"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(3) + .text(), + "Tohoku Expressway"); + + ++maneuver_index; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 6); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "常磐道"); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "東北道"); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "Joban Expressway"); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(3).value(), + "Tohoku Expressway"); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(4).value(), + "E6"); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(5).value(), + "E4"); + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, the_map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId JICDMN_edge_id; + const DirectedEdge* JICDMN_edge = nullptr; + GraphId NMDCIJ_edge_id; + const DirectedEdge* NMDCIJ_edge = nullptr; + std::tie(JICDMN_edge_id, JICDMN_edge, NMDCIJ_edge_id, NMDCIJ_edge) = + findEdge(graph_reader, the_map.nodes, "", "N", baldr::GraphId{}, 100); + EXPECT_NE(JICDMN_edge, nullptr); + EXPECT_NE(NMDCIJ_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(BC_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(BC_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 9); + ASSERT_EQ(linguistics.size(), 6); + + ASSERT_EQ(edge_signs.at(0).text(), "26B"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(1).text(), "E6"); + iter = linguistics.find(1); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(2).text(), "E4"); + iter = linguistics.find(2); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(3).text(), "清洲橋通り"); + iter = linguistics.find(3); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "ja"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(4).text(), "Kiyosubashi-dori Avenue"); + iter = linguistics.find(4); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(5).text(), "常磐道"); + iter = linguistics.find(5); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "ja"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(6).text(), "東北道"); + iter = linguistics.find(6); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "ja"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(7).text(), "Joban Expressway"); + iter = linguistics.find(7); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(8).text(), "Tohoku Expressway"); + iter = linguistics.find(8); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + node_id = JICDMN_edge->endnode(); + tile = graph_reader.GetGraphTile(node_id); + edgeinfo = tile->edgeinfo(JICDMN_edge); + types.clear(); + names_and_types = edgeinfo.GetNamesAndTypes(true); + std::unordered_map> name_linguistics = + edgeinfo.GetLinguisticMap(); + ; + + ASSERT_EQ(names_and_types.size(), 6); + + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "常磐道"); + std::unordered_map>::const_iterator lang_iter = + name_linguistics.find(0); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "ja"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "東北道"); + lang_iter = name_linguistics.find(1); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "ja"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(2)), "Joban Expressway"); + lang_iter = name_linguistics.find(2); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(3)), "Tohoku Expressway"); + lang_iter = name_linguistics.find(3); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(4)), "E6"); + lang_iter = name_linguistics.find(4); + ASSERT_EQ(lang_iter, name_linguistics.end()); + + ASSERT_EQ(std::get<0>(names_and_types.at(5)), "E4"); + lang_iter = name_linguistics.find(5); + ASSERT_EQ(lang_iter, name_linguistics.end()); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_ja_en_Japan, CheckStreetNamesAndSigns2) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"G", "J"}, "auto"); + gurka::assert::raw::expect_path(result, + {"6/首都高速6号向島線/Shuto Expressway Route 6 Mukojima Line", "", + "常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4"}); + + // Verify starting on 6 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "6"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "首都高速6号向島線"); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "Shuto Expressway Route 6 Mukojima Line"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations_size(), + 3); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(0) + .text(), + "M4"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(1) + .text(), + "清澄通り"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(2) + .text(), + "Kiyosumi-dori"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId HI_edge_id; + const DirectedEdge* HI_edge = nullptr; + GraphId IH_edge_id; + const DirectedEdge* IH_edge = nullptr; + std::tie(HI_edge_id, HI_edge, IH_edge_id, IH_edge) = + findEdge(graph_reader, the_map.nodes, "", "I", baldr::GraphId{}, 102); + EXPECT_NE(HI_edge, nullptr); + EXPECT_NE(IH_edge, nullptr); + + GraphId node_id = HI_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(HI_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(HI_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 4); + ASSERT_EQ(linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "26B"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(1).text(), "M4"); + iter = linguistics.find(1); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(2).text(), "清澄通り"); + iter = linguistics.find(2); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "ja"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(3).text(), "Kiyosumi-dori"); + iter = linguistics.find(3); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_ja_en_Japan, CheckGuideSigns) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"J", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4", + "常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4", + "常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4", + "", "国技館通り/Kokugikan-dori"}); + + // Verify starting on 常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 6); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 6); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "常磐道"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "東北道"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "Joban Expressway"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(3).value(), + "Tohoku Expressway"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(4).value(), + "E6"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(5).value(), + "E4"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets_size(), + 2); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets(0) + .text(), + "国技館通り"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets(1) + .text(), + "Kokugikan-dori"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_toward_locations_size(), + 1); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_toward_locations(0) + .text(), + "大和街道"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId DP_edge_id; + const DirectedEdge* DP_edge = nullptr; + GraphId PD_edge_id; + const DirectedEdge* PD_edge = nullptr; + std::tie(DP_edge_id, DP_edge, PD_edge_id, PD_edge) = + findEdge(graph_reader, the_map.nodes, "", "P", baldr::GraphId{}, 104); + EXPECT_NE(DP_edge, nullptr); + EXPECT_NE(PD_edge, nullptr); + + GraphId node_id = PD_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(PD_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(DP_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 3); + ASSERT_EQ(linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "国技館通り"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "ja"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(1).text(), "Kokugikan-dori"); + iter = linguistics.find(1); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(2).text(), "大和街道"); + iter = linguistics.find(2); + ASSERT_EQ(iter, linguistics.end()); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_ja_en_Japan, CheckNonJunctionName) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"J", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, {"常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4", + "常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4", + "常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4", + "常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4", + "国技館通り/Kokugikan-dori"}); + + // Verify starting on 常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 6); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "常磐道"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "東北道"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "Joban Expressway"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(3).value(), + "Tohoku Expressway"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(4).value(), + "E6"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(5).value(), + "E4"); + + // Verify street name language tag + ++maneuver_index; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 2); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "国技館通り"); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Kokugikan-dori"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "国技館通り"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "ja"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + lang_iter = linguistics.find(1); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Kokugikan-dori"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + // Junction should exist here. Named junctions are allowed in JP + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .junction_names_size(), + 2); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .junction_names(0) + .text(), + "両国二丁目"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .junction_names(1) + .text(), + "Ryogoku 2-chome"); + + GraphId DM_edge_id; + const DirectedEdge* DM_edge = nullptr; + GraphId MD_edge_id; + const DirectedEdge* MD_edge = nullptr; + std::tie(DM_edge_id, DM_edge, MD_edge_id, MD_edge) = + findEdge(graph_reader, the_map.nodes, "", "M", baldr::GraphId{}, 100); + EXPECT_NE(DM_edge, nullptr); + EXPECT_NE(MD_edge, nullptr); + + node_id = DM_edge->endnode(); + tile = graph_reader.GetGraphTile(node_id); + + std::unordered_map> sign_linguistics; + + std::vector edge_signs = tile->GetSigns(node_id.id(), sign_linguistics, true); + + ASSERT_EQ(edge_signs.size(), 2); + ASSERT_EQ(sign_linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "両国二丁目"); + std::unordered_map>::const_iterator iter = + sign_linguistics.find(0); + ASSERT_NE(iter, sign_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "ja"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(1).text(), "Ryogoku 2-chome"); + iter = sign_linguistics.find(1); + ASSERT_NE(iter, sign_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); +} + +class RouteWithStreetnameAndSign_en_fr_OttawaCanada : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + J + | + | + | + I + /|\ + / | \ + / | \ + L----K-------------H----G + A----B-------------E----F + \ | / + \ | / + \|/ + C + | + | + | + D + O------PM------Q + | + | + | + N + + )"; + + const gurka::ways ways = { + {"ABEF", + {{"highway", "motorway"}, + {"name", "Highway 417"}, + {"name:en", "Highway 417"}, + {"name:fr", "Route 417"}, + {"ref", "417"}, + {"nat_name", "Trans-Canada Highway"}, + {"nat_name:en", "Trans-Canada Highway"}, + {"nat_name:fr", " Route Transcanadienne"}, + {"oneway", "yes"}}}, + {"GHKL", + {{"highway", "motorway"}, + {"name", "Highway 417"}, + {"name:en", "Highway 417"}, + {"name:fr", "Route 417"}, + {"ref", "417"}, + {"nat_name", "Trans-Canada Highway"}, + {"nat_name:en", "Trans-Canada Highway"}, + {"nat_name:fr", " Route Transcanadienne"}, + {"oneway", "yes"}}}, + {"JICDMN", + {{"highway", "primary"}, + {"osm_id", "100"}, + {"name", ""}, + {"name:en", "Vanier Parkway"}, + {"name:fr", "promenade Vanier"}, + {"ref", "19"}}}, + {"BC", + {{"highway", "motorway_link"}, + {"osm_id", "101"}, + {"name", ""}, + {"oneway", "yes"}, + {"junction:ref", "26B"}, + {"destination", "Sandy Hill;"}, + {"destination:lang:fr", "Colline de sable"}, + {"destination:street", "Vanier Parkway;Riverside Drive"}, + {"destination:street:lang:fr", "Promenade Vanier;Promenade Riverside"}, + {"destination:street:lang:en", "Vanier Parkway;Riverside Drive"}, + {"destination:ref", "19"}}}, + {"CE", + {{"highway", "motorway_link"}, {"name", ""}, {"oneway", "yes"}, {"destination:ref", "417"}}}, + {"HI", + {{"highway", "motorway_link"}, + {"osm_id", "102"}, + {"name", ""}, + {"oneway", "yes"}, + {"junction:ref", "26B"}, + {"destination:street:to", "Queen Street"}, + {"destination:street:to:lang:fr", "rue Queen"}, + {"destination:ref:to", "19"}}}, + {"IK", + {{"highway", "motorway_link"}, {"name", ""}, {"oneway", "yes"}, {"destination:ref", "417"}}}, + {"OPMQ", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", "Albert Street"}, + {"name:en", "Albert Street"}, + {"name:fr", "rue Albert"}}}, + {"DP", + {{"highway", "secondary_link"}, + {"osm_id", "104"}, + {"name", ""}, + {"oneway", "yes"}, + {"destination", "Centretown"}, + {"destination:street", "rue Albert"}, + {"destination:street:lang:en", "Albert Street"}}}, + }; + + const gurka::nodes nodes = {{"M", {{"highway", "traffic_signals"}, {"name", "M Junction"}}}}; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-75.6625, 45.3940}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, nodes, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_fr_OttawaCanada, CheckStreetNamesAndSigns1) { + + const std::string workdir = "test/data/gurka_language_with_streetname_and_sign_en_fr_OttawaCanada"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"A", "D"}, "auto"); + gurka::assert::raw::expect_path(result, {"417/Highway 417/Route 417", "", + "Vanier Parkway/promenade Vanier/19"}); + + // Verify starting on 417 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "417"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Highway 417"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "Route 417"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets_size(), + 5); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(0) + .text(), + "19"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(1) + .text(), + "Vanier Parkway"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(2) + .text(), + "Riverside Drive"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(3) + .text(), + "Promenade Vanier"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(4) + .text(), + "Promenade Riverside"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations_size(), + 2); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(0) + .text(), + "Sandy Hill"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(1) + .text(), + "Colline de sable"); + + ++maneuver_index; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Vanier Parkway"); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "promenade Vanier"); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "19"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, the_map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId JICDMN_edge_id; + const DirectedEdge* JICDMN_edge = nullptr; + GraphId NMDCIJ_edge_id; + const DirectedEdge* NMDCIJ_edge = nullptr; + std::tie(JICDMN_edge_id, JICDMN_edge, NMDCIJ_edge_id, NMDCIJ_edge) = + findEdge(graph_reader, the_map.nodes, "", "N", baldr::GraphId{}, 100); + EXPECT_NE(JICDMN_edge, nullptr); + EXPECT_NE(NMDCIJ_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(BC_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(BC_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 8); + ASSERT_EQ(linguistics.size(), 6); + + ASSERT_EQ(edge_signs.at(0).text(), "26B"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(1).text(), "19"); + iter = linguistics.find(1); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(2).text(), "Vanier Parkway"); + iter = linguistics.find(2); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(3).text(), "Riverside Drive"); + iter = linguistics.find(3); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(4).text(), "Promenade Vanier"); + iter = linguistics.find(4); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(5).text(), "Promenade Riverside"); + iter = linguistics.find(5); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(6).text(), "Sandy Hill"); + iter = linguistics.find(6); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(7).text(), "Colline de sable"); + iter = linguistics.find(7); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + node_id = JICDMN_edge->endnode(); + tile = graph_reader.GetGraphTile(node_id); + edgeinfo = tile->edgeinfo(JICDMN_edge); + types.clear(); + names_and_types = edgeinfo.GetNamesAndTypes(true); + std::unordered_map> name_linguistics = + edgeinfo.GetLinguisticMap(); + ; + + ASSERT_EQ(names_and_types.size(), 3); + + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Vanier Parkway"); + std::unordered_map>::const_iterator lang_iter = + name_linguistics.find(0); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "promenade Vanier"); + lang_iter = name_linguistics.find(1); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(2)), "19"); + lang_iter = name_linguistics.find(2); + ASSERT_EQ(lang_iter, name_linguistics.end()); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_fr_OttawaCanada, CheckStreetNamesAndSigns2) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"G", "J"}, "auto"); + gurka::assert::raw::expect_path(result, {"417/Highway 417/Route 417", "", + "Vanier Parkway/promenade Vanier/19"}); + + // Verify starting on 417 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "417"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Highway 417"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "Route 417"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations_size(), + 3); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(0) + .text(), + "19"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(1) + .text(), + "Queen Street"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(2) + .text(), + "rue Queen"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId HI_edge_id; + const DirectedEdge* HI_edge = nullptr; + GraphId IH_edge_id; + const DirectedEdge* IH_edge = nullptr; + std::tie(HI_edge_id, HI_edge, IH_edge_id, IH_edge) = + findEdge(graph_reader, the_map.nodes, "", "I", baldr::GraphId{}, 102); + EXPECT_NE(HI_edge, nullptr); + EXPECT_NE(IH_edge, nullptr); + + GraphId node_id = HI_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(HI_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(HI_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 4); + ASSERT_EQ(linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "26B"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(1).text(), "19"); + iter = linguistics.find(1); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(2).text(), "Queen Street"); + iter = linguistics.find(2); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(3).text(), "rue Queen"); + iter = linguistics.find(3); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_fr_OttawaCanada, CheckGuideSigns) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"J", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"Vanier Parkway/promenade Vanier/19", + "Vanier Parkway/promenade Vanier/19", + "Vanier Parkway/promenade Vanier/19", "", + "Albert Street/rue Albert"}); + + // Verify starting on Vanier Parkway/promenade Vanier/19 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Vanier Parkway"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "promenade Vanier"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "19"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets_size(), + 2); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets(0) + .text(), + "rue Albert"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets(1) + .text(), + "Albert Street"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_toward_locations_size(), + 1); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_toward_locations(0) + .text(), + "Centretown"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId DP_edge_id; + const DirectedEdge* DP_edge = nullptr; + GraphId PD_edge_id; + const DirectedEdge* PD_edge = nullptr; + std::tie(DP_edge_id, DP_edge, PD_edge_id, PD_edge) = + findEdge(graph_reader, the_map.nodes, "", "P", baldr::GraphId{}, 104); + EXPECT_NE(DP_edge, nullptr); + EXPECT_NE(PD_edge, nullptr); + + GraphId node_id = PD_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(PD_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(DP_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 3); + ASSERT_EQ(linguistics.size(), 2); + + // note flipped on purpose + ASSERT_EQ(edge_signs.at(0).text(), "rue Albert"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(1).text(), "Albert Street"); + iter = linguistics.find(1); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(2).text(), "Centretown"); + iter = linguistics.find(2); + ASSERT_EQ(iter, linguistics.end()); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_fr_OttawaCanada, CheckNonJunctionName) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"J", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, + {"Vanier Parkway/promenade Vanier/19", + "Vanier Parkway/promenade Vanier/19", + "Vanier Parkway/promenade Vanier/19", + "Vanier Parkway/promenade Vanier/19", "Albert Street/rue Albert"}); + + // Verify starting on Vanier Parkway/promenade Vanier/19 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Vanier Parkway"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "promenade Vanier"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "19"); + + // Verify street name language tag + ++maneuver_index; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 2); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Albert Street"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "rue Albert"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Albert Street"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + lang_iter = linguistics.find(1); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "rue Albert"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + // No junction should exist here. Named junctions are not allowed in CA + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .junction_names_size(), + 0); +} + +class RouteWithStreetnameAndSign_en_fr_QuebecCanada : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + J + | + | + | + I + /|\ + / | \ + / | \ + L----K-------------H----G + A----B-------------E----F + \ | / + \ | / + \|/ + C + | + | + | + D + O------PM------Q + | + | + | + N + + )"; + + const gurka::ways ways = { + {"ABEF", + {{"highway", "motorway"}, + {"name", "Highway 417"}, + {"name:en", "Highway 417"}, + {"name:fr", "Route 417"}, + {"ref", "417"}, + {"nat_name", "Trans-Canada Highway"}, + {"nat_name:en", "Trans-Canada Highway"}, + {"nat_name:fr", " Route Transcanadienne"}, + {"oneway", "yes"}}}, + {"GHKL", + {{"highway", "motorway"}, + {"name", "Highway 417"}, + {"name:en", "Highway 417"}, + {"name:fr", "Route 417"}, + {"ref", "417"}, + {"nat_name", "Trans-Canada Highway"}, + {"nat_name:en", "Trans-Canada Highway"}, + {"nat_name:fr", " Route Transcanadienne"}, + {"oneway", "yes"}}}, + {"JICDMN", + {{"highway", "primary"}, + {"osm_id", "100"}, + {"name", ""}, + {"name:en", "Vanier Parkway"}, + {"name:fr", "promenade Vanier"}, + {"ref", "19"}}}, + {"BC", + {{"highway", "motorway_link"}, + {"osm_id", "101"}, + {"name", ""}, + {"oneway", "yes"}, + {"junction:ref", "26B"}, + {"destination", "Sandy Hill;"}, + {"destination:lang:fr", "Colline de sable"}, + {"destination:street", "Vanier Parkway;Riverside Drive"}, + {"destination:street:lang:fr", "Promenade Vanier;Promenade Riverside"}, + {"destination:street:lang:en", "Vanier Parkway;Riverside Drive"}, + {"destination:ref", "19"}}}, + {"CE", + {{"highway", "motorway_link"}, {"name", ""}, {"oneway", "yes"}, {"destination:ref", "417"}}}, + {"HI", + {{"highway", "motorway_link"}, + {"osm_id", "102"}, + {"name", ""}, + {"oneway", "yes"}, + {"junction:ref", "26B"}, + {"destination:street:to", "Queen Street"}, + {"destination:street:to:lang:fr", "rue Queen"}, + {"destination:ref:to", "19"}}}, + {"IK", + {{"highway", "motorway_link"}, {"name", ""}, {"oneway", "yes"}, {"destination:ref", "417"}}}, + {"OPMQ", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", "Albert Street"}, + {"name:en", "Albert Street"}, + {"name:fr", "rue Albert"}}}, + {"DP", + {{"highway", "secondary_link"}, + {"osm_id", "104"}, + {"name", ""}, + {"oneway", "yes"}, + {"destination", "Centretown"}, + {"destination:street", "rue Albert"}, + {"destination:street:lang:en", "Albert Street"}}}, + }; + + const gurka::nodes nodes = {{"M", {{"highway", "traffic_signals"}, {"name", "M Junction"}}}}; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-71.2593, 46.8111}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, nodes, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_fr_QuebecCanada, CheckStreetNamesAndSigns1) { + + const std::string workdir = "test/data/gurka_language_with_streetname_and_sign_en_fr_QuebecCanada"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"A", "D"}, "auto"); + gurka::assert::raw::expect_path(result, {"417/Highway 417/Route 417", "", + "promenade Vanier/Vanier Parkway/19"}); + + // Verify starting on 417 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "417"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Highway 417"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "Route 417"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets_size(), + 5); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(0) + .text(), + "19"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(1) + .text(), + "Vanier Parkway"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_onto_streets(2) + .text(), + "Riverside Drive"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations_size(), + 2); + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(0) + .text(), + "Sandy Hill"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(1) + .text(), + "Colline de sable"); + + ++maneuver_index; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "promenade Vanier"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Vanier Parkway"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "19"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, the_map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId JICDMN_edge_id; + const DirectedEdge* JICDMN_edge = nullptr; + GraphId NMDCIJ_edge_id; + const DirectedEdge* NMDCIJ_edge = nullptr; + std::tie(JICDMN_edge_id, JICDMN_edge, NMDCIJ_edge_id, NMDCIJ_edge) = + findEdge(graph_reader, the_map.nodes, "", "N", baldr::GraphId{}, 100); + EXPECT_NE(JICDMN_edge, nullptr); + EXPECT_NE(NMDCIJ_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(BC_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(BC_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 8); + ASSERT_EQ(linguistics.size(), 6); + + ASSERT_EQ(edge_signs.at(0).text(), "26B"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(1).text(), "19"); + iter = linguistics.find(1); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(2).text(), "Vanier Parkway"); + iter = linguistics.find(2); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(3).text(), "Riverside Drive"); + iter = linguistics.find(3); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(4).text(), "Promenade Vanier"); + iter = linguistics.find(4); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(5).text(), "Promenade Riverside"); + iter = linguistics.find(5); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(6).text(), "Sandy Hill"); + iter = linguistics.find(6); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(7).text(), "Colline de sable"); + iter = linguistics.find(7); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + node_id = JICDMN_edge->endnode(); + tile = graph_reader.GetGraphTile(node_id); + edgeinfo = tile->edgeinfo(JICDMN_edge); + types.clear(); + names_and_types = edgeinfo.GetNamesAndTypes(true); + std::unordered_map> name_linguistics = + edgeinfo.GetLinguisticMap(); + ; + + ASSERT_EQ(names_and_types.size(), 3); + + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "promenade Vanier"); + std::unordered_map>::const_iterator lang_iter = + name_linguistics.find(0); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Vanier Parkway"); + lang_iter = name_linguistics.find(1); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(2)), "19"); + lang_iter = name_linguistics.find(2); + ASSERT_EQ(lang_iter, name_linguistics.end()); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_fr_QuebecCanada, CheckStreetNamesAndSigns2) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"G", "J"}, "auto"); + gurka::assert::raw::expect_path(result, {"417/Highway 417/Route 417", "", + "promenade Vanier/Vanier Parkway/19"}); + + // Verify starting on 417 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "417"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Highway 417"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "Route 417"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations_size(), + 3); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(0) + .text(), + "19"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(1) + .text(), + "Queen Street"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .exit_toward_locations(2) + .text(), + "rue Queen"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId HI_edge_id; + const DirectedEdge* HI_edge = nullptr; + GraphId IH_edge_id; + const DirectedEdge* IH_edge = nullptr; + std::tie(HI_edge_id, HI_edge, IH_edge_id, IH_edge) = + findEdge(graph_reader, the_map.nodes, "", "I", baldr::GraphId{}, 102); + EXPECT_NE(HI_edge, nullptr); + EXPECT_NE(IH_edge, nullptr); + + GraphId node_id = HI_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(HI_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(HI_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 4); + ASSERT_EQ(linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "26B"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(1).text(), "19"); + iter = linguistics.find(1); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(2).text(), "Queen Street"); + iter = linguistics.find(2); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(3).text(), "rue Queen"); + iter = linguistics.find(3); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_fr_QuebecCanada, CheckGuideSigns) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"J", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"promenade Vanier/Vanier Parkway/19", + "promenade Vanier/Vanier Parkway/19", + "promenade Vanier/Vanier Parkway/19", "", + "Albert Street/rue Albert"}); + + // Verify starting on Vanier Parkway/promenade Vanier/19 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "promenade Vanier"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Vanier Parkway"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "19"); + + ++maneuver_index; + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets_size(), + 2); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets(0) + .text(), + "rue Albert"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets(1) + .text(), + "Albert Street"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_onto_streets(0) + .text(), + "rue Albert"); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_toward_locations_size(), + 1); + + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .guide_toward_locations(0) + .text(), + "Centretown"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId DP_edge_id; + const DirectedEdge* DP_edge = nullptr; + GraphId PD_edge_id; + const DirectedEdge* PD_edge = nullptr; + std::tie(DP_edge_id, DP_edge, PD_edge_id, PD_edge) = + findEdge(graph_reader, the_map.nodes, "", "P", baldr::GraphId{}, 104); + EXPECT_NE(DP_edge, nullptr); + EXPECT_NE(PD_edge, nullptr); + + GraphId node_id = PD_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(PD_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(DP_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 3); + ASSERT_EQ(linguistics.size(), 2); + + // note flipped on purpose + ASSERT_EQ(edge_signs.at(0).text(), "rue Albert"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(1).text(), "Albert Street"); + iter = linguistics.find(1); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(2).text(), "Centretown"); + iter = linguistics.find(2); + ASSERT_EQ(iter, linguistics.end()); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_fr_QuebecCanada, CheckNonJunctionName) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"J", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, + {"promenade Vanier/Vanier Parkway/19", + "promenade Vanier/Vanier Parkway/19", + "promenade Vanier/Vanier Parkway/19", + "promenade Vanier/Vanier Parkway/19", "Albert Street/rue Albert"}); + + // Verify starting on Vanier Parkway/promenade Vanier/19 + int maneuver_index = 0; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 3); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "promenade Vanier"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "Vanier Parkway"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(2).value(), + "19"); + + // Verify street name language tag + ++maneuver_index; + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name_size(), 2); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(0).value(), + "Albert Street"); + EXPECT_EQ(result.directions().routes(0).legs(0).maneuver(maneuver_index).street_name(1).value(), + "rue Albert"); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Albert Street"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + lang_iter = linguistics.find(1); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "rue Albert"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "fr"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + // No junction should exist here. Named junctions are not allowed in CA + EXPECT_EQ(result.directions() + .routes(0) + .legs(0) + .maneuver(maneuver_index) + .sign() + .junction_names_size(), + 0); +} + +class RouteWithStreetnameAndSign_en_ms_ta_zh_Singapore : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + O------PM------Q + )"; + const gurka::ways ways = { + {"OPMQ", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", "Rochor"}, + {"name:en", "Rochor"}, + {"name:ms", "Rochor"}, + {"name:ta", "ரோச்சோர்"}, + {"name:zh", "梧槽"}, + {"name:en:pronunciation:jeita", "English Language pronunciation"}, + {"name:zh:pronunciation:jeita", "Native zh Language pronunciation"}, + {"name:pronunciation:jeita", "Native Language pronunciation"}}}, + }; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {103.87149, 1.32510}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_ms_ta_zh_Singapore, CheckForwardNames) { + + const std::string workdir = + "test/data/gurka_language_with_streetname_and_sign_en_ms_ta_zh_Singapore"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"O", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, {"Rochor/梧槽/Rochor/ரோச்சோர்"}); + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 4); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + ASSERT_EQ(linguistics.size(), 4); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kJeita)); + EXPECT_EQ(std::get(lang_iter->second), + "English Language pronunciation"); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Rochor"); + + lang_iter = linguistics.find(1); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "zh"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kJeita)); + EXPECT_EQ(std::get(lang_iter->second), + "Native zh Language pronunciation"); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "梧槽"); + + lang_iter = linguistics.find(2); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "ms"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + ASSERT_EQ(std::get<0>(names_and_types.at(2)), "Rochor"); + + lang_iter = linguistics.find(3); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "ta"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + ASSERT_EQ(std::get<0>(names_and_types.at(3)), "ரோச்சோர்"); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_ms_ta_zh_Singapore, CheckBackwardNames) { + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"Q", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"Rochor/梧槽/Rochor/ரோச்சோர்"}); + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = QMPO_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(QMPO_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 4); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + ASSERT_EQ(linguistics.size(), 4); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kJeita)); + EXPECT_EQ(std::get(lang_iter->second), + "English Language pronunciation"); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Rochor"); + + lang_iter = linguistics.find(1); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "zh"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kJeita)); + EXPECT_EQ(std::get(lang_iter->second), + "Native zh Language pronunciation"); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "梧槽"); + + lang_iter = linguistics.find(2); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "ms"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + ASSERT_EQ(std::get<0>(names_and_types.at(2)), "Rochor"); + + lang_iter = linguistics.find(3); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "ta"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + ASSERT_EQ(std::get<0>(names_and_types.at(3)), "ரோச்சோர்"); +} + +class RouteWithStreetnameAndSign_ja_en_JapanPronunciations : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + J + | + | + | + I + /|\ + / | \ + / | \ + L----K-------------H----G + A----B-------------E----F + \ | / + \ | / + \|/ + C + | + | + | + D + O------PM------Q + | + | + | + N + + )"; + + const gurka::ways ways = { + {"ABEF", + {{"highway", "motorway"}, + {"osm_id", "98"}, + {"name", "首都高速6号向島線"}, + {"name:ja:pronunciation", "ja_name_pronunciation"}, + {"name:pronunciation", "ja_name_pronunciation"}, + // TODO: When multiple linguistics are supported, make sure pronunciations reference the + // correct name in the list. For now these are tossed. + {"name:en", "Shuto Expressway Route 6 Mukojima Line"}, + {"name:es", "Ruta 6 Mukojima de la Autopista Shuto"}, + {"name:ja", "首都高速6号向島線"}, + {"name:ru", "Шоссе Мукодзима"}, + {"ref", "6"}, + {"oneway", "yes"}}}, + {"GHKL", + {{"highway", "motorway"}, + {"osm_id", "99"}, + {"name", "首都高速6号向島線"}, + {"name:pronunciation", "ja_name_pronunciation"}, + {"name:en", "Shuto Expressway Route 6 Mukojima Line"}, + {"name:es", "Ruta 6 Mukojima de la Autopista Shuto"}, + {"name:ja", "首都高速6号向島線"}, + {"name:ru", "Шоссе Мукодзима"}, + {"ref", "6"}, + {"oneway", "yes"}}}, + {"JICDMN", + {{"highway", "primary"}, + {"osm_id", "100"}, + {"name", "常磐道;東北道"}, + {"name:en", "Joban Expressway;Tohoku Expressway"}, + {"ref", "E6;E4"}}}, + {"BC", + {{"highway", "motorway_link"}, + {"osm_id", "101"}, + {"name", ""}, + {"oneway", "yes"}, + {"junction:ref", "26B"}, + {"destination", "常磐道;東北道"}, + {"destination:lang:ja", "常磐道;東北道"}, + {"destination:pronunciation", + "ja_destination_pronunciation_1;ja_destination_pronunciation_2"}, + {"destination:lang:ja:pronunciation", + "ja_destination_pronunciation_1;ja_destination_pronunciation_2"}, + {"destination:lang:en:pronunciation", + "en_destination_pronunciation_1;en_destination_pronunciation_2"}, + {"destination:lang:en", "Joban Expressway;Tohoku Expressway"}, + {"destination:street", "清洲橋通り"}, + {"destination:street:lang:en", "Kiyosubashi-dori Avenue"}, + {"destination:street:lang:es", "Calle Kiyosubashi"}, + {"destination:street:lang:ja", "清洲橋通り"}, + {"destination:street:lang:ja_rm", "Kiyosubashi Dōri"}, + {"destination:ref", "E6;E4"}}}, + {"CE", + {{"highway", "motorway_link"}, + {"name", ""}, + {"oneway", "yes"}, + {"destination:ref", "E6;E4"}}}, + {"HI", + {{"highway", "motorway_link"}, + {"osm_id", "102"}, + {"name", ""}, + {"oneway", "yes"}, + {"junction:ref", "26B"}, + {"destination:street:to", "清澄通り"}, + {"destination:street:to:lang:en", "Kiyosumi-dori"}, + {"destination:street:to:lang:es", "Calle Kiyosumi"}, + {"destination:street:to:lang:ja_kana", "きよすみどおり"}, + {"destination:ref:to", "M4"}}}, + {"IK", + {{"highway", "motorway_link"}, + {"name", ""}, + {"oneway", "yes"}, + {"destination:ref", "E6;E4"}}}, + {"OPMQ", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", "国技館通り"}, + {"name:en", "Kokugikan-dori"}, + {"name:es", "Calle Kokugikan"}, + {"name:ja_kana", "こくぎかんどおり"}, + {"name:pronunciation", "ja_name_pronunciation_1;ja_name_pronunciation_2"}}}, + {"DP", + {{"highway", "secondary_link"}, + {"osm_id", "104"}, + {"name", ""}, + {"oneway", "yes"}, + {"destination", "大和街道"}, + {"destination:street", "国技館通り"}, + {"destination:street:lang:en", "Kokugikan-dori"}, + {"destination:street:lang:es", "Calle Kokugikan"}, + {"destination:street:lang:ja_kana", "こくぎかんどおり"}}}, + }; + + const gurka::nodes nodes = { + {"M", + {{"highway", "traffic_signals"}, {"name", "両国二丁目"}, {"name:en", "Ryogoku 2-chome"}}}}; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {139.79079, 35.69194}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, nodes, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_ja_en_JapanPronunciations, CheckStreetNamesAndSigns1) { + + const std::string workdir = + "test/data/gurka_language_with_streetname_and_sign_ja_en_JapanPronunciations"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"A", "D"}, "auto"); + gurka::assert::raw::expect_path(result, + {"6/首都高速6号向島線/Shuto Expressway Route 6 Mukojima Line", "", + "常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4"}); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId ABEF_edge_id; + const DirectedEdge* ABEF_edge = nullptr; + GraphId FEBA_edge_id; + const DirectedEdge* FEBA_edge = nullptr; + std::tie(ABEF_edge_id, ABEF_edge, FEBA_edge_id, FEBA_edge) = + findEdge(graph_reader, the_map.nodes, "", "F", baldr::GraphId{}, 98); + EXPECT_NE(ABEF_edge, nullptr); + EXPECT_NE(FEBA_edge, nullptr); + + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, the_map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId JICDMN_edge_id; + const DirectedEdge* JICDMN_edge = nullptr; + GraphId NMDCIJ_edge_id; + const DirectedEdge* NMDCIJ_edge = nullptr; + std::tie(JICDMN_edge_id, JICDMN_edge, NMDCIJ_edge_id, NMDCIJ_edge) = + findEdge(graph_reader, the_map.nodes, "", "N", baldr::GraphId{}, 100); + EXPECT_NE(JICDMN_edge, nullptr); + EXPECT_NE(NMDCIJ_edge, nullptr); + + GraphId node_id = ABEF_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(ABEF_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + + ASSERT_EQ(names_and_types.size(), 3); + ASSERT_EQ(linguistics.size(), 2); + + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "6"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "首都高速6号向島線"); + iter = linguistics.find(1); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "ja"); + ASSERT_EQ(std::get(iter->second), "ja_name_pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kIpa)); + + ASSERT_EQ(std::get<0>(names_and_types.at(2)), "Shuto Expressway Route 6 Mukojima Line"); + iter = linguistics.find(2); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + ASSERT_EQ(std::get(iter->second), ""); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + + node_id = BC_edge->endnode(); + tile = graph_reader.GetGraphTile(node_id); + edgeinfo = tile->edgeinfo(BC_edge); + types.clear(); + names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> sign_linguistics; + + std::vector edge_signs = tile->GetSigns(BC_edge_id.id(), sign_linguistics); + + ASSERT_EQ(edge_signs.size(), 9); + ASSERT_EQ(sign_linguistics.size(), 6); + + ASSERT_EQ(edge_signs.at(0).text(), "26B"); + iter = sign_linguistics.find(0); + ASSERT_EQ(iter, sign_linguistics.end()); + + ASSERT_EQ(edge_signs.at(1).text(), "E6"); + iter = sign_linguistics.find(1); + ASSERT_EQ(iter, sign_linguistics.end()); + + ASSERT_EQ(edge_signs.at(2).text(), "E4"); + iter = sign_linguistics.find(2); + ASSERT_EQ(iter, sign_linguistics.end()); + + ASSERT_EQ(edge_signs.at(3).text(), "清洲橋通り"); + iter = sign_linguistics.find(3); + ASSERT_NE(iter, sign_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "ja"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(4).text(), "Kiyosubashi-dori Avenue"); + iter = sign_linguistics.find(4); + ASSERT_NE(iter, sign_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(5).text(), "常磐道"); + iter = sign_linguistics.find(5); + ASSERT_NE(iter, sign_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "ja"); + ASSERT_EQ(std::get(iter->second), + "ja_destination_pronunciation_1"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kIpa)); + + ASSERT_EQ(edge_signs.at(6).text(), "東北道"); + iter = sign_linguistics.find(6); + ASSERT_NE(iter, sign_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "ja"); + ASSERT_EQ(std::get(iter->second), + "ja_destination_pronunciation_2"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kIpa)); + + ASSERT_EQ(edge_signs.at(7).text(), "Joban Expressway"); + iter = sign_linguistics.find(7); + ASSERT_NE(iter, sign_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + ASSERT_EQ(std::get(iter->second), + "en_destination_pronunciation_1"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kIpa)); + + ASSERT_EQ(edge_signs.at(8).text(), "Tohoku Expressway"); + iter = sign_linguistics.find(8); + ASSERT_NE(iter, sign_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + ASSERT_EQ(std::get(iter->second), + "en_destination_pronunciation_2"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kIpa)); + node_id = JICDMN_edge->endnode(); + tile = graph_reader.GetGraphTile(node_id); + edgeinfo = tile->edgeinfo(JICDMN_edge); + types.clear(); + names_and_types = edgeinfo.GetNamesAndTypes(true); + std::unordered_map> name_linguistics = + edgeinfo.GetLinguisticMap(); + ; + + ASSERT_EQ(names_and_types.size(), 6); + + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "常磐道"); + std::unordered_map>::const_iterator lang_iter = + name_linguistics.find(0); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "ja"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "東北道"); + lang_iter = name_linguistics.find(1); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "ja"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(2)), "Joban Expressway"); + lang_iter = name_linguistics.find(2); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(3)), "Tohoku Expressway"); + lang_iter = name_linguistics.find(3); + ASSERT_NE(lang_iter, name_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(std::get<0>(names_and_types.at(4)), "E6"); + lang_iter = name_linguistics.find(4); + ASSERT_EQ(lang_iter, name_linguistics.end()); + + ASSERT_EQ(std::get<0>(names_and_types.at(5)), "E4"); + lang_iter = name_linguistics.find(5); + ASSERT_EQ(lang_iter, name_linguistics.end()); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_ja_en_JapanPronunciations, CheckStreetNamesAndSigns2) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"G", "J"}, "auto"); + gurka::assert::raw::expect_path(result, + {"6/首都高速6号向島線/Shuto Expressway Route 6 Mukojima Line", "", + "常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4"}); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId HI_edge_id; + const DirectedEdge* HI_edge = nullptr; + GraphId IH_edge_id; + const DirectedEdge* IH_edge = nullptr; + std::tie(HI_edge_id, HI_edge, IH_edge_id, IH_edge) = + findEdge(graph_reader, the_map.nodes, "", "I", baldr::GraphId{}, 102); + EXPECT_NE(HI_edge, nullptr); + EXPECT_NE(IH_edge, nullptr); + + GraphId node_id = HI_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(HI_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(HI_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 4); + ASSERT_EQ(linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "26B"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(1).text(), "M4"); + iter = linguistics.find(1); + ASSERT_EQ(iter, linguistics.end()); + + ASSERT_EQ(edge_signs.at(2).text(), "清澄通り"); + iter = linguistics.find(2); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "ja"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(3).text(), "Kiyosumi-dori"); + iter = linguistics.find(3); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_ja_en_JapanPronunciations, CheckGuideSigns) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"J", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4", + "常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4", + "常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4", + "", "国技館通り/Kokugikan-dori"}); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId DP_edge_id; + const DirectedEdge* DP_edge = nullptr; + GraphId PD_edge_id; + const DirectedEdge* PD_edge = nullptr; + std::tie(DP_edge_id, DP_edge, PD_edge_id, PD_edge) = + findEdge(graph_reader, the_map.nodes, "", "P", baldr::GraphId{}, 104); + EXPECT_NE(DP_edge, nullptr); + EXPECT_NE(PD_edge, nullptr); + + GraphId node_id = PD_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(PD_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 0); + + std::unordered_map> linguistics; + + std::vector edge_signs = tile->GetSigns(DP_edge_id.id(), linguistics); + + ASSERT_EQ(edge_signs.size(), 3); + ASSERT_EQ(linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "国技館通り"); + std::unordered_map>::const_iterator iter = + linguistics.find(0); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "ja"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(1).text(), "Kokugikan-dori"); + iter = linguistics.find(1); + ASSERT_NE(iter, linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(iter->second), ""); + + ASSERT_EQ(edge_signs.at(2).text(), "大和街道"); + iter = linguistics.find(2); + ASSERT_EQ(iter, linguistics.end()); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_ja_en_JapanPronunciations, CheckNonJunctionName) { + auto result = gurka::do_action(valhalla::Options::route, the_map, {"J", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, {"常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4", + "常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4", + "常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4", + "常磐道/東北道/Joban Expressway/Tohoku Expressway/E6/E4", + "国技館通り/Kokugikan-dori"}); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "国技館通り"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "ja"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + lang_iter = linguistics.find(1); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Kokugikan-dori"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + GraphId DM_edge_id; + const DirectedEdge* DM_edge = nullptr; + GraphId MD_edge_id; + const DirectedEdge* MD_edge = nullptr; + std::tie(DM_edge_id, DM_edge, MD_edge_id, MD_edge) = + findEdge(graph_reader, the_map.nodes, "", "M", baldr::GraphId{}, 100); + EXPECT_NE(DM_edge, nullptr); + EXPECT_NE(MD_edge, nullptr); + + node_id = DM_edge->endnode(); + tile = graph_reader.GetGraphTile(node_id); + + std::unordered_map> sign_linguistics; + + std::vector edge_signs = tile->GetSigns(node_id.id(), sign_linguistics, true); + + ASSERT_EQ(edge_signs.size(), 2); + ASSERT_EQ(sign_linguistics.size(), 2); + + ASSERT_EQ(edge_signs.at(0).text(), "両国二丁目"); + std::unordered_map>::const_iterator iter = + sign_linguistics.find(0); + ASSERT_NE(iter, sign_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "ja"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + ASSERT_EQ(edge_signs.at(1).text(), "Ryogoku 2-chome"); + iter = sign_linguistics.find(1); + ASSERT_NE(iter, sign_linguistics.end()); + ASSERT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); +} + +class RouteWithStreetnameAndSign_en_USMultiWithNameDash : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + O------PM------Q + )"; + + const gurka::ways ways = { + {"OPMQ", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", "Pamplona South - Airport"}, + {"name:en", "Pamplona South - Airport"}}}, + }; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-76.69980, 40.25882}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_USMultiWithNameDash, CheckForwardNames) { + + const std::string workdir = + "test/data/gurka_language_with_streetname_and_sign_en_USMultiWithNameDash"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"O", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, {"Pamplona South/Airport"}); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Pamplona South"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + lang_iter = linguistics.find(1); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Airport"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_USMultiWithNameDash, CheckBackwardNames) { + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"Q", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"Pamplona South/Airport"}); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = QMPO_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(QMPO_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Pamplona South"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + lang_iter = linguistics.find(1); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Airport"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); +} + +class RouteWithStreetnameAndSign_en_USMultiWithNameSlash : public ::testing::Test { +protected: + valhalla::gurka::map BuildPBF(const std::string& workdir) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + O------PM------Q + )"; + + const gurka::ways ways = { + {"OPMQ", + {{"highway", "secondary"}, + {"osm_id", "103"}, + {"name", "Pamplona South / Airport"}, + {"name:en", "Pamplona South / Airport"}}}, + }; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-76.69980, 40.25882}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + return result; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_USMultiWithNameSlash, CheckForwardNames) { + + const std::string workdir = + "test/data/gurka_language_with_streetname_and_sign_en_USMultiWithNameSlash"; + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + the_map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; + the_map.config = + test::make_config(workdir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.tile_dir", workdir + "/tiles"}}); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(the_map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"O", "Q"}, "auto"); + gurka::assert::raw::expect_path(result, {"Pamplona South/Airport"}); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = OPMQ_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(OPMQ_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Pamplona South"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + lang_iter = linguistics.find(1); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Airport"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(RouteWithStreetnameAndSign_en_USMultiWithNameSlash, CheckBackwardNames) { + + auto result = gurka::do_action(valhalla::Options::route, the_map, {"Q", "O"}, "auto"); + gurka::assert::raw::expect_path(result, {"Pamplona South/Airport"}); + + GraphReader graph_reader(the_map.config.get_child("mjolnir")); + + GraphId OPMQ_edge_id; + const DirectedEdge* OPMQ_edge = nullptr; + GraphId QMPO_edge_id; + const DirectedEdge* QMPO_edge = nullptr; + std::tie(OPMQ_edge_id, OPMQ_edge, QMPO_edge_id, QMPO_edge) = + findEdge(graph_reader, the_map.nodes, "", "Q", baldr::GraphId{}, 103); + EXPECT_NE(OPMQ_edge, nullptr); + EXPECT_NE(QMPO_edge, nullptr); + + GraphId node_id = QMPO_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(QMPO_edge); + std::vector types; + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 2); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + std::unordered_map>::const_iterator lang_iter = + linguistics.find(0); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(0)), "Pamplona South"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); + + lang_iter = linguistics.find(1); + ASSERT_NE(lang_iter, linguistics.end()); + ASSERT_EQ(std::get<0>(names_and_types.at(1)), "Airport"); + ASSERT_EQ(to_string( + static_cast(std::get(lang_iter->second))), + "en"); + EXPECT_EQ(static_cast(std::get(lang_iter->second)), + static_cast(PronunciationAlphabet::kNone)); + EXPECT_EQ(std::get(lang_iter->second), ""); +} diff --git a/test/gurka/test_locate.cc b/test/gurka/test_locate.cc index 2b4164052a..e97206c788 100644 --- a/test/gurka/test_locate.cc +++ b/test/gurka/test_locate.cc @@ -18,12 +18,18 @@ TEST(locate, basic_properties) { G-a--H--b-I)"; const gurka::ways ways = { - {"AB", {{"highway", "motorway"}}}, {"BC", {{"highway", "primary"}}}, - {"AD", {{"highway", "residential"}}}, {"BE", {{"highway", "motorway_link"}}}, - {"CF", {{"highway", "pedestrian"}}}, {"DE", {{"highway", "trunk"}}}, - {"EF", {{"highway", "secondary"}}}, {"DG", {{"highway", "trunk_link"}}}, - {"EH", {{"highway", "cycleway"}}}, {"FI", {{"highway", "service"}}}, - {"GH", {{"highway", "tertiary"}}}, {"HI", {{"highway", "unclassified"}}}, + {"AB", {{"highway", "motorway"}, {"maxweight", "3.5"}}}, + {"BC", {{"highway", "primary"}, {"maxheight", "4.8"}}}, + {"AD", {{"highway", "residential"}, {"maxaxles", "3"}}}, + {"BE", {{"highway", "motorway_link"}, {"hgv:conditional", "no @ 23:00-05:00"}}}, + {"CF", {{"highway", "pedestrian"}}}, + {"DE", {{"highway", "trunk"}}}, + {"EF", {{"highway", "secondary"}}}, + {"DG", {{"highway", "trunk_link"}}}, + {"EH", {{"highway", "cycleway"}}}, + {"FI", {{"highway", "service"}}}, + {"GH", {{"highway", "tertiary"}}}, + {"HI", {{"highway", "unclassified"}}}, }; const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); @@ -116,6 +122,11 @@ TEST(locate, basic_properties) { // make sure the heading was determined correctly auto heading = static_cast(rapidjson::Pointer("/heading").Get(edge)->GetDouble()); ASSERT_TRUE(std::count(allowed_headings.begin(), allowed_headings.end(), heading)); + + // are there access restrictions + if (ways.find(way_name)->second.size() == 2) { + ASSERT_EQ(rapidjson::Pointer("/access_restrictions").Get(edge)->GetArray().Size(), 1); + } } } diff --git a/test/gurka/test_match.cc b/test/gurka/test_match.cc index 2d4ccabcac..3be216b114 100644 --- a/test/gurka/test_match.cc +++ b/test/gurka/test_match.cc @@ -118,7 +118,7 @@ D--3--4--C--5--6--E)"; map.nodes["2"], map.nodes["B"], map.nodes["C"], map.nodes["6"], map.nodes["C"], map.nodes["3"], }; EXPECT_EQ(shape.size(), expected_shape.size()); - for (int i = 0; i < shape.size(); ++i) { + for (size_t i = 0; i < shape.size(); ++i) { EXPECT_TRUE(shape[i].ApproximatelyEqual(expected_shape[i])); } } @@ -157,7 +157,7 @@ TEST(MapMatch, NodeSnapFix) { auto expected_shape = decltype(shape){map.nodes["B"], map.nodes["C"], map.nodes["F"]}; EXPECT_EQ(shape.size(), expected_shape.size()); - for (int i = 0; i < shape.size(); ++i) { + for (size_t i = 0; i < shape.size(); ++i) { EXPECT_TRUE(shape[i].ApproximatelyEqual(expected_shape[i])); } } @@ -251,7 +251,7 @@ uint32_t TrafficBasedTest::current = 0, TrafficBasedTest::historical = 0, TrafficBasedTest::constrained = 0, TrafficBasedTest::freeflow = 0; uint32_t speed_from_edge(const valhalla::Api& api, bool compare_with_previous_edge = true) { - uint32_t kmh = -1; + uint32_t kmh = invalid(); const auto& nodes = api.trip().routes(0).legs(0).node(); for (int i = 0; i < nodes.size() - 1; ++i) { const auto& node = nodes.Get(i); @@ -262,8 +262,9 @@ uint32_t speed_from_edge(const valhalla::Api& api, bool compare_with_previous_ed node.cost().elapsed_cost().seconds() - node.cost().transition_cost().seconds()) / 3600.0; auto new_kmh = static_cast(km / h + .5); - if (kmh != -1 && compare_with_previous_edge) + if (is_valid(kmh) && compare_with_previous_edge) { EXPECT_EQ(kmh, new_kmh); + } kmh = new_kmh; } return kmh; diff --git a/test/gurka/test_matrix.cc b/test/gurka/test_matrix.cc new file mode 100644 index 0000000000..5af0406d6c --- /dev/null +++ b/test/gurka/test_matrix.cc @@ -0,0 +1,918 @@ +#include "gurka.h" +#include "test.h" +#include +#include + +#include + +using namespace valhalla; +using namespace valhalla::thor; +using namespace valhalla::midgard; + +namespace { +void update_traffic_on_edges(baldr::GraphReader& reader, + baldr::TrafficTile& tile, + uint32_t index, + baldr::TrafficSpeed* current, + const std::string& edge_name, + const gurka::map& closure_map, + uint64_t speed) { + for (const auto& node : {edge_name.front(), edge_name.back()}) { + baldr::GraphId tile_id(tile.header->tile_id); + auto edge = + std::get<0>(gurka::findEdge(reader, closure_map.nodes, edge_name, std::string(1, node))); + if (edge.Tile_Base() == tile_id && edge.id() == index) { + current->breakpoint1 = 255; + current->overall_encoded_speed = speed >> 1; + current->encoded_speed1 = speed >> 1; + } + } +} + +void check_matrix(const rapidjson::Document& result, + const std::vector& exp_dists, + bool valid_traffic, + Matrix::Algorithm matrix_type) { + size_t i = 0; + for (const auto& origin_row : result["sources_to_targets"].GetArray()) { + auto origin_td = origin_row.GetArray(); + for (const auto& v : origin_td) { + std::string msg = "Problem at source " + std::to_string(i / origin_td.Size()) + " and target " + + std::to_string(i % origin_td.Size()); + EXPECT_NEAR(v.GetObject()["distance"].GetFloat(), exp_dists[i], 0.01) << msg; + if (valid_traffic) { + EXPECT_TRUE(v.GetObject().HasMember("date_time")) << msg; + EXPECT_TRUE(v.GetObject()["date_time"] != "") << msg; + } + i++; + } + } + const std::string algo = result["algorithm"].GetString(); + const std::string& exp_algo = MatrixAlgoToString(matrix_type); + EXPECT_EQ(algo, exp_algo); +} +} // namespace + +class MatrixTrafficTest : public ::testing::Test { +protected: + static gurka::map map; + + static void SetUpTestSuite() { + constexpr double gridsize = 100; + + // upper ways are motorways, lower are residential + // unless traffic is enabled, matrix should prefer the longer motorways + // provoke shortcuts around the start & end + const std::string ascii_map = R"( + A-----B + 2 | + | | + 1 | + E--F--G--H I--J--K--L + | | + C-----D + )"; + + const gurka::ways ways = {{"HA", {{"highway", "motorway"}}}, + {"AB", {{"highway", "motorway"}}}, + {"BI", {{"highway", "motorway"}}}, + {"HC", {{"highway", "residential"}}}, + {"CD", {{"highway", "residential"}}}, + {"DI", {{"highway", "residential"}}}, + {"EF", {{"highway", "primary"}}}, + {"FG", {{"highway", "motorway"}, {"name", "Left Street"}}}, + {"GH", {{"highway", "motorway"}, {"name", "Left Street"}}}, + {"IJ", {{"highway", "motorway"}, {"name", "Right Street"}}}, + {"JK", {{"highway", "motorway"}, {"name", "Right Street"}}}, + {"KL", {{"highway", "primary"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + // also turn on the reverse connection search; there's no real test for it + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/matrix_traffic_allowed", + {{"service_limits.max_timedep_distance_matrix", "50000"}, + {"mjolnir.traffic_extract", + "test/data/matrix_traffic_allowed/traffic.tar"}, + {"mjolnir.timezone", "test/data/tz.sqlite"}, + {"thor.costmatrix_check_reverse_connection", "1"}}); + + // verify shortcut edges being built + // TODO: need to hack HierarchyLimits to allow shortcuts being seen by the algo + baldr::GraphReader graph_reader(map.config.get_child("mjolnir")); + auto shortcut_edge_rev = std::get<1>(gurka::findEdgeByNodes(graph_reader, layout, "I", "K")); + auto shortcut_edge_fwd = std::get<1>(gurka::findEdgeByNodes(graph_reader, layout, "F", "H")); + EXPECT_TRUE(shortcut_edge_fwd->is_shortcut()); + EXPECT_TRUE(shortcut_edge_rev->is_shortcut()); + + test::build_live_traffic_data(map.config); + test::LiveTrafficCustomize edges_with_traffic = [](baldr::GraphReader& reader, + baldr::TrafficTile& tile, uint32_t index, + baldr::TrafficSpeed* current) -> void { + // update speeds on primary road + update_traffic_on_edges(reader, tile, index, current, "HA", map, 5); + update_traffic_on_edges(reader, tile, index, current, "AB", map, 5); + update_traffic_on_edges(reader, tile, index, current, "BI", map, 5); + }; + test::customize_live_traffic_data(map.config, edges_with_traffic); + } +}; + +gurka::map MatrixTrafficTest::map = {}; + +TEST_F(MatrixTrafficTest, MatrixNoTraffic) { + // no traffic, so this is CostMatrix + std::string res; + const auto result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E", "L"}, + "auto", {}, {}, &res); + + rapidjson::Document res_doc; + res_doc.Parse(res.c_str()); + + // we expect to take the motorways, residential path is 2.8f + check_matrix(res_doc, {0.0f, 3.2f, 3.2f, 0.0f}, false, Matrix::CostMatrix); +} + +TEST_F(MatrixTrafficTest, TDMatrixWithLiveTraffic) { + std::unordered_map options = {{"/date_time/type", "0"}, + {"/costing_options/auto/speed_types/0", + "current"}}; + + // forward tree + std::string res; + auto result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E", "L"}, "auto", + options, nullptr, &res); + rapidjson::Document res_doc; + res_doc.Parse(res.c_str()); + check_matrix(res_doc, {0.0f, 2.8f, 2.8f, 0.0f}, true, Matrix::TimeDistanceMatrix); + ASSERT_EQ(result.info().warnings().size(), 0); + + // forward tree, date_time on the locations, 2nd location has pointless date_time + options = {{"/sources/0/date_time", "current"}, + {"/sources/1/date_time", "2016-07-03T08:06"}, + {"/costing_options/auto/speed_types/0", "current"}}; + res.erase(); + result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E", "L"}, "auto", options, + nullptr, &res); + res_doc.Parse(res.c_str()); + // the second origin can't respect time (no historical data) + check_matrix(res_doc, {0.0f, 2.8f, 3.2f, 0.0f}, false, Matrix::TimeDistanceMatrix); + ASSERT_EQ(result.info().warnings().size(), 0); + + // TODO: there's still a bug in CostMatrix which chooses the wrong correlated edges: + // https://github.com/valhalla/valhalla/issues/3803 + // this should really take the longer route since it's not using traffic here for TDMatrix + res.erase(); + result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"L"}, "auto", options, + nullptr, &res); + res_doc.Parse(res.c_str()); + check_matrix(res_doc, {2.8f, 0.0f}, false, Matrix::CostMatrix); + ASSERT_EQ(result.info().warnings().size(), 1); + ASSERT_EQ(result.info().warnings(0).code(), 201); + + // forward tree, source & target within a single edge + options = {{"/sources/0/date_time", "current"}, {"/costing_options/auto/speed_types/0", "current"}}; + res.erase(); + result = gurka::do_action(Options::sources_to_targets, map, {"1"}, {"1", "2"}, "auto", options, + nullptr, &res); + res_doc.Parse(res.c_str()); + check_matrix(res_doc, {0.0f, 0.2f}, true, Matrix::TimeDistanceMatrix); + ASSERT_EQ(result.info().warnings().size(), 0); +} + +TEST_F(MatrixTrafficTest, CostMatrixWithLiveTraffic) { + std::unordered_map options = {{"/date_time/type", "0"}, + {"/costing_options/auto/speed_types/0", + "current"}, + {"/prioritize_bidirectional", "1"}}; + + // forward tree + std::string res; + auto result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E", "L"}, "auto", + options, nullptr, &res); + rapidjson::Document res_doc; + res_doc.Parse(res.c_str()); + check_matrix(res_doc, {0.0f, 2.8f, 2.8f, 0.0f}, true, Matrix::CostMatrix); + ASSERT_EQ(result.info().warnings().size(), 0); + res.erase(); + + // forward tree, date_time on the locations, 2nd location has pointless date_time + options = {{"/sources/0/date_time", "current"}, + {"/sources/1/date_time", "2016-07-03T08:06"}, + {"/costing_options/auto/speed_types/0", "current"}, + {"/prioritize_bidirectional", "1"}}; + res.erase(); + result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E", "L"}, "auto", options, + nullptr, &res); + res_doc.Parse(res.c_str()); + // the second origin can't respect time (no historical data) + check_matrix(res_doc, {0.0f, 2.8f, 3.2f, 0.0f}, false, Matrix::CostMatrix); + ASSERT_EQ(result.info().warnings().size(), 0); + + // forward tree, source & target within a single edge + options = {{"/sources/0/date_time", "current"}, + {"/costing_options/auto/speed_types/0", "current"}, + {"/prioritize_bidirectional", "1"}}; + res.erase(); + result = gurka::do_action(Options::sources_to_targets, map, {"1"}, {"1", "2"}, "auto", options, + nullptr, &res); + res_doc.Parse(res.c_str()); + check_matrix(res_doc, {0.0f, 0.2f}, true, Matrix::CostMatrix); + ASSERT_EQ(result.info().warnings().size(), 0); + + // bidir matrix allows less targets than sources and date_time on the sources + options = {{"/sources/0/date_time", "2016-07-03T08:06"}, + {"/sources/1/date_time", "current"}, + {"/costing_options/auto/speed_types/0", "current"}, + {"/prioritize_bidirectional", "1"}}; + res.erase(); + result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E"}, "auto", options, + nullptr, &res); + res_doc.Parse(res.c_str()); + check_matrix(res_doc, {0.0f, 2.8f}, true, Matrix::CostMatrix); + ASSERT_EQ(result.info().warnings().size(), 0); + + // we don't support date_time on the targets + options = {{"/targets/0/date_time", "2016-07-03T08:06"}, + {"/costing_options/auto/speed_types/0", "current"}, + {"/prioritize_bidirectional", "1"}}; + res.erase(); + result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E"}, "auto", options, + nullptr, &res); + res_doc.Parse(res.c_str()); + // TODO: there's still a bug in CostMatrix which chooses the wrong correlated edges: + // https://github.com/valhalla/valhalla/issues/3803 + // this should really take the longer route since it's not using traffic here + check_matrix(res_doc, {0.0f, 2.8f}, false, Matrix::CostMatrix); + ASSERT_EQ(result.info().warnings().size(), 1); + ASSERT_EQ(result.info().warnings(0).code(), 206); +} + +TEST_F(MatrixTrafficTest, DisallowedRequest) { + map.config.put("service_limits.max_timedep_distance_matrix", "0"); + const std::unordered_map options = {{"/date_time/type", "0"}}; + const auto result = + gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E", "L"}, "auto", options); + + ASSERT_EQ(result.info().warnings().size(), 0); + for (auto& loc : result.options().sources()) { + ASSERT_TRUE(loc.date_time().empty()); + } + for (auto& loc : result.options().targets()) { + ASSERT_TRUE(loc.date_time().empty()); + } + + // revert for other tests + map.config.put("service_limits.max_timedep_distance_matrix", "50000"); +} + +TEST_F(MatrixTrafficTest, TDSources) { + // more sources than targets and arrive_by should work + rapidjson::Document res_doc; + std::string res; + std::unordered_map options = {{"/date_time/type", "2"}, + {"/date_time/value", "2016-07-03T08:06"}}; + auto result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E"}, "auto", options, + nullptr, &res); + res_doc.Parse(res.c_str()); + check_matrix(res_doc, {0.0f, 3.2f}, true, Matrix::TimeDistanceMatrix); + ASSERT_EQ(result.info().warnings().size(), 0); + + // more targets than sources with date_time.type = 2 are disallowed + options = {{"/date_time/type", "2"}, {"/date_time/value", "2016-07-03T08:06"}}; + res.erase(); + result = gurka::do_action(Options::sources_to_targets, map, {"E"}, {"E", "L"}, "auto", options, + nullptr, &res); + res_doc.Parse(res.c_str()); + check_matrix(res_doc, {0.0f, 3.2f}, false, Matrix::CostMatrix); + ASSERT_EQ(result.info().warnings().Get(0).code(), 202); + ASSERT_EQ(result.info().warnings().size(), 1); + + // date_time on the sources, disallowed reverse + options = {{"/sources/0/date_time", "current"}, + {"/sources/1/date_time", "2016-07-03T08:06"}, + {"/costing_options/auto/speed_types/0", "current"}}; + res.erase(); + result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E"}, "auto", options, + nullptr, &res); + res_doc.Parse(res.c_str()); + check_matrix(res_doc, {0.0f, 3.2f}, false, Matrix::CostMatrix); + ASSERT_EQ(result.info().warnings().Get(0).code(), 201); + ASSERT_EQ(result.info().warnings().size(), 1); +} + +TEST_F(MatrixTrafficTest, TDTargets) { + // more targets than sources are allowed + rapidjson::Document res_doc; + std::string res; + std::unordered_map options = {{"/date_time/type", "0"}, + {"/date_time/value", "current"}}; + auto result = gurka::do_action(Options::sources_to_targets, map, {"E"}, {"E", "L"}, "auto", options, + nullptr, &res); + res_doc.Parse(res.c_str()); + check_matrix(res_doc, {0.0f, 2.8f}, true, Matrix::TimeDistanceMatrix); + ASSERT_EQ(result.info().warnings().size(), 0); + + // more sources than targets with date_time.type = 1 are disallowed + options = {{"/date_time/type", "1"}, {"/date_time/value", "2016-07-03T08:06"}}; + res.erase(); + result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E"}, "auto", options, + nullptr, &res); + res_doc.Parse(res.c_str()); + check_matrix(res_doc, {0.0f, 3.2f}, false, Matrix::CostMatrix); + ASSERT_EQ(result.info().warnings().size(), 1); + ASSERT_EQ(result.info().warnings().Get(0).code(), 201); + + // date_time on the targets, disallowed forward + options = {{"/targets/0/date_time", "current"}, {"/targets/1/date_time", "2016-07-03T08:06"}}; + res.erase(); + result = gurka::do_action(Options::sources_to_targets, map, {"E"}, {"E", "L"}, "auto", options, + nullptr, &res); + res_doc.Parse(res.c_str()); + check_matrix(res_doc, {0.0f, 3.2f}, false, Matrix::CostMatrix); + ASSERT_EQ(result.info().warnings().size(), 1); + ASSERT_EQ(result.info().warnings().Get(0).code(), 202); +} + +TEST(StandAlone, CostMatrixDeadends) { + // ABI has a turn restriction + // F is a blocking node + const std::string ascii_map = R"( + I + | + A--B--C + | | + | D + E + 1| + | + F--H + . + G + + )"; + // clang-format off + const gurka::ways ways = { + {"AB", {{"highway", "residential"}, {"oneway", "yes"}}}, + {"BC", {{"highway", "residential"}}}, + {"CD", {{"highway", "residential"}}}, + {"BE", {{"highway", "residential"}}}, + {"EF", {{"highway", "residential"}}}, + {"FH", {{"highway", "residential"}}}, + {"FG", {{"highway", "residential"}}}, + {"BI", {{"highway", "residential"}}} + }; + // clang-format on + const gurka::nodes nodes = {{"F", {{"barrier", "block"}}}}; + const gurka::relations relations = { + {{ + {gurka::way_member, "AB", "from"}, + {gurka::node_member, "B", "via"}, + {gurka::way_member, "BI", "to"}, + }, + { + {"type", "restriction"}, + {"restriction", "no_left_turn"}, + }}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); + + auto map = gurka::buildtiles(layout, ways, nodes, relations, + VALHALLA_BUILD_DIR "test/data/costmatrix_deadends"); + + rapidjson::Document res_doc; + std::string res; + + // test that the we're taking the u-turn at D to get from A -> I + // because of the ABI turn restriction + { + auto result = gurka::do_action(valhalla::Options::sources_to_targets, map, {"A"}, {"I"}, "auto", + {}, nullptr, &res); + res_doc.Parse(res.c_str()); + check_matrix(res_doc, {1.5f}, false, Matrix::CostMatrix); + res.erase(); + } + + // then we force to go 1 -> F to hit a blocking node, doing a u-turn and go back the same way + { + auto result = gurka::do_action(valhalla::Options::sources_to_targets, map, {"1"}, {"B"}, "auto", + {{"/sources/0/preferred_side", "opposite"}}, nullptr, &res); + res_doc.Parse(res.c_str()); + check_matrix(res_doc, {0.8f}, false, Matrix::CostMatrix); + } + + // throw if no connection can be found at all + try { + auto result = gurka::do_action(valhalla::Options::sources_to_targets, map, {"C"}, {"A"}, "auto"); + FAIL() << "No connection found should have thrown"; + } catch (const valhalla_exception_t& e) { EXPECT_EQ(e.code, 442); } +} + +TEST(StandAlone, CostMatrixShapes) { + // keep the same order in the map.nodes for encoding easily + const std::string ascii_map = R"( + A-B-C-D-E-F-G-H-I-J-K-------L + )"; + // clang-format off + const gurka::ways ways = { + {"ABCDE", {{"highway", "residential"}}}, + {"EFGHIJK", {{"highway", "residential"}}}, + {"KL", {{"highway", "residential"}}}, + }; + // clang-format on + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); + + auto map = + gurka::buildtiles(layout, ways, {}, {}, VALHALLA_BUILD_DIR "test/data/costmatrix_shapes"); + + // points of all nodes + std::vector vertices; + for (const auto& node : map.nodes) { + vertices.emplace_back(node.second); + } + + std::string res; + rapidjson::Document res_doc; + + // no shapes if not specified or "none" + auto result = gurka::do_action(valhalla::Options::sources_to_targets, map, {"A"}, {"L"}, "auto", {}, + nullptr, &res); + EXPECT_EQ(result.matrix().shapes(0), ""); + EXPECT_FALSE(res_doc.Parse(res.c_str())["sources_to_targets"] + .GetArray()[0] + .GetArray()[0] + .GetObject() + .HasMember("shape")); + res.erase(); + + std::unordered_map options = {{"/shape_format", "no_shape"}}; + + result = gurka::do_action(valhalla::Options::sources_to_targets, map, {"A"}, {"L"}, "auto", options, + nullptr, &res); + EXPECT_EQ(result.matrix().shapes(0), ""); + EXPECT_FALSE(res_doc.Parse(res.c_str())["sources_to_targets"] + .GetArray()[0] + .GetArray()[0] + .GetObject() + .HasMember("shape")); + res.erase(); + + // polyline5/6 + + options["/shape_format"] = "polyline5"; + auto encoded = encode>(vertices, 1e5); + result = gurka::do_action(valhalla::Options::sources_to_targets, map, {"A"}, {"L"}, "auto", options, + nullptr, &res); + EXPECT_EQ(result.matrix().shapes(0), encoded); + EXPECT_EQ(res_doc.Parse(res.c_str())["sources_to_targets"] + .GetArray()[0] + .GetArray()[0] + .GetObject()["shape"], + encoded); + res.erase(); + + options["/shape_format"] = "polyline6"; + encoded = encode>(vertices, 1e6); + result = gurka::do_action(valhalla::Options::sources_to_targets, map, {"A"}, {"L"}, "auto", options, + nullptr, &res); + EXPECT_EQ(result.matrix().shapes(0), encoded); + EXPECT_EQ(res_doc.Parse(res.c_str())["sources_to_targets"] + .GetArray()[0] + .GetArray()[0] + .GetObject()["shape"], + encoded); + res.erase(); + + // geojson + + options["/shape_format"] = "geojson"; + result = gurka::do_action(valhalla::Options::sources_to_targets, map, {"A"}, {"L"}, "auto", options, + nullptr, &res); + EXPECT_EQ(result.matrix().shapes(0), encoded); // has the encoded polyline6 in PBF + const auto& gj_shp = res_doc.Parse(res.c_str())["sources_to_targets"] + .GetArray()[0] + .GetArray()[0] + .GetObject()["shape"]; + EXPECT_TRUE(gj_shp.IsObject()); + EXPECT_EQ(gj_shp["coordinates"].GetArray().Size(), 12); + EXPECT_EQ(gj_shp["type"], "LineString"); + res.erase(); + + // trivial route + // has a bug: https://github.com/valhalla/valhalla/issues/4433, but it's band-aided for now + // floating point crap makes this fail though, it adds a tiny little bit on both ends, resulting in + // 4 (not 2) points + + options["/shape_format"] = "polyline6"; + encoded = encode>({map.nodes["G"], map.nodes["H"]}); + result = gurka::do_action(valhalla::Options::sources_to_targets, map, {"G"}, {"H"}, "auto", options, + nullptr, &res); + /* + EXPECT_EQ(result.matrix().shapes(0), encoded); + EXPECT_EQ(res_doc.Parse(res.c_str())["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject()["shape"], + encoded); + */ + res.erase(); + + // trivial route reverse + // has a bug: https://github.com/valhalla/valhalla/issues/4433, but it's band-aided for now + + options["/shape_format"] = "polyline6"; + encoded = encode>({map.nodes["H"], map.nodes["G"]}); + result = gurka::do_action(valhalla::Options::sources_to_targets, map, {"H"}, {"G"}, "auto", options, + nullptr, &res); + /* + EXPECT_EQ(result.matrix().shapes(0), encoded); + EXPECT_EQ(res_doc.Parse(res.c_str())["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject()["shape"], + encoded); + */ + res.erase(); + + // timedistancematrix + + options["/shape_format"] = "geojson"; + result = gurka::do_action(valhalla::Options::sources_to_targets, map, {"A"}, {"L"}, "pedestrian", + options, nullptr, &res); + EXPECT_FALSE(res_doc.Parse(res.c_str())["sources_to_targets"] + .GetArray()[0] + .GetArray()[0] + .GetObject() + .HasMember("shape")); + EXPECT_EQ(result.info().warnings().size(), 1); + EXPECT_EQ(result.info().warnings(0).code(), 207); + EXPECT_EQ(res_doc.Parse(res.c_str())["warnings"].GetArray().Size(), 1); + EXPECT_EQ(res_doc.Parse(res.c_str())["warnings"].GetArray()[0].GetObject()["code"].GetUint64(), + 207); + + res.erase(); +} + +class DateTimeTest : public ::testing::Test { +protected: + // check both with and without time zones present + static gurka::map map; + static gurka::map map_tz; + + static void SetUpTestSuite() { + constexpr double gridsize = 1500; + + // ~ are approximate time zone crossings + const std::string ascii_map = R"( + A----------B + | | + C D + | | + ~ ~ + | | + | | + E F + | | + G----------H + )"; + + const gurka::ways ways = {{"AC", {{"highway", "residential"}}}, + {"CE", {{"highway", "residential"}}}, + {"EG", {{"highway", "residential"}}}, + {"GH", {{"highway", "residential"}}}, + {"HF", {{"highway", "residential"}}}, + {"FD", {{"highway", "residential"}}}, + {"DB", {{"highway", "residential"}}}, + {"BA", {{"highway", "residential"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {-8.5755, 42.1079}); + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/time_zone_matrix_no_tz"); + map_tz = gurka::buildtiles(layout, ways, {}, {}, "test/data/time_zone_matrix", + {{"mjolnir.timezone", "test/data/tz.sqlite"}, + {"service_limits.max_timedep_distance_matrix", "50000"}}); + } +}; +gurka::map DateTimeTest::map = {}; +gurka::map DateTimeTest::map_tz = {}; + +TEST_F(DateTimeTest, DepartAtCostMatrix) { + rapidjson::Document res_doc; + std::string res; + { + auto api = gurka::do_action(valhalla::Options::sources_to_targets, map_tz, {"A", "G"}, {"A", "G"}, + "auto", + {{"/prioritize_bidirectional", "1"}, + {"/date_time/type", "1"}, + {"/date_time/value", "2020-10-30T09:00"}}, + nullptr, &res); + + res_doc.Parse(res.c_str()); + + // sanity check + EXPECT_EQ(api.matrix().algorithm(), Matrix::CostMatrix); + + EXPECT_TRUE( + res_doc["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject().HasMember("date_time")); + + EXPECT_EQ(res_doc["sources_to_targets"] + .GetArray()[0] + .GetArray()[0] + .GetObject()["time_zone_offset"], + "+01:00"); + + EXPECT_EQ(res_doc["sources_to_targets"] + .GetArray()[0] + .GetArray()[1] + .GetObject()["time_zone_offset"], + "+00:00"); + + EXPECT_EQ(res_doc["sources_to_targets"] + .GetArray()[1] + .GetArray()[0] + .GetObject()["time_zone_offset"], + "+01:00"); + + EXPECT_EQ(res_doc["sources_to_targets"] + .GetArray()[1] + .GetArray()[1] + .GetObject()["time_zone_offset"], + "+00:00"); + + EXPECT_EQ(res_doc["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject()["time_zone_name"], + "Europe/Madrid"); + + EXPECT_EQ(res_doc["sources_to_targets"].GetArray()[0].GetArray()[1].GetObject()["time_zone_name"], + "Europe/Lisbon"); + + EXPECT_EQ(res_doc["sources_to_targets"].GetArray()[1].GetArray()[0].GetObject()["time_zone_name"], + "Europe/Madrid"); + + EXPECT_EQ(res_doc["sources_to_targets"].GetArray()[1].GetArray()[1].GetObject()["time_zone_name"], + "Europe/Lisbon"); + } +} + +TEST_F(DateTimeTest, DepartAtTimeDistanceMatrix) { + rapidjson::Document res_doc; + std::string res; + { + auto api = gurka::do_action(valhalla::Options::sources_to_targets, map_tz, {"A", "G"}, {"A", "G"}, + "bicycle", + {{"/date_time/type", "1"}, {"/date_time/value", "2020-10-30T09:00"}}, + nullptr, &res); + + res_doc.Parse(res.c_str()); + + // sanity check + EXPECT_EQ(api.matrix().algorithm(), Matrix::TimeDistanceMatrix); + + EXPECT_TRUE( + res_doc["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject().HasMember("date_time")); + + EXPECT_EQ(res_doc["sources_to_targets"] + .GetArray()[0] + .GetArray()[0] + .GetObject()["time_zone_offset"], + "+01:00"); + + EXPECT_EQ(res_doc["sources_to_targets"] + .GetArray()[0] + .GetArray()[1] + .GetObject()["time_zone_offset"], + "+00:00"); + + EXPECT_EQ(res_doc["sources_to_targets"] + .GetArray()[1] + .GetArray()[0] + .GetObject()["time_zone_offset"], + "+01:00"); + + EXPECT_EQ(res_doc["sources_to_targets"] + .GetArray()[1] + .GetArray()[1] + .GetObject()["time_zone_offset"], + "+00:00"); + + EXPECT_EQ(res_doc["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject()["time_zone_name"], + "Europe/Madrid"); + + EXPECT_EQ(res_doc["sources_to_targets"].GetArray()[0].GetArray()[1].GetObject()["time_zone_name"], + "Europe/Lisbon"); + + EXPECT_EQ(res_doc["sources_to_targets"].GetArray()[1].GetArray()[0].GetObject()["time_zone_name"], + "Europe/Madrid"); + + EXPECT_EQ(res_doc["sources_to_targets"].GetArray()[1].GetArray()[1].GetObject()["time_zone_name"], + "Europe/Lisbon"); + } +} + +TEST_F(DateTimeTest, NoTimeZone) { + rapidjson::Document res_doc; + std::string res; + { + auto api = + gurka::do_action(valhalla::Options::sources_to_targets, map, {"A", "G"}, {"A", "G"}, "auto", + {{"/prioritize_bidirectional", "true"}, + {"/date_time/type", "1"}, + {"/date_time/value", "2020-10-30T09:00"}}, + nullptr, &res); + res_doc.Parse(res.c_str()); + + EXPECT_FALSE( + res_doc["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject().HasMember("date_time")); + + EXPECT_FALSE(res_doc["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject().HasMember( + "time_zone_offset")); + + EXPECT_FALSE(res_doc["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject().HasMember( + "time_zone_name")); + } +} + +TEST(StandAlone, MatrixSecondPass) { + // from no-thru to no-thru should trigger a second pass + // JL has a forward destination-only, + // so K -> I also triggers second pass (see oneway at HK), but I -> K doesn't (no oneway) + const std::string ascii_map = R"( + A---B I---J + | | | | + | E---F---G---H | + | | ↓ | + C---D K---L + )"; + + gurka::ways ways; + for (const auto& node_pair : + {"AB", "BE", "AC", "CD", "DE", "EF", "FG", "GH", "HI", "IJ", "JL", "HK", "KL"}) { + ways[node_pair] = {{"highway", "residential"}}; + } + ways["JL"].emplace("motor_vehicle", "destination"); + ways["HK"].emplace("oneway", "true"); + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 50); + const auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/matrix_second_pass", + {{"thor.costmatrix_allow_second_pass", "1"}}); + baldr::GraphReader graph_reader(map.config.get_child("mjolnir")); + + // Make sure the relevant edges are actually built as no-thru + auto FE_edge = std::get<1>(gurka::findEdgeByNodes(graph_reader, layout, "F", "E")); + auto GH_edge = std::get<1>(gurka::findEdgeByNodes(graph_reader, layout, "G", "H")); + auto JL_edge = std::get<1>(gurka::findEdgeByNodes(graph_reader, layout, "J", "L")); + EXPECT_TRUE(FE_edge->not_thru()); + EXPECT_TRUE(GH_edge->not_thru()); + EXPECT_TRUE(JL_edge->destonly()); + + // Simple single route from no-thru to no-thru + { + auto api = gurka::do_action(valhalla::Options::sources_to_targets, map, {"A"}, {"J"}, "auto"); + EXPECT_GT(api.matrix().times(0), 0.f); + EXPECT_TRUE(api.matrix().second_pass(0)); + EXPECT_TRUE(api.info().warnings(0).description().find('0') != std::string::npos); + } + + // I -> K (idx 1) should pass on the first try + // K -> I (idx 2) should need a second pass + { + auto api = + gurka::do_action(valhalla::Options::sources_to_targets, map, {"I", "K"}, {"I", "K"}, "auto"); + EXPECT_GT(api.matrix().times(1), 0.f); + EXPECT_FALSE(api.matrix().second_pass(1)); + EXPECT_GT(api.matrix().times(2), 0.f); + EXPECT_TRUE(api.matrix().second_pass(2)); + EXPECT_GT(api.matrix().distances(2), api.matrix().distances(1)); + EXPECT_GT(api.matrix().times(2), api.matrix().times(1)); + + // I -> I & K -> K shouldn't be processed a second time either + EXPECT_FALSE(api.matrix().second_pass(0)); + EXPECT_FALSE(api.matrix().second_pass(3)); + EXPECT_TRUE(api.info().warnings(0).description().find('2') != std::string::npos); + } +} + +TEST(StandAlone, CostMatrixTrivialRoutes) { + const std::string ascii_map = R"( + A---B--2->-1--C---D + | | + 6 5 + | | + E--3---4--F + )"; + const gurka::ways ways = { + {"AB", {{"highway", "residential"}}}, {"BC", {{"highway", "residential"}, {"oneway", "yes"}}}, + {"CD", {{"highway", "residential"}}}, {"BE", {{"highway", "residential"}}}, + {"EF", {{"highway", "residential"}}}, {"FC", {{"highway", "residential"}}}, + }; + auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); + auto map = + gurka::buildtiles(layout, ways, {}, {}, VALHALLA_BUILD_DIR "test/data/costmatrix_trivial"); + + std::unordered_map options = {{"/shape_format", "polyline6"}}; + + // test the against-oneway case + { + auto matrix = + gurka::do_action(valhalla::Options::sources_to_targets, map, {"1"}, {"2"}, "auto", options); + EXPECT_EQ(matrix.matrix().distances(0), 2400); + + std::vector oneway_vertices; + for (auto& node : {"1", "C", "F", "E", "B", "2"}) { + oneway_vertices.push_back(layout[node]); + } + auto encoded = encode>(oneway_vertices, 1e6); + EXPECT_EQ(matrix.matrix().shapes(0), encoded); + } + + // test the oneway case + { + auto matrix = + gurka::do_action(valhalla::Options::sources_to_targets, map, {"2"}, {"1"}, "auto", options); + EXPECT_EQ(matrix.matrix().distances(0), 400); + + auto encoded = encode>({layout["2"], layout["1"]}, 1e6); + EXPECT_EQ(matrix.matrix().shapes(0), encoded); + } + + // test the normal trivial case + { + auto matrix = + gurka::do_action(valhalla::Options::sources_to_targets, map, {"3"}, {"4"}, "auto", options); + EXPECT_EQ(matrix.matrix().distances(0), 400); + + auto encoded = encode>({layout["3"], layout["4"]}, 1e6); + EXPECT_EQ(matrix.matrix().shapes(0), encoded); + } + + // test trivial case via connecting edge + { + auto matrix = + gurka::do_action(valhalla::Options::sources_to_targets, map, {"4"}, {"5"}, "auto", options); + EXPECT_EQ(matrix.matrix().distances(0), 500); + + auto encoded = encode>({layout["4"], layout["F"], layout["5"]}, 1e6); + EXPECT_EQ(matrix.matrix().shapes(0), encoded); + } +} + +TEST(StandAlone, HGVNoAccessPenalty) { + // if hgv_no_penalty is on we should still respect the maxweight restriction on CD + // so we should take the next-best hgv=no edge with JK + const std::string ascii_map = R"( + A-1-------B----C----D----E--2-------F + | | + J----K + | | + | | + L----M + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "residential"}, {"hgv", "no"}}}, + {"BC", {{"highway", "residential"}}}, + {"CD", {{"highway", "residential"}, {"hgv", "no"}, {"maxweight", "3.5"}}}, + {"DE", {{"highway", "residential"}}}, + {"EF", {{"highway", "residential"}, {"hgv", "no"}}}, + {"CJ", {{"highway", "residential"}}}, + {"JK", {{"highway", "residential"}, {"hgv", "no"}}}, + {"JLMK", {{"highway", "residential"}}}, + {"KD", {{"highway", "residential"}}}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); + gurka::map map = gurka::buildtiles(layout, ways, {}, {}, "test/data/hgv_no_access_penalty", + {{"service_limits.max_timedep_distance_matrix", "50000"}}); + + std::unordered_map cost_matrix = + {{"/costing_options/truck/hgv_no_access_penalty", "2000"}, + {"/sources/0/date_time", "2024-03-20T09:00"}, + {"/prioritize_bidirectional", "1"}}; + std::unordered_map td_matrix = + {{"/costing_options/truck/hgv_no_access_penalty", "2000"}, + {"/sources/0/date_time", "2024-03-20T09:00"}}; + + // do both costmatrix & timedistancematrix + std::vector> options = {cost_matrix, td_matrix}; + for (auto& truck_options : options) { + + // by default, take the detour via LM + // NOTE, we're not snapping to the hgv=no edges either + { + auto matrix = + gurka::do_action(valhalla::Options::sources_to_targets, map, {"1"}, {"2"}, "truck"); + EXPECT_EQ(matrix.matrix().distances(0), 2500); + } + + // with a high hgv_no_penalty also take the detour via LM, but do snap to the hgv=no edges + { + auto matrix = gurka::do_action(valhalla::Options::sources_to_targets, map, {"1"}, {"2"}, + "truck", truck_options); + // TODO(nils): timedistancematrix seems to have a tiny bug where time options result in slightly + // less distances + EXPECT_NEAR(matrix.matrix().distances(0), 3600, 2); + } + + // with a low hgv_no_penalty take the JK edge + { + truck_options["/costing_options/truck/hgv_no_access_penalty"] = "10"; + auto matrix = gurka::do_action(valhalla::Options::sources_to_targets, map, {"1"}, {"2"}, + "truck", truck_options); + // TODO(nils): timedistancematrix seems to have a tiny bug where time options result in slightly + // less distances + EXPECT_NEAR(matrix.matrix().distances(0), 3000, 2); + } + } +} diff --git a/test/gurka/test_matrix_time_dependent.cc b/test/gurka/test_matrix_time_dependent.cc deleted file mode 100644 index e4b933f7dd..0000000000 --- a/test/gurka/test_matrix_time_dependent.cc +++ /dev/null @@ -1,328 +0,0 @@ -#include "gurka.h" -#include "test.h" -#include - -#include - -using namespace valhalla; -using namespace valhalla::thor; - -namespace { -void update_traffic_on_edges(baldr::GraphReader& reader, - baldr::TrafficTile& tile, - uint32_t index, - baldr::TrafficSpeed* current, - const std::string& edge_name, - const gurka::map& closure_map, - uint64_t speed) { - for (const auto& node : {edge_name.front(), edge_name.back()}) { - baldr::GraphId tile_id(tile.header->tile_id); - auto edge = - std::get<0>(gurka::findEdge(reader, closure_map.nodes, edge_name, std::string(1, node))); - if (edge.Tile_Base() == tile_id && edge.id() == index) { - current->breakpoint1 = 255; - current->overall_encoded_speed = speed >> 1; - current->encoded_speed1 = speed >> 1; - } - } -} - -void check_matrix(const rapidjson::Document& result, - const std::vector& exp_dists, - bool valid_traffic, - MatrixType matrix_type) { - size_t i = 0; - for (const auto& origin_row : result["sources_to_targets"].GetArray()) { - auto origin_td = origin_row.GetArray(); - for (const auto& v : origin_td) { - std::string msg = "Problem at source " + std::to_string(i / origin_td.Size()) + " and target " + - std::to_string(i % origin_td.Size()); - EXPECT_NEAR(v.GetObject()["distance"].GetFloat(), exp_dists[i], 0.01) << msg; - if (valid_traffic) { - EXPECT_TRUE(v.GetObject().HasMember("date_time")) << msg; - EXPECT_TRUE(v.GetObject()["date_time"] != "") << msg; - } - i++; - } - } - const std::string algo = result["algorithm"].GetString(); - const std::string exp_algo = matrix_type == MatrixType::Cost ? "costmatrix" : "timedistancematrix"; - EXPECT_EQ(algo, exp_algo); -} -} // namespace - -class MatrixTest : public ::testing::Test { -protected: - static gurka::map map; - - static void SetUpTestSuite() { - constexpr double gridsize = 100; - - // upper ways are motorways, lower are residential - // unless traffic is enabled, matrix should prefer the longer motorways - // provoke shortcuts around the start & end - const std::string ascii_map = R"( - A-----B - 2 | - | | - 1 | - E--F--G--H I--J--K--L - | | - C-----D - )"; - - const gurka::ways ways = {{"HA", {{"highway", "motorway"}}}, - {"AB", {{"highway", "motorway"}}}, - {"BI", {{"highway", "motorway"}}}, - {"HC", {{"highway", "residential"}}}, - {"CD", {{"highway", "residential"}}}, - {"DI", {{"highway", "residential"}}}, - {"EF", {{"highway", "primary"}}}, - {"FG", {{"highway", "motorway"}, {"name", "Left Street"}}}, - {"GH", {{"highway", "motorway"}, {"name", "Left Street"}}}, - {"IJ", {{"highway", "motorway"}, {"name", "Right Street"}}}, - {"JK", {{"highway", "motorway"}, {"name", "Right Street"}}}, - {"KL", {{"highway", "primary"}}}}; - - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); - map = gurka::buildtiles(layout, ways, {}, {}, "test/data/matrix_traffic_allowed", - {{"service_limits.max_timedep_distance_matrix", "50000"}, - {"mjolnir.traffic_extract", - "test/data/matrix_traffic_allowed/traffic.tar"}, - {"mjolnir.timezone", "test/data/tz.sqlite"}}); - - // verify shortcut edges being built - // TODO: need to hack HierarchyLimits to allow shortcuts being seen by the algo - baldr::GraphReader graph_reader(map.config.get_child("mjolnir")); - auto shortcut_edge_rev = std::get<1>(gurka::findEdgeByNodes(graph_reader, layout, "I", "K")); - auto shortcut_edge_fwd = std::get<1>(gurka::findEdgeByNodes(graph_reader, layout, "F", "H")); - EXPECT_TRUE(shortcut_edge_fwd->is_shortcut()); - EXPECT_TRUE(shortcut_edge_rev->is_shortcut()); - - test::build_live_traffic_data(map.config); - test::LiveTrafficCustomize edges_with_traffic = [](baldr::GraphReader& reader, - baldr::TrafficTile& tile, uint32_t index, - baldr::TrafficSpeed* current) -> void { - // update speeds on primary road - update_traffic_on_edges(reader, tile, index, current, "HA", map, 5); - update_traffic_on_edges(reader, tile, index, current, "AB", map, 5); - update_traffic_on_edges(reader, tile, index, current, "BI", map, 5); - }; - test::customize_live_traffic_data(map.config, edges_with_traffic); - } -}; - -gurka::map MatrixTest::map = {}; - -TEST_F(MatrixTest, MatrixNoTraffic) { - // no traffic, so this is CostMatrix - std::string res; - const auto result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E", "L"}, - "auto", {}, {}, &res); - - rapidjson::Document res_doc; - res_doc.Parse(res.c_str()); - - // we expect to take the motorways, residential path is 2.8f - check_matrix(res_doc, {0.0f, 3.2f, 3.2f, 0.0f}, false, MatrixType::Cost); -} - -TEST_F(MatrixTest, TDMatrixWithLiveTraffic) { - std::unordered_map options = {{"/date_time/type", "0"}, - {"/costing_options/auto/speed_types/0", - "current"}}; - - // forward tree - std::string res; - auto result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E", "L"}, "auto", - options, nullptr, &res); - rapidjson::Document res_doc; - res_doc.Parse(res.c_str()); - check_matrix(res_doc, {0.0f, 2.8f, 2.8f, 0.0f}, true, MatrixType::TimeDist); - ASSERT_EQ(result.info().warnings().size(), 0); - - // forward tree, date_time on the locations, 2nd location has pointless date_time - options = {{"/sources/0/date_time", "current"}, - {"/sources/1/date_time", "2016-07-03T08:06"}, - {"/costing_options/auto/speed_types/0", "current"}}; - res.erase(); - result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E", "L"}, "auto", options, - nullptr, &res); - res_doc.Parse(res.c_str()); - // the second origin can't respect time (no historical data) - check_matrix(res_doc, {0.0f, 2.8f, 3.2f, 0.0f}, false, MatrixType::TimeDist); - ASSERT_EQ(result.info().warnings().size(), 0); - - // TODO: there's still a bug in CostMatrix which chooses the wrong correlated edges: - // https://github.com/valhalla/valhalla/issues/3803 - // this should really take the longer route since it's not using traffic here for TDMatrix - res.erase(); - result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"L"}, "auto", options, - nullptr, &res); - res_doc.Parse(res.c_str()); - check_matrix(res_doc, {2.8f, 0.0f}, false, MatrixType::Cost); - ASSERT_EQ(result.info().warnings().size(), 1); - ASSERT_EQ(result.info().warnings(0).code(), 201); - - // forward tree, source & target within a single edge - options = {{"/sources/0/date_time", "current"}, {"/costing_options/auto/speed_types/0", "current"}}; - res.erase(); - result = gurka::do_action(Options::sources_to_targets, map, {"1"}, {"1", "2"}, "auto", options, - nullptr, &res); - res_doc.Parse(res.c_str()); - check_matrix(res_doc, {0.0f, 0.2f}, true, MatrixType::TimeDist); - ASSERT_EQ(result.info().warnings().size(), 0); -} - -TEST_F(MatrixTest, CostMatrixWithLiveTraffic) { - std::unordered_map options = {{"/date_time/type", "0"}, - {"/costing_options/auto/speed_types/0", - "current"}, - {"/prioritize_bidirectional", "1"}}; - - // forward tree - std::string res; - auto result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E", "L"}, "auto", - options, nullptr, &res); - rapidjson::Document res_doc; - res_doc.Parse(res.c_str()); - check_matrix(res_doc, {0.0f, 2.8f, 2.8f, 0.0f}, true, MatrixType::Cost); - ASSERT_EQ(result.info().warnings().size(), 0); - - // forward tree, date_time on the locations, 2nd location has pointless date_time - options = {{"/sources/0/date_time", "current"}, - {"/sources/1/date_time", "2016-07-03T08:06"}, - {"/costing_options/auto/speed_types/0", "current"}, - {"/prioritize_bidirectional", "1"}}; - res.erase(); - result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E", "L"}, "auto", options, - nullptr, &res); - res_doc.Parse(res.c_str()); - // the second origin can't respect time (no historical data) - check_matrix(res_doc, {0.0f, 2.8f, 3.2f, 0.0f}, false, MatrixType::Cost); - ASSERT_EQ(result.info().warnings().size(), 0); - - // forward tree, source & target within a single edge - options = {{"/sources/0/date_time", "current"}, - {"/costing_options/auto/speed_types/0", "current"}, - {"/prioritize_bidirectional", "1"}}; - res.erase(); - result = gurka::do_action(Options::sources_to_targets, map, {"1"}, {"1", "2"}, "auto", options, - nullptr, &res); - res_doc.Parse(res.c_str()); - check_matrix(res_doc, {0.0f, 0.2f}, true, MatrixType::Cost); - ASSERT_EQ(result.info().warnings().size(), 0); - - // bidir matrix allows less targets than sources and date_time on the sources - options = {{"/sources/0/date_time", "2016-07-03T08:06"}, - {"/sources/1/date_time", "current"}, - {"/costing_options/auto/speed_types/0", "current"}, - {"/prioritize_bidirectional", "1"}}; - res.erase(); - result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E"}, "auto", options, - nullptr, &res); - res_doc.Parse(res.c_str()); - check_matrix(res_doc, {0.0f, 2.8f}, true, MatrixType::Cost); - ASSERT_EQ(result.info().warnings().size(), 0); - - // we don't support date_time on the targets - options = {{"/targets/0/date_time", "2016-07-03T08:06"}, - {"/costing_options/auto/speed_types/0", "current"}, - {"/prioritize_bidirectional", "1"}}; - res.erase(); - result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E"}, "auto", options, - nullptr, &res); - res_doc.Parse(res.c_str()); - // TODO: there's still a bug in CostMatrix which chooses the wrong correlated edges: - // https://github.com/valhalla/valhalla/issues/3803 - // this should really take the longer route since it's not using traffic here - check_matrix(res_doc, {0.0f, 2.8f}, false, MatrixType::Cost); - ASSERT_EQ(result.info().warnings().size(), 1); - ASSERT_EQ(result.info().warnings(0).code(), 206); -} - -TEST_F(MatrixTest, DisallowedRequest) { - map.config.put("service_limits.max_timedep_distance_matrix", "0"); - const std::unordered_map options = {{"/date_time/type", "0"}}; - const auto result = - gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E", "L"}, "auto", options); - - ASSERT_EQ(result.info().warnings().size(), 0); - for (auto& loc : result.options().sources()) { - ASSERT_TRUE(loc.date_time().empty()); - } - for (auto& loc : result.options().targets()) { - ASSERT_TRUE(loc.date_time().empty()); - } - - // revert for other tests - map.config.put("service_limits.max_timedep_distance_matrix", "50000"); -} - -TEST_F(MatrixTest, TDSources) { - // more sources than targets and arrive_by should work - rapidjson::Document res_doc; - std::string res; - std::unordered_map options = {{"/date_time/type", "2"}, - {"/date_time/value", "2016-07-03T08:06"}}; - auto result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E"}, "auto", options, - nullptr, &res); - res_doc.Parse(res.c_str()); - check_matrix(res_doc, {0.0f, 3.2f}, true, MatrixType::TimeDist); - ASSERT_EQ(result.info().warnings().size(), 0); - - // more targets than sources with date_time.type = 2 are disallowed - options = {{"/date_time/type", "2"}, {"/date_time/value", "2016-07-03T08:06"}}; - res.erase(); - result = gurka::do_action(Options::sources_to_targets, map, {"E"}, {"E", "L"}, "auto", options, - nullptr, &res); - res_doc.Parse(res.c_str()); - check_matrix(res_doc, {0.0f, 3.2f}, false, MatrixType::Cost); - ASSERT_EQ(result.info().warnings().Get(0).code(), 202); - ASSERT_EQ(result.info().warnings().size(), 1); - - // date_time on the sources, disallowed reverse - options = {{"/sources/0/date_time", "current"}, - {"/sources/1/date_time", "2016-07-03T08:06"}, - {"/costing_options/auto/speed_types/0", "current"}}; - res.erase(); - result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E"}, "auto", options, - nullptr, &res); - res_doc.Parse(res.c_str()); - check_matrix(res_doc, {0.0f, 3.2f}, false, MatrixType::Cost); - ASSERT_EQ(result.info().warnings().Get(0).code(), 201); - ASSERT_EQ(result.info().warnings().size(), 1); -} - -TEST_F(MatrixTest, TDTargets) { - // more targets than sources are allowed - rapidjson::Document res_doc; - std::string res; - std::unordered_map options = {{"/date_time/type", "0"}, - {"/date_time/value", "current"}}; - auto result = gurka::do_action(Options::sources_to_targets, map, {"E"}, {"E", "L"}, "auto", options, - nullptr, &res); - res_doc.Parse(res.c_str()); - check_matrix(res_doc, {0.0f, 2.8f}, true, MatrixType::TimeDist); - ASSERT_EQ(result.info().warnings().size(), 0); - - // more sources than targets with date_time.type = 1 are disallowed - options = {{"/date_time/type", "1"}, {"/date_time/value", "2016-07-03T08:06"}}; - res.erase(); - result = gurka::do_action(Options::sources_to_targets, map, {"E", "L"}, {"E"}, "auto", options, - nullptr, &res); - res_doc.Parse(res.c_str()); - check_matrix(res_doc, {0.0f, 3.2f}, false, MatrixType::Cost); - ASSERT_EQ(result.info().warnings().size(), 1); - ASSERT_EQ(result.info().warnings().Get(0).code(), 201); - - // date_time on the targets, disallowed forward - options = {{"/targets/0/date_time", "current"}, {"/targets/1/date_time", "2016-07-03T08:06"}}; - res.erase(); - result = gurka::do_action(Options::sources_to_targets, map, {"E"}, {"E", "L"}, "auto", options, - nullptr, &res); - res_doc.Parse(res.c_str()); - check_matrix(res_doc, {0.0f, 3.2f}, false, MatrixType::Cost); - ASSERT_EQ(result.info().warnings().size(), 1); - ASSERT_EQ(result.info().warnings().Get(0).code(), 202); -} diff --git a/test/gurka/test_mtb_access.cc b/test/gurka/test_mtb_access.cc deleted file mode 100644 index ce21d4dd01..0000000000 --- a/test/gurka/test_mtb_access.cc +++ /dev/null @@ -1,44 +0,0 @@ -#include "gurka.h" -#include - -#if !defined(VALHALLA_SOURCE_DIR) -#define VALHALLA_SOURCE_DIR -#endif - -using namespace valhalla; - -// Check that MTB tags override SAC scale and allow bicycle access -class MtbAccess : public ::testing::Test { -protected: - static gurka::map map; - - static void SetUpTestSuite() { - constexpr double gridsize = 100; - - // A--B - const std::string ascii_map = R"(A----B----C)"; - const gurka::ways ways = {{"AB", - {{"highway", "cycleway"}, - {"sac_scale", "mountain_hiking"}, - {"mtb:scale:uphill", "2"}, - {"foot", "designated"}}}, - {"BC", - {{"highway", "cycleway"}, - {"sac_scale", "mountain_hiking"}, - {"mtb:scale:uphill", "2"}, - {"foot", "designated"}}}}; - - const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); - map = gurka::buildtiles(layout, ways, {}, {}, "test/data/mtb_access"); - } -}; - -gurka::map MtbAccess::map = {}; - -/*************************************************************/ - -TEST_F(MtbAccess, CheckMtbAccess) { - auto result = gurka::do_action(valhalla::Options::route, map, {"A", "C"}, "bicycle"); - gurka::assert::osrm::expect_steps(result, {"AB"}); - gurka::assert::raw::expect_path(result, {"AB", "BC"}); -} diff --git a/test/gurka/test_multi_level_loki.cc b/test/gurka/test_multi_level_loki.cc index 14bfcaa888..53f505a984 100644 --- a/test/gurka/test_multi_level_loki.cc +++ b/test/gurka/test_multi_level_loki.cc @@ -1,7 +1,6 @@ #include #include -#include #include #include "baldr/rapidjson_utils.h" diff --git a/test/gurka/test_node_access.cc b/test/gurka/test_node_access.cc deleted file mode 100644 index c62f175b0a..0000000000 --- a/test/gurka/test_node_access.cc +++ /dev/null @@ -1,50 +0,0 @@ -#include "gurka.h" -#include - -using namespace valhalla; - -const std::vector& costing = {"auto", "taxi", "bus", "truck", - "bicycle", "motor_scooter", "motorcycle", "pedestrian"}; - -void validate_path(const valhalla::Api& result, const std::vector& expected_names) { - ASSERT_EQ(result.trip().routes(0).legs_size(), 1); - auto leg = result.trip().routes(0).legs(0); - gurka::assert::raw::expect_path(result, expected_names); -} - -TEST(Standalone, NodeAccess) { - const std::string ascii_map = R"( - A----B----C----D------------E - | | | - F G H - | | | - I----J----K------------L - )"; - - const gurka::ways ways = { - {"AB", {{"highway", "residential"}}}, {"BC", {{"highway", "residential"}}}, - {"CD", {{"highway", "residential"}}}, {"DE", {{"highway", "residential"}}}, - {"BF", {{"highway", "residential"}}}, {"DG", {{"highway", "residential"}}}, - {"EH", {{"highway", "residential"}}}, {"FI", {{"highway", "residential"}}}, - {"GK", {{"highway", "residential"}}}, {"HL", {{"highway", "residential"}}}, - {"IJ", {{"highway", "residential"}}}, {"JK", {{"highway", "residential"}}}, - {"KL", {{"highway", "residential"}}}, - }; - - const gurka::nodes nodes = {{"F", {{"motor_vehicle", "no"}}}, {"G", {{"motorcar", "no"}}}}; - - const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); - auto map = gurka::buildtiles(layout, ways, nodes, {}, "test/data/gurka_node_access"); - - for (auto& c : costing) { - if (c == "auto" || c == "taxi") - validate_path(gurka::do_action(valhalla::Options::route, map, {"A", "I"}, c), - {"AB", "BC", "CD", "DE", "EH", "HL", "KL", "JK", "IJ"}); - else if (c == "bicycle" || c == "pedestrian") - validate_path(gurka::do_action(valhalla::Options::route, map, {"A", "I"}, c), - {"AB", "BF", "FI"}); - else - validate_path(gurka::do_action(valhalla::Options::route, map, {"A", "I"}, c), - {"AB", "BC", "CD", "DG", "GK", "JK", "IJ"}); - } -} diff --git a/test/gurka/test_node_type.cc b/test/gurka/test_node_type.cc index dbc9431288..d016adabe5 100644 --- a/test/gurka/test_node_type.cc +++ b/test/gurka/test_node_type.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_not_thru_pruning.cc b/test/gurka/test_not_thru_pruning.cc index 0e2062b23d..06adfe7134 100644 --- a/test/gurka/test_not_thru_pruning.cc +++ b/test/gurka/test_not_thru_pruning.cc @@ -128,7 +128,7 @@ TEST(Standalone, NotThruPruning) { } }); - // trival path test. uses time dep forward A* + // trivial path test. uses time dep forward A* auto result = gurka::do_action(valhalla::Options::route, map, {"4", "6"}, "auto"); gurka::assert::raw::expect_path(result, {"TS", "RS", "HKNR", "HKNR", "HKNR", "FH", "FG", "IG", "HIJ", "HKNR", "HKNR", "HKNR", "RS", "SYT", "SYT"}); diff --git a/test/gurka/test_osrm_serializer.cc b/test/gurka/test_osrm_serializer.cc index faf3f40c09..8313fde980 100644 --- a/test/gurka/test_osrm_serializer.cc +++ b/test/gurka/test_osrm_serializer.cc @@ -1,4 +1,6 @@ +#include "baldr/rapidjson_utils.h" #include "gurka.h" +#include #include using namespace valhalla; @@ -53,11 +55,12 @@ TEST(Standalone, HeadingNumberTurnLeft) { {{"/shape_format", "geojson"}}); // auto json = gurka::convert_to_json(result, Options::Format::Options_Format_osrm); - auto heading_number{1}; - for (int i = 0; i < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++i) - ASSERT_EQ(heading_number, json["routes"][0]["legs"][0]["steps"][0]["intersections"][0]["bearings"] - .GetArray() - .Size()); + std::vector expected_headings{1, 2, 1}; + for (rapidjson::SizeType i = 0; i < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++i) + ASSERT_EQ(expected_headings[i], + json["routes"][0]["legs"][0]["steps"][i]["intersections"][0]["bearings"] + .GetArray() + .Size()); } TEST(Standalone, HeadingNumberTRoute) { @@ -81,9 +84,9 @@ TEST(Standalone, HeadingNumberTRoute) { auto json = gurka::convert_to_json(result, Options::Format::Options_Format_osrm); std::vector expected_headings{1, 3, 1}; - for (int i = 0; i < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++i) { - for (int j = 0; j < json["routes"][0]["legs"][0]["steps"][i]["intersections"].GetArray().Size(); - ++j) { + for (rapidjson::SizeType i = 0; i < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++i) { + for (rapidjson::SizeType j = 0; + j < json["routes"][0]["legs"][0]["steps"][i]["intersections"].GetArray().Size(); ++j) { ASSERT_EQ(expected_headings[i], json["routes"][0]["legs"][0]["steps"][i]["intersections"][j]["bearings"] .GetArray() @@ -111,9 +114,9 @@ TEST(Standalone, HeadingNumberForkRoute) { auto json = gurka::convert_to_json(result, valhalla::Options_Format_osrm); std::vector expected_headings{1, 3, 3, 1}; - for (int i = 0; i < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++i) { - for (int j = 0; j < json["routes"][0]["legs"][0]["steps"][i]["intersections"].GetArray().Size(); - ++j) { + for (rapidjson::SizeType i = 0; i < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++i) { + for (rapidjson::SizeType j = 0; + j < json["routes"][0]["legs"][0]["steps"][i]["intersections"].GetArray().Size(); ++j) { ASSERT_EQ(expected_headings[i], json["routes"][0]["legs"][0]["steps"][i]["intersections"][j]["bearings"] .GetArray() @@ -146,9 +149,9 @@ TEST(Standalone, HeadingNumberCrossRoad) { auto json = gurka::convert_to_json(result, Options::Format::Options_Format_osrm); std::vector expected_headings{1, 4, 1}; - for (int i = 0; i < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++i) { - for (int j = 0; j < json["routes"][0]["legs"][0]["steps"][i]["intersections"].GetArray().Size(); - ++j) { + for (rapidjson::SizeType i = 0; i < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++i) { + for (rapidjson::SizeType j = 0; + j < json["routes"][0]["legs"][0]["steps"][i]["intersections"].GetArray().Size(); ++j) { ASSERT_EQ(expected_headings[i], json["routes"][0]["legs"][0]["steps"][i]["intersections"][j]["bearings"] .GetArray() @@ -182,9 +185,9 @@ TEST(Standalone, HeadingNumber2CrossRoadPedestrian) { auto json = gurka::convert_to_json(result, Options::Format::Options_Format_osrm); std::vector expected_headings{1, 4, 4, 1}; int index{0}; - for (int i = 0; i < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++i) { - for (int j = 0; j < json["routes"][0]["legs"][0]["steps"][i]["intersections"].GetArray().Size(); - ++j) { + for (rapidjson::SizeType i = 0; i < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++i) { + for (size_t j = 0; + j < json["routes"][0]["legs"][0]["steps"][i]["intersections"].GetArray().Size(); ++j) { ASSERT_EQ(expected_headings[index++], json["routes"][0]["legs"][0]["steps"][i]["intersections"][j]["bearings"] .GetArray() @@ -195,9 +198,9 @@ TEST(Standalone, HeadingNumber2CrossRoadPedestrian) { result = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, "pedestrian"); json = gurka::convert_to_json(result, Options::Format::Options_Format_osrm); index = 0; - for (int i = 0; i < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++i) { - for (int j = 0; j < json["routes"][0]["legs"][0]["steps"][i]["intersections"].GetArray().Size(); - ++j) { + for (rapidjson::SizeType i = 0; i < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++i) { + for (rapidjson::SizeType j = 0; + j < json["routes"][0]["legs"][0]["steps"][i]["intersections"].GetArray().Size(); ++j) { ASSERT_EQ(expected_headings[index++], json["routes"][0]["legs"][0]["steps"][i]["intersections"][j]["bearings"] .GetArray() @@ -238,7 +241,8 @@ TEST(Standalone, HeadingNumber2CrossRoadAuto) { int out_cnt{0}; std::vector in_indexes{3, 3, 0}; // `in` field expected values int in_cnt{0}; - for (int steps = 0; steps < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++steps) { + for (rapidjson::SizeType steps = 0; + steps < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++steps) { int intersection_num = json["routes"][0]["legs"][0]["steps"][steps]["intersections"].GetArray().Size(); for (int intersection = 0; intersection < intersection_num; ++intersection) { @@ -301,7 +305,7 @@ TEST(Standalone, HeadingNumber2CrossRoadAuto) { int in_cnt{0}; // clang-format off - for (int step = 0; step < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++step) { + for (rapidjson::SizeType step = 0; step < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++step) { int intersection_num = json["routes"][0]["legs"][0]["steps"][step]["intersections"].GetArray().Size(); for (int intersection = 0; intersection < intersection_num; ++intersection) { @@ -430,7 +434,8 @@ TEST(Standalone, HeadingNumberAutoRoute) { std::vector expected_headings{1, 2, 4, 2, 4, 1}; int index{0}; // Validate number of bearings at each intersection of each step. - for (int step = 0; step < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++step) { + for (rapidjson::SizeType step = 0; step < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); + ++step) { int intersection_num = json["routes"][0]["legs"][0]["steps"][step]["intersections"].GetArray().Size(); for (int intersection = 0; intersection < intersection_num; ++intersection) { @@ -457,7 +462,7 @@ TEST(Standalone, HeadingNumberAutoRoute) { // Validating `in` and `out` edge indexes and bearings at each intersection of each step. // clang-format off - for (int step = 0; step < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++step) { + for (rapidjson::SizeType step = 0; step < json["routes"][0]["legs"][0]["steps"].GetArray().Size(); ++step) { int intersection_num = json["routes"][0]["legs"][0]["steps"][step]["intersections"].GetArray().Size(); for (int intersection = 0; intersection < intersection_num; ++intersection) { if (step == 0) { @@ -515,3 +520,672 @@ TEST(Standalone, HeadingNumberAutoRoute) { } // clang-format on } + +class VoiceInstructions : public ::testing::Test { +protected: + static gurka::map map; + + static void SetUpTestSuite() { + constexpr double gridsize_metres = 50; + + const std::string ascii_map = R"( + X Y + \ | --M--N + \ | __ -- ¯¯ + A----------BCD< + | ¯¯ -- __ + | --E--F + Z + )"; + + const gurka::ways ways = + {{"AB", {{"highway", "primary"}, {"maxspeed", "80"}, {"name", "10th Avenue SE"}}}, + {"BC", {{"highway", "primary"}, {"maxspeed", "50"}, {"name", "10th Avenue SE"}}}, + {"CD", {{"highway", "primary"}, {"maxspeed", "30"}, {"name", "10th Avenue SE"}}}, + {"CX", {{"highway", "primary"}, {"maxspeed", "30"}, {"name", "Sidestreet"}}}, + {"DMN", {{"highway", "primary"}, {"maxspeed", "30"}, {"name", "Heinrich Street"}}}, + {"DEF", {{"highway", "primary"}, {"maxspeed", "30"}, {"name", "Alfred Street"}}}, + {"YDZ", {{"highway", "primary"}, {"name", "Market Street"}, {"oneway", "yes"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {0.0, 0.0}); + + const std::unordered_map build_config{ + {"mjolnir.data_processing.use_admin_db", "false"}}; + + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/osrm_serializer_voice", build_config); + } + rapidjson::Document json_request(const std::string& from, + const std::string& to, + const std::string& language = "en-US") { + const std::string& request = + (boost::format( + R"({"locations":[{"lat":%s,"lon":%s},{"lat":%s,"lon":%s}],"costing":"auto","voice_instructions":true,"language":"%s"})") % + std::to_string(map.nodes.at(from).lat()) % std::to_string(map.nodes.at(from).lng()) % + std::to_string(map.nodes.at(to).lat()) % std::to_string(map.nodes.at(to).lng()) % language) + .str(); + auto result = gurka::do_action(valhalla::Options::route, map, request); + return gurka::convert_to_json(result, Options::Format::Options_Format_osrm); + } +}; + +gurka::map VoiceInstructions::map = {}; + +TEST_F(VoiceInstructions, VoiceInstructionsPresent) { + auto json = json_request("A", "F"); + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + // Validate that each step (except the last one) has voiceInstructions with announcement, + // ssmlAnnouncement and distanceAlongGeometry + for (int step = 0; step < steps.Size() - 1; ++step) { + ASSERT_TRUE(steps[step].HasMember("voiceInstructions")); + ASSERT_TRUE(steps[step]["voiceInstructions"].IsArray()); + + EXPECT_GT(steps[step]["voiceInstructions"].Size(), 0); + for (int instr = 0; instr < steps[step]["voiceInstructions"].GetArray().Size(); ++instr) { + ASSERT_TRUE(steps[step]["voiceInstructions"][instr].HasMember("announcement")); + ASSERT_TRUE(steps[step]["voiceInstructions"][instr].HasMember("ssmlAnnouncement")); + ASSERT_TRUE(steps[step]["voiceInstructions"][instr].HasMember("distanceAlongGeometry")); + } + } + + // Validate the last step as empty voiceInstructions + ASSERT_TRUE(steps[steps.Size() - 1].HasMember("voiceInstructions")); + ASSERT_TRUE(steps[steps.Size() - 1]["voiceInstructions"].IsArray()); + EXPECT_EQ(steps[steps.Size() - 1]["voiceInstructions"].Size(), 0); +} + +// depart_instruction +// +// 13 grids * 50m/grid = 650m +// => distanceAlongGeometry = 650m +// +// verbal_transition_alert_instruction +// +// The idea is that the instructions come a fixed amount of seconds before the maneuver takes place. +// For whatever reasons, a distance in meters from the end of the maneuver needs to be provided +// though. When different speeds are used on the road, they all need to be taken into account. +// +// CD: 50m / 30km/h = 50m * 3,600s / 30,000m = 50m * 0.12s/m = 6s +// BC: 50m / 50km/h = 50m * 3,600s / 50,000m = 50m * 0.072s/m = 3.6s +// SECONDS_BEFORE_VERBAL_TRANSITION_ALERT_INSTRUCTION = 15s +// AB: 15s - 6s - 3.6s = 5.4s +// 5.4s * 80 km/h = 5.4s * 80,000m / 3600s = 120m +// => distanceAlongGeometry = 120m + 50m + 50m = 220m +// +// verbal_pre_transition_instruction +// +// SECONDS_BEFORE_VERBAL_PRE_TRANSITION_INSTRUCTION = 5s +// CD: 5s * 30km/h = 5s * 30,000m / 3600s ~= 42m +TEST_F(VoiceInstructions, DistanceAlongGeometryVoiceInstructions) { + auto json = json_request("A", "D"); + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + + auto depart_instruction = steps[0]["voiceInstructions"][0].GetObject(); + EXPECT_STREQ( + depart_instruction["announcement"].GetString(), + "Drive east on 10th Avenue SE. Then, in 700 meters, You will arrive at your destination."); + EXPECT_EQ(depart_instruction["distanceAlongGeometry"].GetFloat(), 650.0); + auto verbal_transition_alert_instruction = steps[0]["voiceInstructions"][1].GetObject(); + EXPECT_STREQ(verbal_transition_alert_instruction["announcement"].GetString(), + "You will arrive at your destination."); + EXPECT_EQ(round(verbal_transition_alert_instruction["distanceAlongGeometry"].GetFloat()), 220); + auto verbal_pre_transition_instruction = steps[0]["voiceInstructions"][2].GetObject(); + EXPECT_STREQ(verbal_pre_transition_instruction["announcement"].GetString(), + "You have arrived at your destination."); + EXPECT_EQ(round(verbal_pre_transition_instruction["distanceAlongGeometry"].GetFloat()), 42); +} + +TEST_F(VoiceInstructions, ShortDepartVoiceInstructions) { + auto json = json_request("C", "F"); + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + + EXPECT_EQ(steps[0]["voiceInstructions"].Size(), 3); + + auto depart_instruction = steps[0]["voiceInstructions"][0].GetObject(); + EXPECT_STREQ(depart_instruction["announcement"].GetString(), + "Drive east on 10th Avenue SE. Then Bear right onto Alfred Street."); + EXPECT_EQ(depart_instruction["distanceAlongGeometry"].GetFloat(), 50.0); + auto verbal_transition_alert_instruction = steps[0]["voiceInstructions"][1].GetObject(); + EXPECT_STREQ(verbal_transition_alert_instruction["announcement"].GetString(), + "Bear right onto Alfred Street."); + EXPECT_EQ(verbal_transition_alert_instruction["distanceAlongGeometry"].GetFloat(), 25.0); + auto verbal_pre_transition_instruction = steps[0]["voiceInstructions"][2].GetObject(); + EXPECT_STREQ(verbal_pre_transition_instruction["announcement"].GetString(), + "Bear right onto Alfred Street."); + EXPECT_EQ(verbal_pre_transition_instruction["distanceAlongGeometry"].GetFloat(), 12.5); +} + +TEST_F(VoiceInstructions, ShortIntermediateStepVoiceInstructions) { + auto json = json_request("X", "Z"); + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + + EXPECT_EQ(steps[1]["voiceInstructions"].Size(), 2); // No verbal_post_transition_instruction + + auto verbal_transition_alert_instruction = steps[1]["voiceInstructions"][0].GetObject(); + EXPECT_STREQ(verbal_transition_alert_instruction["announcement"].GetString(), + "Turn right onto Market Street."); + EXPECT_EQ(verbal_transition_alert_instruction["distanceAlongGeometry"].GetFloat(), 50.0); + + auto verbal_pre_transition_instruction = steps[1]["voiceInstructions"][1].GetObject(); + EXPECT_STREQ(verbal_pre_transition_instruction["announcement"].GetString(), + "Turn right onto Market Street. Then You will arrive at your destination."); + // ~= 38.2 + EXPECT_GT(verbal_pre_transition_instruction["distanceAlongGeometry"].GetFloat(), 38); + EXPECT_LT(verbal_pre_transition_instruction["distanceAlongGeometry"].GetFloat(), 39); +} + +TEST_F(VoiceInstructions, AllVoiceInstructions) { + auto json = json_request("A", "F"); + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + + auto depart_instruction = steps[0]["voiceInstructions"][0].GetObject(); + EXPECT_STREQ(depart_instruction["announcement"].GetString(), + "Drive east on 10th Avenue SE. Then Bear right onto Alfred Street."); + EXPECT_EQ(depart_instruction["distanceAlongGeometry"].GetFloat(), 650.0); + + auto bear_right_instruction = steps[0]["voiceInstructions"][1].GetObject(); + EXPECT_STREQ(bear_right_instruction["announcement"].GetString(), "Bear right onto Alfred Street."); + EXPECT_EQ(round(bear_right_instruction["distanceAlongGeometry"].GetFloat()), 220); + + auto continue_instruction = steps[1]["voiceInstructions"][0].GetObject(); + EXPECT_STREQ(continue_instruction["announcement"].GetString(), "Continue for 900 meters."); + EXPECT_EQ(continue_instruction["distanceAlongGeometry"].GetFloat(), 847.0); + + auto arrive_instruction = steps[1]["voiceInstructions"][1].GetObject(); + EXPECT_STREQ(arrive_instruction["announcement"].GetString(), + "You will arrive at your destination."); + // ~= 125 + EXPECT_GT(arrive_instruction["distanceAlongGeometry"].GetFloat(), 124); + EXPECT_LT(arrive_instruction["distanceAlongGeometry"].GetFloat(), 126); + + auto final_arrive_instruction = steps[1]["voiceInstructions"][2].GetObject(); + EXPECT_STREQ(final_arrive_instruction["announcement"].GetString(), + "You have arrived at your destination."); + // ~= 42 + EXPECT_GT(final_arrive_instruction["distanceAlongGeometry"].GetFloat(), 41); + EXPECT_LT(final_arrive_instruction["distanceAlongGeometry"].GetFloat(), 43); + + auto last_instruction = steps[2]["voiceInstructions"].GetArray(); + EXPECT_EQ(last_instruction.Size(), 0); +} + +TEST_F(VoiceInstructions, DefaultVoiceLocalePresent) { + auto json = json_request("A", "F"); + auto routes = json["routes"].GetArray(); + // Validate that each route has the default voiceLocale + for (int route = 0; route < routes.Size(); ++route) { + ASSERT_TRUE(routes[route].HasMember("voiceLocale")); + EXPECT_STREQ(routes[route]["voiceLocale"].GetString(), "en-US"); + } +} + +TEST_F(VoiceInstructions, VoiceLocalePresent) { + auto json = json_request("A", "F", "de-DE"); + auto routes = json["routes"].GetArray(); + // Validate that each route has the voiceLocale from the options + for (int route = 0; route < routes.Size(); ++route) { + ASSERT_TRUE(routes[route].HasMember("voiceLocale")); + EXPECT_STREQ(routes[route]["voiceLocale"].GetString(), "de-DE"); + } +} + +TEST(Standalone, BannerInstructions) { + const std::string ascii_map = R"( + A-------------1-B---X + \ + \-2 + | + Y-------------------C---D + Z + )"; + + const gurka::ways ways = {{"A1", {{"name", "Alley Broadway"}, {"highway", "motorway"}}}, + {"1B", + {{"name", "Alley Broadway"}, + {"highway", "motorway"}, + {"lanes", "2"}, + {"turn:lanes", "through|through;slight_right"}}}, + {"B2", + {{"name", "Broadway Course"}, + {"highway", "motorway_link"}, + {"destination", "Destiny Town"}}}, + {"2C", + {{"name", "Broadway Course"}, + {"highway", "motorway_link"}, + {"destination", "Destiny Town"}, + {"lanes", "2"}, + {"turn:lanes", "left|right"}}}, + {"CD", {{"name", "Course Drive"}, {"highway", "primary"}, {"ref", "R2"}}}, + {"BX", {{"highway", "motorway"}}}, + {"CY", {{"highway", "primary"}}}, + {"CZ", {{"highway", "primary"}}}}; + const gurka::nodes nodes = {{"B", {{"highway", "motorway_junction"}, {"ref", "10"}}}}; + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 50, {0, 0}); + auto map = + gurka::buildtiles(layout, ways, nodes, {}, "test/data/osrm_serializer_banner_instructions"); + + auto from = "A"; + auto to = "D"; + const std::string& request = + (boost::format( + R"({"locations":[{"lat":%s,"lon":%s},{"lat":%s,"lon":%s}],"costing":"auto","banner_instructions":true})") % + std::to_string(map.nodes.at(from).lat()) % std::to_string(map.nodes.at(from).lng()) % + std::to_string(map.nodes.at(to).lat()) % std::to_string(map.nodes.at(to).lng())) + .str(); + auto result = gurka::do_action(valhalla::Options::route, map, request); + + auto json = gurka::convert_to_json(result, Options::Format::Options_Format_osrm); + + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + + // Validate that each step (except the last one) has bannerInstructions with primary + for (int step = 0; step < steps.Size() - 1; ++step) { + ASSERT_TRUE(steps[step].HasMember("bannerInstructions")); + ASSERT_TRUE(steps[step]["bannerInstructions"].IsArray()); + EXPECT_GT(steps[step]["bannerInstructions"].GetArray().Size(), 0); + for (int instr = 0; instr < steps[step]["bannerInstructions"].GetArray().Size(); ++instr) { + ASSERT_TRUE(steps[step]["bannerInstructions"][instr].HasMember("distanceAlongGeometry")); + ASSERT_TRUE(steps[step]["bannerInstructions"][instr].HasMember("primary")); + ASSERT_TRUE(steps[step]["bannerInstructions"][instr]["primary"].HasMember("type")); + ASSERT_TRUE(steps[step]["bannerInstructions"][instr]["primary"].HasMember("text")); + ASSERT_TRUE(steps[step]["bannerInstructions"][instr]["primary"].HasMember("components")); + ASSERT_TRUE(steps[step]["bannerInstructions"][instr]["primary"]["components"].IsArray()); + } + + // Validate the last step has empty bannerInstructions + ASSERT_TRUE(steps[steps.Size() - 1].HasMember("bannerInstructions")); + ASSERT_TRUE(steps[steps.Size() - 1]["bannerInstructions"].IsArray()); + EXPECT_EQ(steps[steps.Size() - 1]["bannerInstructions"].GetArray().Size(), 0); + } + + // validate first step has two bannerInstruction + // validate first step's distance of first bannerInstruction + EXPECT_EQ(steps[0]["bannerInstructions"][0]["distanceAlongGeometry"].GetFloat(), 800.0); + // validate first step's distance of scond bannerInstruction + // EXPECT_EQ(steps[0]["bannerInstructions"][1]["distanceAlongGeometry"].GetFloat(), 400.0); + + // validate first step's first bannerInstructions' primary instructions + // "primary": { + // "type": "off ramp", + // "modifier": "slight_right", + // "text": "Broadway Course", + // "components": [ + // {"text": "Exit","type": "text"}, + // {"text": "10","type": "exit-number"}, + // {"text": "Broadway Course","type": "text"} + // ] + // }, + auto primary_0 = steps[0]["bannerInstructions"][0]["primary"].GetObject(); + EXPECT_STREQ(primary_0["type"].GetString(), "off ramp"); + EXPECT_STREQ(primary_0["modifier"].GetString(), "slight right"); + EXPECT_STREQ(primary_0["text"].GetString(), "Broadway Course"); + EXPECT_STREQ(primary_0["text"].GetString(), "Broadway Course"); + EXPECT_EQ(primary_0["components"].GetArray().Size(), 3); + EXPECT_STREQ(primary_0["components"][0]["type"].GetString(), "exit"); + EXPECT_STREQ(primary_0["components"][0]["text"].GetString(), "Exit"); + EXPECT_STREQ(primary_0["components"][1]["type"].GetString(), "exit-number"); + EXPECT_STREQ(primary_0["components"][1]["text"].GetString(), "10"); + EXPECT_STREQ(primary_0["components"][2]["type"].GetString(), "text"); + EXPECT_STREQ(primary_0["components"][2]["text"].GetString(), "Broadway Course"); + + // validate the first step's secondary instructions + // "secondary": { + // "text": "Destiny Town", + // "components": [{"text": "Destiny Town","type": "text"}] + // }, + ASSERT_TRUE(steps[0]["bannerInstructions"][0].HasMember("secondary")); + ASSERT_TRUE(steps[0]["bannerInstructions"][0]["secondary"].HasMember("text")); + ASSERT_TRUE(steps[0]["bannerInstructions"][1].HasMember("secondary")); + ASSERT_TRUE(steps[0]["bannerInstructions"][1]["secondary"].HasMember("text")); + auto secondary_0 = steps[0]["bannerInstructions"][0]["secondary"].GetObject(); + ASSERT_TRUE(secondary_0.HasMember("text")); + EXPECT_STREQ(secondary_0["text"].GetString(), "Destiny Town"); + ASSERT_TRUE(secondary_0.HasMember("components")); + ASSERT_TRUE(secondary_0["components"].IsArray()); + EXPECT_EQ(secondary_0["components"].GetArray().Size(), 1); + ASSERT_TRUE(secondary_0["components"][0].IsObject()); + EXPECT_STREQ(secondary_0["components"][0]["type"].GetString(), "text"); + EXPECT_STREQ(secondary_0["components"][0]["text"].GetString(), "Destiny Town"); + + // validate the first step's second bannerInstructions' sub instructions + // "sub": { + // "text": "", + // "components": [ + // { + // "active": false, + // "text": "", + // "directions": ["straight"], + // "type": "lane" + // }, + // { + // "active_direction": "slight right", + // "active": true, + // "text": "", + // "directions": ["straight","slight right"], + // "type": "lane" + // } + // ] + // } + ASSERT_FALSE(steps[0]["bannerInstructions"][0].HasMember("sub")); + ASSERT_TRUE(steps[0]["bannerInstructions"][1].HasMember("sub")); + ASSERT_TRUE(steps[0]["bannerInstructions"][1].HasMember("primary")); + ASSERT_TRUE(steps[0]["bannerInstructions"][1].HasMember("secondary")); + ASSERT_EQ(steps[0]["bannerInstructions"][1]["distanceAlongGeometry"].GetFloat(), 400); + auto sub_0 = steps[0]["bannerInstructions"][1]["sub"].GetObject(); + ASSERT_TRUE(sub_0.HasMember("components")); + ASSERT_TRUE(sub_0["components"].IsArray()); + EXPECT_EQ(sub_0["components"].GetArray().Size(), 2); + for (int component = 0; component < sub_0["components"].GetArray().Size(); ++component) { + EXPECT_STREQ(sub_0["components"][component]["type"].GetString(), "lane"); + ASSERT_TRUE(sub_0["components"][component]["directions"].IsArray()); + } + EXPECT_STREQ(sub_0["components"][0]["directions"][0].GetString(), "straight"); + ASSERT_FALSE(sub_0["components"][0]["active"].GetBool()); + EXPECT_STREQ(sub_0["components"][1]["directions"][0].GetString(), "straight"); + EXPECT_STREQ(sub_0["components"][1]["directions"][1].GetString(), "slight right"); + EXPECT_STREQ(sub_0["components"][1]["active_direction"].GetString(), "slight right"); + ASSERT_TRUE(sub_0["components"][1]["active"].GetBool()); + + // validate second step's primary instructions + EXPECT_EQ(steps[1]["bannerInstructions"].GetArray().Size(), 1); + EXPECT_GT(steps[1]["bannerInstructions"][0]["distanceAlongGeometry"].GetFloat(), 310); + EXPECT_LT(steps[1]["bannerInstructions"][0]["distanceAlongGeometry"].GetFloat(), 340); + auto primary_1 = steps[1]["bannerInstructions"][0]["primary"].GetObject(); + EXPECT_STREQ(primary_1["type"].GetString(), "turn"); + EXPECT_STREQ(primary_1["modifier"].GetString(), "left"); + EXPECT_STREQ(primary_1["text"].GetString(), "Course Drive"); + ASSERT_TRUE(primary_1.HasMember("components")); + ASSERT_TRUE(primary_1["components"].IsArray()); + EXPECT_EQ(primary_1["components"].GetArray().Size(), 3); + ASSERT_TRUE(primary_1["components"][0].IsObject()); + EXPECT_STREQ(primary_1["components"][0]["type"].GetString(), "text"); + EXPECT_STREQ(primary_1["components"][0]["text"].GetString(), "Course Drive"); + EXPECT_STREQ(primary_1["components"][1]["type"].GetString(), "delimiter"); + EXPECT_STREQ(primary_1["components"][1]["text"].GetString(), "/"); + EXPECT_STREQ(primary_1["components"][2]["type"].GetString(), "text"); + EXPECT_STREQ(primary_1["components"][2]["text"].GetString(), "R2"); + + // validate the second step's second bannerInstructions' sub instructions + // "sub": { + // "text": "", + // "components": [ + // { + // "active_direction": "left", + // "active": true, + // "text": "", + // "directions": ["left"], + // "type": "lane" + // }, + // { + // "active": false, + // "text": "", + // "directions": ["right"], + // "type": "lane" + // } + // ] + // }, + ASSERT_TRUE(steps[1]["bannerInstructions"][0].HasMember("sub")); + auto sub_1 = steps[1]["bannerInstructions"][0]["sub"].GetObject(); + ASSERT_TRUE(sub_1.HasMember("components")); + ASSERT_TRUE(sub_1["components"].IsArray()); + EXPECT_EQ(sub_1["components"].GetArray().Size(), 2); + for (int component = 0; component < sub_1["components"].GetArray().Size(); ++component) { + EXPECT_STREQ(sub_1["components"][component]["type"].GetString(), "lane"); + ASSERT_TRUE(sub_1["components"][component]["directions"].IsArray()); + } + EXPECT_STREQ(sub_1["components"][0]["directions"][0].GetString(), "left"); + ASSERT_TRUE(sub_1["components"][0]["active"].GetBool()); + EXPECT_STREQ(sub_1["components"][0]["active_direction"].GetString(), "left"); + EXPECT_STREQ(sub_1["components"][1]["directions"][0].GetString(), "right"); + ASSERT_FALSE(sub_1["components"][1]["active"].GetBool()); + + // validate third step's primary instructions + auto primary_2 = steps[2]["bannerInstructions"][0]["primary"].GetObject(); + EXPECT_STREQ(primary_2["type"].GetString(), "arrive"); + EXPECT_STREQ(primary_2["text"].GetString(), "You have arrived at your destination."); +} + +TEST(Standalone, BannerInstructionsRoundabout) { + const std::string ascii_map = R"( + B + 3| + J-I-H + /K | + A---1D | + | | + E-F-G + \2 + L + )"; + + const gurka::ways ways = + {{"A1D", {{"name", "Road Name"}, {"highway", "primary"}, {"oneway", "yes"}}}, + {"DEFGHIJKD", + {{"name", "Small Roundabout"}, {"highway", "primary"}, {"junction", "roundabout"}}}, + {"B3J", {{"name", "Other"}, {"highway", "primary"}, {"oneway", "yes"}}}, + {"IB", {{"name", "Other"}, {"highway", "primary"}, {"oneway", "yes"}}}, + {"L2F", {{"name", "Other"}, {"highway", "primary"}, {"oneway", "yes"}}}, + {"EL", {{"name", "Other"}, {"highway", "primary"}, {"oneway", "yes"}}}}; + const gurka::nodes nodes = {{"1", {{"highway", "give_way"}, {"direction", "forward"}}}, + {"2", {{"highway", "give_way"}, {"direction", "forward"}}}, + {"3", {{"highway", "give_way"}, {"direction", "forward"}}}}; + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 20, {0, 0}); + const std::unordered_map build_config{ + {"mjolnir.data_processing.use_admin_db", "false"}}; + + auto map = + gurka::buildtiles(layout, ways, nodes, {}, + "test/data/osrm_serializer_banner_instructions_roundabout", build_config); + + auto from = "A"; + auto to = "B"; + const std::string& request = + (boost::format( + R"({"locations":[{"lat":%s,"lon":%s},{"lat":%s,"lon":%s}],"costing":"auto","banner_instructions":true})") % + std::to_string(map.nodes.at(from).lat()) % std::to_string(map.nodes.at(from).lng()) % + std::to_string(map.nodes.at(to).lat()) % std::to_string(map.nodes.at(to).lng())) + .str(); + auto result = gurka::do_action(valhalla::Options::route, map, request); + + auto json = gurka::convert_to_json(result, Options::Format::Options_Format_osrm); + + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + + // Validate that each step (except the last one) has bannerInstructions with primary + for (int step = 0; step < steps.Size() - 1; ++step) { + ASSERT_TRUE(steps[step].HasMember("bannerInstructions")); + ASSERT_TRUE(steps[step]["bannerInstructions"].IsArray()); + EXPECT_GT(steps[step]["bannerInstructions"].GetArray().Size(), 0); + for (int instr = 0; instr < steps[step]["bannerInstructions"].GetArray().Size(); ++instr) { + ASSERT_TRUE(steps[step]["bannerInstructions"][instr].HasMember("distanceAlongGeometry")); + ASSERT_TRUE(steps[step]["bannerInstructions"][instr].HasMember("primary")); + ASSERT_TRUE(steps[step]["bannerInstructions"][instr]["primary"].HasMember("type")); + ASSERT_TRUE(steps[step]["bannerInstructions"][instr]["primary"].HasMember("text")); + ASSERT_TRUE(steps[step]["bannerInstructions"][instr]["primary"].HasMember("components")); + ASSERT_TRUE(steps[step]["bannerInstructions"][instr]["primary"]["components"].IsArray()); + } + } + + // Validate the last step has empty bannerInstructions + ASSERT_TRUE(steps[steps.Size() - 1].HasMember("bannerInstructions")); + ASSERT_TRUE(steps[steps.Size() - 1]["bannerInstructions"].IsArray()); + EXPECT_EQ(steps[steps.Size() - 1]["bannerInstructions"].GetArray().Size(), 0); + + // validate first step's primary instructions + auto primary_0 = steps[0]["bannerInstructions"][0]["primary"].GetObject(); + EXPECT_STREQ(primary_0["type"].GetString(), "rotary"); + ASSERT_TRUE(primary_0.HasMember("degrees")); + EXPECT_EQ(primary_0["degrees"].GetFloat(), 270); + EXPECT_STREQ(primary_0["text"].GetString(), "Other"); +} + +class Rotary : public ::testing::Test { +protected: + static gurka::map map; + + static void SetUpTestSuite() { + const std::string ascii_map = R"( + V + | + | + | + U + / \ + 3 | + K---J + L---/ \---I + --/ \-- + / \ + M H + | | + /----N G-2--\ + Q----R | | S------T + \--1-A F----/ + | | + B E + \ / + --\ /-- + C---\ /---D + Y---Z + + + )"; + + const gurka::ways ways = + {{"ABCYZDEFG", {{"name", "Grand Rotary"}, {"highway", "primary"}, {"junction", "circular"}}}, + {"GHIJK", {{"name", "Grand Rotary"}, {"highway", "primary"}, {"junction", "circular"}}}, + {"KLMNA", {{"name", "Grand Rotary"}, {"highway", "primary"}, {"junction", "circular"}}}, + + {"QR", {{"name", "Western Road"}, {"highway", "primary"}}}, + {"R1A", {{"name", "Western Road"}, {"highway", "primary"}, {"oneway", "yes"}}}, + {"NR", {{"name", "Western Road"}, {"highway", "primary"}, {"oneway", "yes"}}}, + + {"TS", {{"name", "Eastern Road"}, {"highway", "primary"}}}, + {"S2G", {{"name", "Eastern Road"}, {"highway", "primary"}, {"oneway", "yes"}}}, + {"FS", {{"name", "Eastern Road"}, {"highway", "primary"}, {"oneway", "yes"}}}, + + {"UV", {{"name", "Northern Road"}, {"highway", "primary"}}}, + {"U3K", {{"name", "Northern Road"}, {"highway", "primary"}, {"oneway", "yes"}}}, + {"JU", {{"name", "Northern Road"}, {"highway", "primary"}, {"oneway", "yes"}}}}; + const gurka::nodes nodes = {{"1", {{"highway", "give_way"}, {"direction", "forward"}}}, + {"2", {{"highway", "give_way"}, {"direction", "forward"}}}, + {"3", {{"highway", "give_way"}, {"direction", "forward"}}}}; + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 10, {13.34792, 52.51585}); + const std::unordered_map build_config{ + {"mjolnir.data_processing.use_admin_db", "false"}}; + + map = gurka::buildtiles(layout, ways, nodes, {}, + "test/data/osrm_serializer_banner_instructions_rotary", build_config); + } + + rapidjson::Document json_request(const std::string& from, const std::string& to) { + const std::string& request = + (boost::format( + R"({"locations":[{"lat":%s,"lon":%s},{"lat":%s,"lon":%s}],"costing":"auto","banner_instructions":true})") % + std::to_string(map.nodes.at(from).lat()) % std::to_string(map.nodes.at(from).lng()) % + std::to_string(map.nodes.at(to).lat()) % std::to_string(map.nodes.at(to).lng())) + .str(); + auto result = gurka::do_action(valhalla::Options::route, map, request); + return gurka::convert_to_json(result, Options::Format::Options_Format_osrm); + } +}; + +gurka::map Rotary::map = {}; + +TEST_F(Rotary, BannerInstructionsRotaryWestNorth) { + auto json = json_request("Q", "V"); + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + + auto primary_0 = steps[0]["bannerInstructions"][0]["primary"].GetObject(); + + EXPECT_STREQ(primary_0["type"].GetString(), "rotary"); + ASSERT_TRUE(primary_0.HasMember("degrees")); + EXPECT_GT(primary_0["degrees"].GetFloat(), 225); + EXPECT_LT(primary_0["degrees"].GetFloat(), 315); + ASSERT_TRUE(primary_0.HasMember("driving_side")); + EXPECT_STREQ(primary_0["driving_side"].GetString(), "right"); + EXPECT_STREQ(primary_0["text"].GetString(), "Northern Road"); + + auto primary_1 = steps[1]["bannerInstructions"][0]["primary"].GetObject(); + + EXPECT_STREQ(primary_1["type"].GetString(), "exit rotary"); + ASSERT_TRUE(primary_1.HasMember("degrees")); + EXPECT_GT(primary_1["degrees"].GetFloat(), 225); + EXPECT_LT(primary_1["degrees"].GetFloat(), 315); + ASSERT_TRUE(primary_1.HasMember("driving_side")); + EXPECT_STREQ(primary_1["driving_side"].GetString(), "right"); + EXPECT_STREQ(primary_1["text"].GetString(), "Northern Road"); +} + +TEST_F(Rotary, BannerInstructionsOnRotary) { + auto json = json_request("A", "V"); + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + + auto primary_0 = steps[0]["bannerInstructions"][0]["primary"].GetObject(); + + EXPECT_STREQ(primary_0["type"].GetString(), "exit roundabout"); + ASSERT_TRUE(primary_0.HasMember("degrees")); + ASSERT_TRUE(primary_0.HasMember("driving_side")); + EXPECT_STREQ(primary_0["driving_side"].GetString(), "right"); + EXPECT_STREQ(primary_0["text"].GetString(), "Northern Road"); +} + +TEST_F(Rotary, BannerInstructionsRotaryEastNorth) { + auto json = json_request("T", "V"); + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + + auto primary_0 = steps[0]["bannerInstructions"][0]["primary"].GetObject(); + + EXPECT_STREQ(primary_0["type"].GetString(), "rotary"); + ASSERT_TRUE(primary_0.HasMember("degrees")); + EXPECT_GT(primary_0["degrees"].GetFloat(), 45); + EXPECT_LT(primary_0["degrees"].GetFloat(), 135); + ASSERT_TRUE(primary_0.HasMember("driving_side")); + EXPECT_STREQ(primary_0["driving_side"].GetString(), "right"); + EXPECT_STREQ(primary_0["text"].GetString(), "Northern Road"); + + auto primary_1 = steps[1]["bannerInstructions"][0]["primary"].GetObject(); + + EXPECT_STREQ(primary_1["type"].GetString(), "exit rotary"); + ASSERT_TRUE(primary_1.HasMember("degrees")); + EXPECT_GT(primary_1["degrees"].GetFloat(), 45); + EXPECT_LT(primary_1["degrees"].GetFloat(), 135); + ASSERT_TRUE(primary_1.HasMember("driving_side")); + EXPECT_STREQ(primary_1["driving_side"].GetString(), "right"); + EXPECT_STREQ(primary_1["text"].GetString(), "Northern Road"); +} + +TEST_F(Rotary, BannerInstructionsRotaryEastWest) { + auto json = json_request("Q", "T"); + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + + auto primary_0 = steps[0]["bannerInstructions"][0]["primary"].GetObject(); + + EXPECT_STREQ(primary_0["type"].GetString(), "rotary"); + ASSERT_TRUE(primary_0.HasMember("degrees")); + EXPECT_GT(primary_0["degrees"].GetFloat(), 135); + EXPECT_LT(primary_0["degrees"].GetFloat(), 225); + ASSERT_TRUE(primary_0.HasMember("driving_side")); + EXPECT_STREQ(primary_0["driving_side"].GetString(), "right"); + EXPECT_STREQ(primary_0["text"].GetString(), "Eastern Road"); + + auto primary_1 = steps[1]["bannerInstructions"][0]["primary"].GetObject(); + + EXPECT_STREQ(primary_1["type"].GetString(), "exit rotary"); + ASSERT_TRUE(primary_1.HasMember("degrees")); + EXPECT_GT(primary_1["degrees"].GetFloat(), 135); + EXPECT_LT(primary_1["degrees"].GetFloat(), 225); + ASSERT_TRUE(primary_1.HasMember("driving_side")); + EXPECT_STREQ(primary_1["driving_side"].GetString(), "right"); + EXPECT_STREQ(primary_1["text"].GetString(), "Eastern Road"); +} + +TEST_F(Rotary, EndOnRotary) { + // This is just a test that ending on the rotary doesn't cause a segfault :D + auto json = json_request("Q", "F"); + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + + auto primary_0 = steps[0]["bannerInstructions"][0]["primary"].GetObject(); + + EXPECT_STREQ(primary_0["type"].GetString(), "rotary"); + ASSERT_TRUE(primary_0.HasMember("degrees")); +} diff --git a/test/gurka/test_pathlocation_serialization.cc b/test/gurka/test_pathlocation_serialization.cc index 8ee74fc4a8..8c73e46baf 100644 --- a/test/gurka/test_pathlocation_serialization.cc +++ b/test/gurka/test_pathlocation_serialization.cc @@ -5,8 +5,6 @@ // with the same properties. // -#include - #include "baldr/pathlocation.h" #include "gurka.h" diff --git a/test/gurka/test_pbf_api.cc b/test/gurka/test_pbf_api.cc index 28d5ddc5f7..9e0b8ec71b 100644 --- a/test/gurka/test_pbf_api.cc +++ b/test/gurka/test_pbf_api.cc @@ -28,28 +28,42 @@ TEST(pbf_api, pbf_in_out) { const auto layout = gurka::detail::map_to_coordinates(ascii_map, 10); auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_api_minimal"); - std::unordered_set pbf_actions{ - Options::route, Options::trace_route, Options::optimized_route, - Options::centroid, Options::trace_attributes, Options::status, - }; + std::unordered_set pbf_actions{Options::route, + Options::trace_route, + Options::optimized_route, + Options::centroid, + Options::trace_attributes, + Options::status, + Options::sources_to_targets, + Options::isochrone}; PbfFieldSelector select_all; select_all.set_directions(true); select_all.set_trip(true); select_all.set_status(true); select_all.set_options(true); + select_all.set_matrix(true); + select_all.set_isochrone(true); for (int action = Options::no_action + 1; action <= Options::Action_MAX; ++action) { // don't have convenient support of these in gurka yet - if (action == Options::sources_to_targets || action == Options::isochrone || - action == Options::expansion) + if (action == Options::expansion) continue; // do the regular request with json in and out std::string expected_json, request_json; - auto expected_pbf = - gurka::do_action(Options::Action(action), map, {"A", "C", "I", "G"}, "pedestrian", {}, {}, - &expected_json, "break", &request_json); + Api expected_pbf; + if (action == Options::sources_to_targets) { + expected_pbf = gurka::do_action(Options::Action(action), map, {"A", "C"}, {"I", "G"}, + "pedestrian", {}, {}, &expected_json, &request_json); + } else if (action == Options::isochrone) { + expected_pbf = + gurka::do_action(Options::Action(action), map, {"A"}, "pedestrian", + {{"/contours/0/time", "10"}}, {}, &expected_json, "break", &request_json); + } else { + expected_pbf = gurka::do_action(Options::Action(action), map, {"A", "C", "I", "G"}, + "pedestrian", {}, {}, &expected_json, "break", &request_json); + } // get some clean input options for pbf requests Api clean_pbf; @@ -69,16 +83,20 @@ TEST(pbf_api, pbf_in_out) { pbf_out.mutable_options()->clear_costings(); pbf_out.mutable_options()->set_format(Options::pbf); pbf_out.mutable_options()->mutable_pbf_field_selector()->CopyFrom(select_all); + auto pbf_bytes = gurka::do_action(map, pbf_out); Api actual_pbf; EXPECT_TRUE(actual_pbf.ParseFromString(pbf_bytes)); EXPECT_EQ(actual_pbf.trip().SerializeAsString(), expected_pbf.trip().SerializeAsString()); EXPECT_TRUE(actual_pbf.has_options()); - EXPECT_TRUE(actual_pbf.has_trip() || action == Options::status); - EXPECT_TRUE(actual_pbf.has_directions() || action == Options::trace_attributes || - action == Options::status); + EXPECT_TRUE(actual_pbf.has_trip() || action == Options::status || + action == Options::sources_to_targets || action == Options::isochrone); + EXPECT_TRUE(actual_pbf.has_directions() || action != Options::trace_route || + action != Options::route); EXPECT_TRUE(actual_pbf.has_status() || action != Options::status); EXPECT_TRUE(actual_pbf.has_info() || action == Options::status); + EXPECT_TRUE(actual_pbf.has_matrix() || action != Options::sources_to_targets); + EXPECT_TRUE(actual_pbf.has_isochrone() || action != Options::isochrone); // lets try it again but this time we'll disable all the fields but one Api slimmed; @@ -90,7 +108,8 @@ TEST(pbf_api, pbf_in_out) { Api actual_slimmed; EXPECT_TRUE(actual_slimmed.ParseFromString(pbf_bytes)); EXPECT_FALSE(actual_slimmed.has_options()); - EXPECT_TRUE(actual_slimmed.has_trip() || action == Options::status); + EXPECT_TRUE(actual_slimmed.has_trip() || action == Options::status || + action == Options::sources_to_targets || action == Options::isochrone); EXPECT_FALSE(actual_slimmed.has_directions()); EXPECT_FALSE(actual_slimmed.has_status()); EXPECT_TRUE(actual_slimmed.has_info() || action == Options::status); @@ -104,11 +123,13 @@ TEST(pbf_api, pbf_in_out) { actual_slimmed.Clear(); EXPECT_TRUE(actual_slimmed.ParseFromString(pbf_bytes)); EXPECT_FALSE(actual_slimmed.has_options()); - EXPECT_TRUE(actual_slimmed.has_trip() || action != Options::trace_attributes); - EXPECT_TRUE(actual_slimmed.has_directions() || action == Options::trace_attributes || - action == Options::status); + EXPECT_TRUE(actual_slimmed.has_trip() || action != Options::trace_attributes || + action != Options::isochrone); + EXPECT_TRUE(actual_slimmed.has_directions() || action != Options::trace_route || + action != Options::route); EXPECT_TRUE(actual_slimmed.has_status() || action != Options::status); EXPECT_TRUE(actual_slimmed.has_info() || action == Options::status); + EXPECT_TRUE(actual_slimmed.has_matrix() || action != Options::sources_to_targets); } } } @@ -131,6 +152,7 @@ TEST(pbf_api, pbf_error) { EXPECT_FALSE(actual.has_trip()); EXPECT_FALSE(actual.has_directions()); EXPECT_FALSE(actual.has_status()); + EXPECT_FALSE(actual.has_matrix()); EXPECT_EQ(actual.info().errors().size(), 1); } // try again with an action but no locations diff --git a/test/gurka/test_phonemes.cc b/test/gurka/test_phonemes.cc index da6f7395bf..c0f7f966be 100644 --- a/test/gurka/test_phonemes.cc +++ b/test/gurka/test_phonemes.cc @@ -1,5 +1,8 @@ +#include "baldr/graphreader.h" +#include "filesystem.h" +#include "mjolnir/util.h" + #include "gurka.h" -#include "test/test.h" #include #if !defined(VALHALLA_SOURCE_DIR) @@ -48,29 +51,29 @@ valhalla::gurka::map BuildPBF(const std::string& workdir) { {"official_name:pronunciation:nt-sampa", "official_name:pronunciation:nt-sampa"}, {"tunnel:name:pronunciation:nt-sampa", "tunnel:name:pronunciation:nt-sampa"}, {"name:en:pronunciation:nt-sampa", "name:en:pronunciation:nt-sampa"}, - {"name:pronunciation:x-katakana", "name:pronunciation:x-katakana"}, - {"ref:pronunciation:x-katakana", "ref:pronunciation:x-katakana"}, - {"int_ref:pronunciation:x-katakana", "int_ref:pronunciation:x-katakana"}, - {"direction:pronunciation:x-katakana", "direction:pronunciation:x-katakana"}, - {"int_direction:pronunciation:x-katakana", "int_direction:pronunciation:x-katakana"}, - {"alt_name:pronunciation:x-katakana", "alt_name:pronunciation:x-katakana"}, - {"official_name:pronunciation:x-katakana", "official_name:pronunciation:x-katakana"}, - {"tunnel:name:pronunciation:x-katakana", "tunnel:name:pronunciation:x-katakana"}, - {"name:en:pronunciation:x-katakana", "name:en:pronunciation:x-katakana"}, - {"name:pronunciation:x-jeita", "name:pronunciation:x-jeita"}, - {"ref:pronunciation:x-jeita", "ref:pronunciation:x-jeita"}, - {"int_ref:pronunciation:x-jeita", "int_ref:pronunciation:x-jeita"}, - {"direction:pronunciation:x-jeita", "direction:pronunciation:x-jeita"}, - {"int_direction:pronunciation:x-jeita", "int_direction:pronunciation:x-jeita"}, - {"alt_name:pronunciation:x-jeita", "alt_name:pronunciation:x-jeita"}, - {"official_name:pronunciation:x-jeita", "official_name:pronunciation:x-jeita"}, - {"tunnel:name:pronunciation:x-jeita", "tunnel:name:pronunciation:x-jeita"}, - {"name:en:pronunciation:x-jeita", "name:en:pronunciation:x-jeita"}, + {"name:pronunciation:katakana", "name:pronunciation:katakana"}, + {"ref:pronunciation:katakana", "ref:pronunciation:katakana"}, + {"int_ref:pronunciation:katakana", "int_ref:pronunciation:katakana"}, + {"direction:pronunciation:katakana", "direction:pronunciation:katakana"}, + {"int_direction:pronunciation:katakana", "int_direction:pronunciation:katakana"}, + {"alt_name:pronunciation:katakana", "alt_name:pronunciation:katakana"}, + {"official_name:pronunciation:katakana", "official_name:pronunciation:katakana"}, + {"tunnel:name:pronunciation:katakana", "tunnel:name:pronunciation:katakana"}, + {"name:en:pronunciation:katakana", "name:en:pronunciation:katakana"}, + {"name:pronunciation:jeita", "name:pronunciation:jeita"}, + {"ref:pronunciation:jeita", "ref:pronunciation:jeita"}, + {"int_ref:pronunciation:jeita", "int_ref:pronunciation:jeita"}, + {"direction:pronunciation:jeita", "direction:pronunciation:jeita"}, + {"int_direction:pronunciation:jeita", "int_direction:pronunciation:jeita"}, + {"alt_name:pronunciation:jeita", "alt_name:pronunciation:jeita"}, + {"official_name:pronunciation:jeita", "official_name:pronunciation:jeita"}, + {"tunnel:name:pronunciation:jeita", "tunnel:name:pronunciation:jeita"}, + {"name:en:pronunciation:jeita", "name:en:pronunciation:jeita"}, {"destination", "destination"}, {"destination:pronunciation", "destination:pronunciation"}, {"destination:pronunciation:nt-sampa", "destination:pronunciation:nt-sampa"}, - {"destination:pronunciation:x-katakana", "destination:pronunciation:x-katakana"}, - {"destination:pronunciation:x-jeita", "destination:pronunciation:x-jeita"}}}, + {"destination:pronunciation:katakana", "destination:pronunciation:katakana"}, + {"destination:pronunciation:jeita", "destination:pronunciation:jeita"}}}, {"BC", {{"highway", "trunk"}, {"name", "BC"}, @@ -79,11 +82,11 @@ valhalla::gurka::map BuildPBF(const std::string& workdir) { {"name:en", "name:en"}, {"name:pronunciation", "name:pronunciation"}, {"alt_name:pronunciation:nt-sampa", "alt_name:pronunciation:nt-sampa"}, - {"alt_name:pronunciation:x-katakana", "alt_name:pronunciation:x-katakana"}, - {"official_name:pronunciation:x-katakana", "official_name:pronunciation:x-katakana"}, - {"official_name:pronunciation:x-jeita", "official_name:pronunciation:x-jeita"}, + {"alt_name:pronunciation:katakana", "alt_name:pronunciation:katakana"}, + {"official_name:pronunciation:katakana", "official_name:pronunciation:katakana"}, + {"official_name:pronunciation:jeita", "official_name:pronunciation:jeita"}, {"name:en:pronunciation", "name:en:pronunciation"}, - {"name:en:pronunciation:x-katakana", "name:en:pronunciation:x-katakana"}, + {"name:en:pronunciation:katakana", "name:en:pronunciation:katakana"}, {"destination:forward", "destination:forward"}, {"destination:backward", "destination:backward"}, {"destination:ref", "destination:ref"}, @@ -98,26 +101,22 @@ valhalla::gurka::map BuildPBF(const std::string& workdir) { {"destination:street:pronunciation", "destination:street:pronunciation"}, {"destination:street:to:pronunciation", "destination:street:to:pronunciation"}, {"junction:ref:pronunciation", "junction:ref:pronunciation"}, - {"destination:forward:pronunciation:x-katakana", - "destination:forward:pronunciation:x-katakana"}, - {"destination:backward:pronunciation:x-katakana", - "destination:backward:pronunciation:x-katakana"}, - {"destination:ref:pronunciation:x-katakana", "destination:ref:pronunciation:x-katakana"}, - {"destination:ref:to:pronunciation:x-katakana", - "destination:ref:to:pronunciation:x-katakana"}, - {"destination:street:pronunciation:x-katakana", - "destination:street:pronunciation:x-katakana"}, - {"destination:street:to:pronunciation:x-katakana", - "destination:street:to:pronunciation:x-katakana"}, - {"junction:ref:pronunciation:x-katakana", "junction:ref:pronunciation:x-katakana"}, - {"destination:forward:pronunciation:x-jeita", "destination:forward:pronunciation:x-jeita"}, - {"destination:backward:pronunciation:x-jeita", "destination:backward:pronunciation:x-jeita"}, - {"destination:ref:pronunciation:x-jeita", "destination:ref:pronunciation:x-jeita"}, - {"destination:ref:to:pronunciation:x-jeita", "destination:ref:to:pronunciation:x-jeita"}, - {"destination:street:pronunciation:x-jeita", "destination:street:pronunciation:x-jeita"}, - {"destination:street:to:pronunciation:x-jeita", - "destination:street:to:pronunciation:x-jeita"}, - {"junction:ref:pronunciation:x-jeita", "junction:ref:pronunciation:x-jeita"}}}, + {"destination:forward:pronunciation:katakana", "destination:forward:pronunciation:katakana"}, + {"destination:backward:pronunciation:katakana", + "destination:backward:pronunciation:katakana"}, + {"destination:ref:pronunciation:katakana", "destination:ref:pronunciation:katakana"}, + {"destination:ref:to:pronunciation:katakana", "destination:ref:to:pronunciation:katakana"}, + {"destination:street:pronunciation:katakana", "destination:street:pronunciation:katakana"}, + {"destination:street:to:pronunciation:katakana", + "destination:street:to:pronunciation:katakana"}, + {"junction:ref:pronunciation:katakana", "junction:ref:pronunciation:katakana"}, + {"destination:forward:pronunciation:jeita", "destination:forward:pronunciation:jeita"}, + {"destination:backward:pronunciation:jeita", "destination:backward:pronunciation:jeita"}, + {"destination:ref:pronunciation:jeita", "destination:ref:pronunciation:jeita"}, + {"destination:ref:to:pronunciation:jeita", "destination:ref:to:pronunciation:jeita"}, + {"destination:street:pronunciation:jeita", "destination:street:pronunciation:jeita"}, + {"destination:street:to:pronunciation:jeita", "destination:street:to:pronunciation:jeita"}, + {"junction:ref:pronunciation:jeita", "junction:ref:pronunciation:jeita"}}}, {"BD", {{"highway", "trunk"}, {"destination:forward", "destination:forward"}, @@ -134,18 +133,15 @@ valhalla::gurka::map BuildPBF(const std::string& workdir) { {"destination:street:pronunciation", "destination:street:pronunciation"}, {"destination:street:to:pronunciation", "destination:street:to:pronunciation"}, {"junction:ref:pronunciation", "junction:ref:pronunciation"}, - {"destination:forward:pronunciation:x-katakana", - "destination:forward:pronunciation:x-katakana"}, - {"destination:backward:pronunciation:x-katakana", - "destination:backward:pronunciation:x-katakana"}, - {"destination:ref:pronunciation:x-katakana", "destination:ref:pronunciation:x-katakana"}, - {"destination:ref:to:pronunciation:x-katakana", - "destination:ref:to:pronunciation:x-katakana"}, - {"destination:street:pronunciation:x-katakana", - "destination:street:pronunciation:x-katakana"}, - {"destination:street:to:pronunciation:x-katakana", - "destination:street:to:pronunciation:x-katakana"}, - {"junction:ref:pronunciation:x-katakana", "junction:ref:pronunciation:x-katakana"}}}, + {"destination:forward:pronunciation:katakana", "destination:forward:pronunciation:katakana"}, + {"destination:backward:pronunciation:katakana", + "destination:backward:pronunciation:katakana"}, + {"destination:ref:pronunciation:katakana", "destination:ref:pronunciation:katakana"}, + {"destination:ref:to:pronunciation:katakana", "destination:ref:to:pronunciation:katakana"}, + {"destination:street:pronunciation:katakana", "destination:street:pronunciation:katakana"}, + {"destination:street:to:pronunciation:katakana", + "destination:street:to:pronunciation:katakana"}, + {"junction:ref:pronunciation:katakana", "junction:ref:pronunciation:katakana"}}}, {"DE", {{"highway", "primary"}, {"name", "DE;xyz street;abc ave"}, @@ -177,16 +173,16 @@ valhalla::gurka::map BuildPBF(const std::string& workdir) { {{"highway", "primary"}, {"oneway", "yes"}, {"name", "HI;xyz street;abc ave"}, - {"name:pronunciation:x-katakana", - ";name:pronunciation2:x-katakana;name:pronunciation3:x-katakana"}, - {"name:pronunciation:x-jeita", ";name:pronunciation2:x-jeita;name:pronunciation3:x-jeita"}, + {"name:pronunciation:katakana", + ";name:pronunciation2:katakana;name:pronunciation3:katakana"}, + {"name:pronunciation:jeita", ";name:pronunciation2:jeita;name:pronunciation3:jeita"}, {"destination", "destination1;destination2;destination3"}, {"destination:pronunciation", ";destination:pronunciation2;destination:pronunciation3"}}}, {"IJ", {{"highway", "primary"}, {"oneway", "yes"}, - {"name:pronunciation:x-katakana", ";name:pronunciation1;;"}, - {"name:pronunciation:x-jeita", ";;name:pronunciation2"}, + {"name:pronunciation:katakana", ";name:pronunciation1;;"}, + {"name:pronunciation:jeita", ";;name:pronunciation2"}, {"destination", "destination1;destination2;destination3"}, {"destination:pronunciation", "destination:pronunciation1;;destination:pronunciation3"}}}, {"JK", @@ -203,14 +199,14 @@ valhalla::gurka::map BuildPBF(const std::string& workdir) { {{"highway", "primary"}, {"oneway", "yes"}, {"destination", "destination1;destination2;destination3"}, - {"destination:pronunciation:x-katakana", ";destination:pronunciation2:x-katakana;"}, - {"destination:pronunciation:x-jeita", ";destination:pronunciation2:x-jeita;"}}}, + {"destination:pronunciation:katakana", ";destination:pronunciation2:katakana;"}, + {"destination:pronunciation:jeita", ";destination:pronunciation2:jeita;"}}}, {"MN", {{"highway", "primary"}, {"oneway", "yes"}, {"destination", "destination1;destination2;destination3"}, - {"destination:pronunciation:x-katakana", ";;destination:pronunciation3:x-katakana"}, - {"destination:pronunciation:x-jeita", ";destination:pronunciation2:x-jeita;"}}}}; + {"destination:pronunciation:katakana", ";;destination:pronunciation3:katakana"}, + {"destination:pronunciation:jeita", ";destination:pronunciation2:jeita;"}}}}; const gurka::nodes nodes = {{"B", @@ -219,13 +215,13 @@ valhalla::gurka::map BuildPBF(const std::string& workdir) { {"highway", "motorway_junction"}, {"name:pronunciation", "named junction:pronunciation"}, {"name:pronunciation:nt-sampa", "named junction:pronunciation:nt-sampa"}, - {"name:pronunciation:x-katakana", "named junction:pronunciation:x-katakana"}, - {"name:pronunciation:x-jeita", "named junction:pronunciation:x-jeita"}}}, + {"name:pronunciation:katakana", "named junction:pronunciation:katakana"}, + {"name:pronunciation:jeita", "named junction:pronunciation:jeita"}}}, {"E", {{"ref", "node ref"}, {"highway", "motorway_junction"}, {"ref:pronunciation", "node ref:pronunciation"}, - {"ref:pronunciation:x-katakana", "node ref:pronunciation:x-katakana"}}}}; + {"ref:pronunciation:katakana", "node ref:pronunciation:katakana"}}}}; constexpr double gridsize = 100; @@ -373,70 +369,91 @@ TEST(Standalone, PhonemesWithAltandDirection) { GraphId node_id = AB_edge->endnode(); auto tile = graph_reader.GetGraphTile(node_id); - std::unordered_map> pronunciations; - std::vector signs = tile->GetSigns(node_id.id(), pronunciations, true); + std::unordered_map> index_linguistic_map; + std::vector signs = tile->GetSigns(node_id.id(), index_linguistic_map, true); uint32_t sign_index = 0; ASSERT_EQ(signs.size(), 1); - ASSERT_EQ(pronunciations.size(), 1); + ASSERT_EQ(index_linguistic_map.size(), 1); for (const auto& sign : signs) { - std::unordered_map>::const_iterator iter = - pronunciations.find(sign_index); - ASSERT_NE(iter, pronunciations.end()); + std::unordered_map>::const_iterator iter = + index_linguistic_map.find(sign_index); + ASSERT_NE(iter, index_linguistic_map.end()); - if ((iter->second).second == "named junction:pronunciation:nt-sampa") { + if (std::get(iter->second) == + "named junction:pronunciation:nt-sampa") { EXPECT_EQ(signs.at(sign_index).text(), "named junction"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(static_cast(std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kNtSampa)); - } else if ((iter->second).second == "destination:pronunciation:nt-sampa") { + } else if (std::get(iter->second) == + "destination:pronunciation:nt-sampa") { EXPECT_EQ(signs.at(sign_index).text(), "destination"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(static_cast(std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kNtSampa)); } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; ++sign_index; } } - // Test the ref on the node katakana wins. Also, test empty pronunciations + // Test the ref on the node katakana wins. Also, test empty linguistics { GraphId node_id = EF_edge->endnode(); auto tile = graph_reader.GetGraphTile(node_id); auto edgeinfo = tile->edgeinfo(EF_edge); - std::unordered_map> pronunciations; - std::vector signs = tile->GetSigns(EF_edge_id.id(), pronunciations); + std::unordered_map> index_linguistic_map; + + std::vector signs = tile->GetSigns(EF_edge_id.id(), index_linguistic_map); uint32_t sign_index = 0; ASSERT_EQ(signs.size(), 4); - ASSERT_EQ(pronunciations.size(), 1); + ASSERT_EQ(index_linguistic_map.size(), 4); + for (const auto& sign : signs) { - std::unordered_map>::const_iterator iter = - pronunciations.find(sign_index); - if (iter == pronunciations.end()) { + std::unordered_map>::const_iterator iter = + index_linguistic_map.find(sign_index); + if (iter == index_linguistic_map.end()) { if (sign_index == 1) EXPECT_EQ(signs.at(sign_index).text(), "destination1"); if (sign_index == 2) EXPECT_EQ(signs.at(sign_index).text(), "destination2"); } else { - if ((iter->second).second == "node ref:pronunciation:x-katakana") { + if (std::get(iter->second) == + "node ref:pronunciation:katakana") { EXPECT_EQ(signs.at(sign_index).text(), "node ref"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXKatakana)); - } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + EXPECT_EQ(static_cast( + std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kKatakana)); + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + } else { + if (static_cast( + std::get(iter->second)) == + PronunciationAlphabet::kNone) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), ""); + } else + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; + } } ++sign_index; } - // blank pronunciations for names. + // blank linguistics for names. + std::vector types; auto names_and_types = edgeinfo.GetNamesAndTypes(true); ASSERT_EQ(names_and_types.size(), 3); - std::unordered_map> name_pronunciations = - edgeinfo.GetPronunciationsMap(); + std::unordered_map> name_linguistics = + edgeinfo.GetLinguisticMap(); uint8_t name_index = 0; for (const auto& name_and_type : names_and_types) { if (std::get<2>(name_and_type) != 0) { @@ -444,65 +461,85 @@ TEST(Standalone, PhonemesWithAltandDirection) { ++name_index; continue; } - std::unordered_map>::const_iterator iter = - name_pronunciations.find(name_index); - if (iter == name_pronunciations.end()) { - ASSERT_NE(name_index, 1); + std::unordered_map>::const_iterator iter = + name_linguistics.find(name_index); + ASSERT_NE(iter, name_linguistics.end()); + if (name_index == 0 || name_index == 2) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), ""); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kNone)); } else { // first and last name is missing in the name field EXPECT_EQ(name_index, 1); - if ((iter->second).second == "name:pronunciation2") { - EXPECT_EQ(std::get<0>(name_and_type), "xyz street"); - EXPECT_EQ(static_cast((iter->second).first), + if (std::get(iter->second) == "name:pronunciation2") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "xyz street"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; } ++name_index; } } - // Test 2 empty pronunciations + // Test 2 empty linguistics { GraphId node_id = FG_edge->endnode(); auto tile = graph_reader.GetGraphTile(node_id); auto edgeinfo = tile->edgeinfo(FG_edge); - std::unordered_map> pronunciations; - std::vector signs = tile->GetSigns(FG_edge_id.id(), pronunciations); + std::unordered_map> index_linguistic_map; + + std::vector signs = tile->GetSigns(FG_edge_id.id(), index_linguistic_map); uint32_t sign_index = 0; ASSERT_EQ(signs.size(), 3); - ASSERT_EQ(pronunciations.size(), 1); + ASSERT_EQ(index_linguistic_map.size(), 3); for (const auto& sign : signs) { - std::unordered_map>::const_iterator iter = - pronunciations.find(sign_index); + std::unordered_map>::const_iterator iter = + index_linguistic_map.find(sign_index); - if (iter == pronunciations.end()) { + if (iter == index_linguistic_map.end()) { if (sign_index == 0) EXPECT_EQ(signs.at(sign_index).text(), "destination1"); else if (sign_index == 1) EXPECT_EQ(signs.at(sign_index).text(), "destination2"); else - FAIL() << (iter->second).second << " Extra key. This should not happen."; - } else if ((iter->second).second == "destination:pronunciation3") { + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; + } else if (std::get(iter->second) == + "destination:pronunciation3") { EXPECT_EQ(signs.at(sign_index).text(), "destination3"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(static_cast(std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); + } else if (static_cast( + std::get(iter->second)) == + PronunciationAlphabet::kNone) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), ""); + } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; ++sign_index; } - // blank pronunciations for names. + // blank linguistics for names. std::vector types; auto names_and_types = edgeinfo.GetNamesAndTypes(true); ASSERT_EQ(names_and_types.size(), 3); - std::unordered_map> name_pronunciations = - edgeinfo.GetPronunciationsMap(); + std::unordered_map> name_linguistics = + edgeinfo.GetLinguisticMap(); uint8_t name_index = 0; for (const auto& name_and_type : names_and_types) { if (std::get<2>(name_and_type) != 0) { @@ -510,66 +547,89 @@ TEST(Standalone, PhonemesWithAltandDirection) { ++name_index; continue; } - std::unordered_map>::const_iterator iter = - name_pronunciations.find(name_index); - if (iter == name_pronunciations.end()) { - ASSERT_NE(name_index, 0); + std::unordered_map>::const_iterator iter = + name_linguistics.find(name_index); + ASSERT_NE(iter, name_linguistics.end()); + if (name_index == 1 || name_index == 2) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), ""); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kNone)); + } else { // first and last name is missing in the name field EXPECT_EQ(name_index, 0); - if ((iter->second).second == "name:pronunciation1") { - EXPECT_EQ(std::get<0>(name_and_type), "FG"); - EXPECT_EQ(static_cast((iter->second).first), + if (std::get(iter->second) == "name:pronunciation1") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "FG"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; } ++name_index; } } - // Test 2 empty pronunciations + // Test 2 empty linguistics { GraphId node_id = GH_edge->endnode(); auto tile = graph_reader.GetGraphTile(node_id); auto edgeinfo = tile->edgeinfo(GH_edge); - std::unordered_map> pronunciations; - std::vector signs = tile->GetSigns(GH_edge_id.id(), pronunciations); + std::unordered_map> index_linguistic_map; + + std::vector signs = tile->GetSigns(GH_edge_id.id(), index_linguistic_map); uint32_t sign_index = 0; ASSERT_EQ(signs.size(), 3); - ASSERT_EQ(pronunciations.size(), 1); + ASSERT_EQ(index_linguistic_map.size(), 3); for (const auto& sign : signs) { - std::unordered_map>::const_iterator iter = - pronunciations.find(sign_index); + std::unordered_map>::const_iterator iter = + index_linguistic_map.find(sign_index); - if (iter == pronunciations.end()) { + if (iter == index_linguistic_map.end()) { if (sign_index == 0) EXPECT_EQ(signs.at(sign_index).text(), "destination1"); else if (sign_index == 2) EXPECT_EQ(signs.at(sign_index).text(), "destination3"); else - FAIL() << (iter->second).second << " Extra key. This should not happen."; - } else if ((iter->second).second == "destination:pronunciation2") { + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; + } else if (std::get(iter->second) == + "destination:pronunciation2") { EXPECT_EQ(signs.at(sign_index).text(), "destination2"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(static_cast(std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); - } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + } else { + if (static_cast( + std::get(iter->second)) == + PronunciationAlphabet::kNone) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), ""); + + } else + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; + } ++sign_index; } - // blank pronunciations for names. + // blank linguistics for names. std::vector types; auto names_and_types = edgeinfo.GetNamesAndTypes(true); ASSERT_EQ(names_and_types.size(), 3); - std::unordered_map> name_pronunciations = - edgeinfo.GetPronunciationsMap(); + std::unordered_map> name_linguistics = + edgeinfo.GetLinguisticMap(); uint8_t name_index = 0; for (const auto& name_and_type : names_and_types) { if (std::get<2>(name_and_type) != 0) { @@ -577,21 +637,32 @@ TEST(Standalone, PhonemesWithAltandDirection) { ++name_index; continue; } - std::unordered_map>::const_iterator iter = - name_pronunciations.find(name_index); - if (iter == name_pronunciations.end()) { - EXPECT_EQ(name_index, 0); + std::unordered_map>::const_iterator iter = + name_linguistics.find(name_index); + ASSERT_NE(iter, name_linguistics.end()); + if (name_index == 0) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), ""); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kNone)); + } else { - if ((iter->second).second == "name:pronunciation2") { - EXPECT_EQ(std::get<0>(name_and_type), "xyz street"); - EXPECT_EQ(static_cast((iter->second).first), + if (std::get(iter->second) == "name:pronunciation2") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "xyz street"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); - } else if ((iter->second).second == "name:pronunciation3") { - EXPECT_EQ(std::get<0>(name_and_type), "abc ave"); - EXPECT_EQ(static_cast((iter->second).first), + } else if (std::get(iter->second) == + "name:pronunciation3") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "abc ave"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; } ++name_index; } @@ -604,45 +675,59 @@ TEST(Standalone, PhonemesWithAltandDirection) { auto tile = graph_reader.GetGraphTile(node_id); auto edgeinfo = tile->edgeinfo(HI_edge); - std::unordered_map> pronunciations; - std::vector signs = tile->GetSigns(HI_edge_id.id(), pronunciations); + std::unordered_map> index_linguistic_map; + + std::vector signs = tile->GetSigns(HI_edge_id.id(), index_linguistic_map); uint32_t sign_index = 0; ASSERT_EQ(signs.size(), 3); - ASSERT_EQ(pronunciations.size(), 2); + ASSERT_EQ(index_linguistic_map.size(), 3); for (const auto& sign : signs) { - std::unordered_map>::const_iterator iter = - pronunciations.find(sign_index); + std::unordered_map>::const_iterator iter = + index_linguistic_map.find(sign_index); - if (iter == pronunciations.end()) { + if (iter == index_linguistic_map.end()) { if (sign_index == 0) EXPECT_EQ(signs.at(sign_index).text(), "destination1"); else - FAIL() << (iter->second).second << " Extra key. This should not happen."; - } else if ((iter->second).second == "destination:pronunciation2") { + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; + } else if (std::get(iter->second) == + "destination:pronunciation2") { EXPECT_EQ(signs.at(sign_index).text(), "destination2"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(static_cast(std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); - } else if ((iter->second).second == "destination:pronunciation3") { + } else if (std::get(iter->second) == + "destination:pronunciation3") { EXPECT_EQ(signs.at(sign_index).text(), "destination3"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(static_cast(std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); - } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + } else { + if (static_cast( + std::get(iter->second)) == + PronunciationAlphabet::kNone) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), ""); + } else + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; + } ++sign_index; } - // blank pronunciations for names. + // blank linguistics for names. std::vector types; auto names_and_types = edgeinfo.GetNamesAndTypes(true); ASSERT_EQ(names_and_types.size(), 3); - std::unordered_map> name_pronunciations = - edgeinfo.GetPronunciationsMap(); + std::unordered_map> name_linguistics = + edgeinfo.GetLinguisticMap(); uint8_t name_index = 0; for (const auto& name_and_type : names_and_types) { if (std::get<2>(name_and_type) != 0) { @@ -650,21 +735,33 @@ TEST(Standalone, PhonemesWithAltandDirection) { ++name_index; continue; } - std::unordered_map>::const_iterator iter = - name_pronunciations.find(name_index); - if (iter == name_pronunciations.end()) { - EXPECT_EQ(name_index, 0); + std::unordered_map>::const_iterator iter = + name_linguistics.find(name_index); + ASSERT_NE(iter, name_linguistics.end()); + if (name_index == 0) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), ""); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kNone)); + } else { - if ((iter->second).second == "name:pronunciation2:x-jeita") { - EXPECT_EQ(std::get<0>(name_and_type), "xyz street"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXJeita)); - } else if ((iter->second).second == "name:pronunciation3:x-jeita") { - EXPECT_EQ(std::get<0>(name_and_type), "abc ave"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXJeita)); + if (std::get(iter->second) == + "name:pronunciation2:jeita") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "xyz street"); + EXPECT_EQ(static_cast( + std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kJeita)); + } else if (std::get(iter->second) == + "name:pronunciation3:jeita") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "abc ave"); + EXPECT_EQ(static_cast( + std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kJeita)); } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; } ++name_index; } @@ -677,44 +774,58 @@ TEST(Standalone, PhonemesWithAltandDirection) { auto tile = graph_reader.GetGraphTile(node_id); auto edgeinfo = tile->edgeinfo(IJ_edge); - std::unordered_map> pronunciations; - std::vector signs = tile->GetSigns(IJ_edge_id.id(), pronunciations); + std::unordered_map> index_linguistic_map; + + std::vector signs = tile->GetSigns(IJ_edge_id.id(), index_linguistic_map); uint32_t sign_index = 0; ASSERT_EQ(signs.size(), 3); - ASSERT_EQ(pronunciations.size(), 2); + ASSERT_EQ(index_linguistic_map.size(), 3); for (const auto& sign : signs) { - std::unordered_map>::const_iterator iter = - pronunciations.find(sign_index); + std::unordered_map>::const_iterator iter = + index_linguistic_map.find(sign_index); - if (iter == pronunciations.end()) { + if (iter == index_linguistic_map.end()) { if (sign_index == 1) EXPECT_EQ(signs.at(sign_index).text(), "destination2"); else - FAIL() << (iter->second).second << " Extra key. This should not happen."; - } else if ((iter->second).second == "destination:pronunciation1") { + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; + } else if (std::get(iter->second) == + "destination:pronunciation1") { EXPECT_EQ(signs.at(sign_index).text(), "destination1"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(static_cast(std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); - } else if ((iter->second).second == "destination:pronunciation3") { + } else if (std::get(iter->second) == + "destination:pronunciation3") { EXPECT_EQ(signs.at(sign_index).text(), "destination3"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(static_cast(std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); - } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + } else { + if (static_cast( + std::get(iter->second)) == + PronunciationAlphabet::kNone) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), ""); + } else + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; + } ++sign_index; } - // blank pronunciations for names. + // blank linguistics for names. std::vector types; auto names_and_types = edgeinfo.GetNamesAndTypes(true); ASSERT_EQ(names_and_types.size(), 1); - std::unordered_map> name_pronunciations = - edgeinfo.GetPronunciationsMap(); + std::unordered_map> name_linguistics = + edgeinfo.GetLinguisticMap(); uint8_t name_index = 0; for (const auto& name_and_type : names_and_types) { if (std::get<2>(name_and_type) != 0) { @@ -722,21 +833,32 @@ TEST(Standalone, PhonemesWithAltandDirection) { ++name_index; continue; } - std::unordered_map>::const_iterator iter = - name_pronunciations.find(name_index); - if (iter == name_pronunciations.end()) { - EXPECT_EQ(name_index, 0); + std::unordered_map>::const_iterator iter = + name_linguistics.find(name_index); + ASSERT_NE(iter, name_linguistics.end()); + if (name_index == 0) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), ""); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kNone)); } else { - if ((iter->second).second == "name:pronunciation1:x-katakana") { - EXPECT_EQ(std::get<0>(name_and_type), "xyz street"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXKatakana)); - } else if ((iter->second).second == "name:pronunciation2:x-jeita") { - EXPECT_EQ(std::get<0>(name_and_type), "abc ave"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXJeita)); + if (std::get(iter->second) == + "name:pronunciation1:katakana") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "xyz street"); + EXPECT_EQ(static_cast( + std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kKatakana)); + } else if (std::get(iter->second) == + "name:pronunciation2:jeita") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "abc ave"); + EXPECT_EQ(static_cast( + std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kJeita)); } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; } ++name_index; } @@ -749,145 +871,199 @@ TEST(Standalone, PhonemesWithAltandDirection) { auto tile = graph_reader.GetGraphTile(node_id); auto edgeinfo = tile->edgeinfo(JK_edge); - std::unordered_map> pronunciations; - std::vector signs = tile->GetSigns(JK_edge_id.id(), pronunciations); + std::unordered_map> index_linguistic_map; + + std::vector signs = tile->GetSigns(JK_edge_id.id(), index_linguistic_map); uint32_t sign_index = 0; ASSERT_EQ(signs.size(), 3); - ASSERT_EQ(pronunciations.size(), 2); + ASSERT_EQ(index_linguistic_map.size(), 3); for (const auto& sign : signs) { - std::unordered_map>::const_iterator iter = - pronunciations.find(sign_index); + std::unordered_map>::const_iterator iter = + index_linguistic_map.find(sign_index); - if (iter == pronunciations.end()) { + if (iter == index_linguistic_map.end()) { if (sign_index == 2) EXPECT_EQ(signs.at(sign_index).text(), "destination3"); else - FAIL() << (iter->second).second << " Extra key. This should not happen."; - } else if ((iter->second).second == "destination:pronunciation1") { + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; + } else if (std::get(iter->second) == + "destination:pronunciation1") { EXPECT_EQ(signs.at(sign_index).text(), "destination1"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(static_cast(std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); - } else if ((iter->second).second == "destination:pronunciation2") { + } else if (std::get(iter->second) == + "destination:pronunciation2") { EXPECT_EQ(signs.at(sign_index).text(), "destination2"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(static_cast(std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); - } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + } else { + if (static_cast( + std::get(iter->second)) == + PronunciationAlphabet::kNone) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), ""); + } else + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; + } ++sign_index; } } - // Test 2 empty pronunciations + // Test 2 empty linguistics { GraphId node_id = KL_edge->endnode(); auto tile = graph_reader.GetGraphTile(node_id); auto edgeinfo = tile->edgeinfo(KL_edge); - std::unordered_map> pronunciations; - std::vector signs = tile->GetSigns(KL_edge_id.id(), pronunciations); + std::unordered_map> index_linguistic_map; + + std::vector signs = tile->GetSigns(KL_edge_id.id(), index_linguistic_map); uint32_t sign_index = 0; ASSERT_EQ(signs.size(), 3); - ASSERT_EQ(pronunciations.size(), 1); + ASSERT_EQ(index_linguistic_map.size(), 3); for (const auto& sign : signs) { - std::unordered_map>::const_iterator iter = - pronunciations.find(sign_index); + std::unordered_map>::const_iterator iter = + index_linguistic_map.find(sign_index); - if (iter == pronunciations.end()) { + if (iter == index_linguistic_map.end()) { if (sign_index == 1) EXPECT_EQ(signs.at(sign_index).text(), "destination2"); else if (sign_index == 2) EXPECT_EQ(signs.at(sign_index).text(), "destination3"); else - FAIL() << (iter->second).second << " Extra key. This should not happen."; - } else if ((iter->second).second == "destination:pronunciation1") { + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; + } else if (std::get(iter->second) == + "destination:pronunciation1") { EXPECT_EQ(signs.at(sign_index).text(), "destination1"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(static_cast(std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); - } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + } else { + if (static_cast( + std::get(iter->second)) == + PronunciationAlphabet::kNone) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), ""); + } else + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; + } ++sign_index; } } - // Test 2 empty pronunciations and jeita wins + // Test 2 empty linguistics and jeita wins { GraphId node_id = LM_edge->endnode(); auto tile = graph_reader.GetGraphTile(node_id); auto edgeinfo = tile->edgeinfo(LM_edge); - std::unordered_map> pronunciations; - std::vector signs = tile->GetSigns(LM_edge_id.id(), pronunciations); + std::unordered_map> index_linguistic_map; + + std::vector signs = tile->GetSigns(LM_edge_id.id(), index_linguistic_map); uint32_t sign_index = 0; ASSERT_EQ(signs.size(), 3); - ASSERT_EQ(pronunciations.size(), 1); + ASSERT_EQ(index_linguistic_map.size(), 3); for (const auto& sign : signs) { - std::unordered_map>::const_iterator iter = - pronunciations.find(sign_index); + std::unordered_map>::const_iterator iter = + index_linguistic_map.find(sign_index); - if (iter == pronunciations.end()) { + if (iter == index_linguistic_map.end()) { if (sign_index == 0) EXPECT_EQ(signs.at(sign_index).text(), "destination1"); else if (sign_index == 2) EXPECT_EQ(signs.at(sign_index).text(), "destination3"); else - FAIL() << (iter->second).second << " Extra key. This should not happen."; - } else if ((iter->second).second == "destination:pronunciation2:x-jeita") { + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; + } else if (std::get(iter->second) == + "destination:pronunciation2:jeita") { EXPECT_EQ(signs.at(sign_index).text(), "destination2"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXJeita)); - } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kJeita)); + } else { + if (static_cast( + std::get(iter->second)) == + PronunciationAlphabet::kNone) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), ""); + } else + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; + } ++sign_index; } } - // Test 2 empty pronunciations. Should have katakanna and jeita + // Test 2 empty linguistics. Should have katakanna and jeita { GraphId node_id = MN_edge->endnode(); auto tile = graph_reader.GetGraphTile(node_id); auto edgeinfo = tile->edgeinfo(MN_edge); - std::unordered_map> pronunciations; - std::vector signs = tile->GetSigns(MN_edge_id.id(), pronunciations); + std::unordered_map> index_linguistic_map; + + std::vector signs = tile->GetSigns(MN_edge_id.id(), index_linguistic_map); uint32_t sign_index = 0; ASSERT_EQ(signs.size(), 3); - ASSERT_EQ(pronunciations.size(), 2); + ASSERT_EQ(index_linguistic_map.size(), 3); for (const auto& sign : signs) { - std::unordered_map>::const_iterator iter = - pronunciations.find(sign_index); + std::unordered_map>::const_iterator iter = + index_linguistic_map.find(sign_index); - if (iter == pronunciations.end()) { + if (iter == index_linguistic_map.end()) { if (sign_index == 0) EXPECT_EQ(signs.at(sign_index).text(), "destination1"); else - FAIL() << (iter->second).second << " Extra key. This should not happen."; - } else if ((iter->second).second == "destination:pronunciation3:x-katakana") { + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; + } else if (std::get(iter->second) == + "destination:pronunciation3:katakana") { EXPECT_EQ(signs.at(sign_index).text(), "destination3"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXKatakana)); - } else if ((iter->second).second == "destination:pronunciation2:x-jeita") { + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kKatakana)); + } else if (std::get(iter->second) == + "destination:pronunciation2:jeita") { EXPECT_EQ(signs.at(sign_index).text(), "destination2"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXJeita)); - } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kJeita)); + } else { + if (static_cast( + std::get(iter->second)) == + PronunciationAlphabet::kNone) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), ""); + } else + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; + } ++sign_index; } } @@ -898,11 +1074,12 @@ TEST(Standalone, PhonemesWithAltandDirection) { GraphId node_id = AB_edge->endnode(); auto tile = graph_reader.GetGraphTile(node_id); auto edgeinfo = tile->edgeinfo(AB_edge); + std::vector types; auto names_and_types = edgeinfo.GetNamesAndTypes(true); - ASSERT_EQ(names_and_types.size(), 7); + ASSERT_EQ(names_and_types.size(), 6); - std::unordered_map> pronunciations = - edgeinfo.GetPronunciationsMap(); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); uint8_t name_index = 0; for (const auto& name_and_type : names_and_types) { if (std::get<2>(name_and_type) != 0) { @@ -911,45 +1088,59 @@ TEST(Standalone, PhonemesWithAltandDirection) { continue; } - std::unordered_map>::const_iterator iter = - pronunciations.find(name_index); - if (iter == pronunciations.end()) { + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + if (iter == linguistics.end()) { // all should have a pronunctiation - EXPECT_NE(iter, pronunciations.end()); + EXPECT_NE(iter, linguistics.end()); } else { - if ((iter->second).second == "name:pronunciation:nt-sampa") { - EXPECT_EQ(std::get<0>(name_and_type), "AB"); - EXPECT_EQ(static_cast((iter->second).first), + if (std::get(iter->second) == + "name:pronunciation:nt-sampa") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "AB"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kNtSampa)); - } else if ((iter->second).second == "tunnel:name:pronunciation:nt-sampa") { - EXPECT_EQ(std::get<0>(name_and_type), "tunnel:name"); - EXPECT_EQ(static_cast((iter->second).first), + } else if (std::get(iter->second) == + "tunnel:name:pronunciation:nt-sampa") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "tunnel:name"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kNtSampa)); - } else if ((iter->second).second == + } else if (std::get(iter->second) == "int_ref:pronunciation:nt-sampa int_direction:pronunciation:nt-sampa") { - EXPECT_EQ(std::get<0>(name_and_type), "int_ref int_direction"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "int_ref int_direction"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kNtSampa)); - } else if ((iter->second).second == + } else if (std::get(iter->second) == "ref:pronunciation:nt-sampa direction:pronunciation:nt-sampa") { - EXPECT_EQ(std::get<0>(name_and_type), "ref direction"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "ref direction"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kNtSampa)); - } else if ((iter->second).second == "alt_name:pronunciation:nt-sampa") { - EXPECT_EQ(std::get<0>(name_and_type), "alt_name"); - EXPECT_EQ(static_cast((iter->second).first), + } else if (std::get(iter->second) == + "alt_name:pronunciation:nt-sampa") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "alt_name"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kNtSampa)); - } else if ((iter->second).second == "official_name:pronunciation:nt-sampa") { - EXPECT_EQ(std::get<0>(name_and_type), "official_name"); - EXPECT_EQ(static_cast((iter->second).first), + } else if (std::get(iter->second) == + "official_name:pronunciation:nt-sampa") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "official_name"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kNtSampa)); - } else if ((iter->second).second == "name:en:pronunciation:nt-sampa") { - EXPECT_EQ(std::get<0>(name_and_type), "name:en"); - EXPECT_EQ(static_cast((iter->second).first), + } else if (std::get(iter->second) == + "name:en:pronunciation:nt-sampa") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "name:en"); + + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kNtSampa)); } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; } ++name_index; } @@ -961,44 +1152,52 @@ TEST(Standalone, PhonemesWithAltandDirection) { auto tile = graph_reader.GetGraphTile(node_id); const NodeInfo* node_info = tile->node(node_id); - std::unordered_map> pronunciations; - std::vector signs = tile->GetSigns(BD_edge_id.id(), pronunciations); + std::unordered_map> index_linguistic_map; + + std::vector signs = tile->GetSigns(BD_edge_id.id(), index_linguistic_map); uint32_t sign_index = 0; ASSERT_EQ(signs.size(), 6); - ASSERT_EQ(pronunciations.size(), 6); + ASSERT_EQ(index_linguistic_map.size(), 6); for (const auto& sign : signs) { - std::unordered_map>::const_iterator iter = - pronunciations.find(sign_index); - ASSERT_NE(iter, pronunciations.end()); + std::unordered_map>::const_iterator iter = + index_linguistic_map.find(sign_index); + ASSERT_NE(iter, index_linguistic_map.end()); - if ((iter->second).second == "destination:forward:pronunciation:x-katakana") { + if (std::get(iter->second) == + "destination:forward:pronunciation:katakana") { EXPECT_EQ(signs.at(sign_index).text(), "destination:forward"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXKatakana)); - } else if ((iter->second).second == "destination:street:to:pronunciation:x-katakana") { + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kKatakana)); + } else if (std::get(iter->second) == + "destination:street:to:pronunciation:katakana") { EXPECT_EQ(signs.at(sign_index).text(), "destination:street:to"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXKatakana)); - } else if ((iter->second).second == "destination:ref:to:pronunciation:x-katakana") { + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kKatakana)); + } else if (std::get(iter->second) == + "destination:ref:to:pronunciation:katakana") { EXPECT_EQ(signs.at(sign_index).text(), "destination:ref:to"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXKatakana)); - } else if ((iter->second).second == "destination:ref:pronunciation:x-katakana") { + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kKatakana)); + } else if (std::get(iter->second) == + "destination:ref:pronunciation:katakana") { EXPECT_EQ(signs.at(sign_index).text(), "destination:ref"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXKatakana)); - } else if ((iter->second).second == "destination:street:pronunciation:x-katakana") { + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kKatakana)); + } else if (std::get(iter->second) == + "destination:street:pronunciation:katakana") { EXPECT_EQ(signs.at(sign_index).text(), "destination:street"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXKatakana)); - } else if ((iter->second).second == "junction:ref:pronunciation:x-katakana") { + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kKatakana)); + } else if (std::get(iter->second) == + "junction:ref:pronunciation:katakana") { EXPECT_EQ(signs.at(sign_index).text(), "junction:ref"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXKatakana)); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kKatakana)); } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; ++sign_index; } @@ -1007,45 +1206,52 @@ TEST(Standalone, PhonemesWithAltandDirection) { tile = graph_reader.GetGraphTile(node_id); node_info = tile->node(node_id); - pronunciations.clear(); + index_linguistic_map.clear(); signs.clear(); - signs = tile->GetSigns(DB_edge_id.id(), pronunciations); + signs = tile->GetSigns(DB_edge_id.id(), index_linguistic_map); sign_index = 0; ASSERT_EQ(signs.size(), 6); - ASSERT_EQ(pronunciations.size(), 6); + ASSERT_EQ(index_linguistic_map.size(), 6); for (const auto& sign : signs) { - std::unordered_map>::const_iterator iter = - pronunciations.find(sign_index); - ASSERT_NE(iter, pronunciations.end()); + std::unordered_map>::const_iterator iter = + index_linguistic_map.find(sign_index); + ASSERT_NE(iter, index_linguistic_map.end()); - if ((iter->second).second == "destination:backward:pronunciation:x-katakana") { + if (std::get(iter->second) == + "destination:backward:pronunciation:katakana") { EXPECT_EQ(signs.at(sign_index).text(), "destination:backward"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXKatakana)); - } else if ((iter->second).second == "destination:street:to:pronunciation:x-katakana") { + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kKatakana)); + } else if (std::get(iter->second) == + "destination:street:to:pronunciation:katakana") { EXPECT_EQ(signs.at(sign_index).text(), "destination:street:to"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXKatakana)); - } else if ((iter->second).second == "destination:ref:to:pronunciation:x-katakana") { + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kKatakana)); + } else if (std::get(iter->second) == + "destination:ref:to:pronunciation:katakana") { EXPECT_EQ(signs.at(sign_index).text(), "destination:ref:to"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXKatakana)); - } else if ((iter->second).second == "destination:ref:pronunciation:x-katakana") { + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kKatakana)); + } else if (std::get(iter->second) == + "destination:ref:pronunciation:katakana") { EXPECT_EQ(signs.at(sign_index).text(), "destination:ref"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXKatakana)); - } else if ((iter->second).second == "destination:street:pronunciation:x-katakana") { + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kKatakana)); + } else if (std::get(iter->second) == + "destination:street:pronunciation:katakana") { EXPECT_EQ(signs.at(sign_index).text(), "destination:street"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXKatakana)); - } else if ((iter->second).second == "junction:ref:pronunciation:x-katakana") { + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kKatakana)); + } else if (std::get(iter->second) == + "junction:ref:pronunciation:katakana") { EXPECT_EQ(signs.at(sign_index).text(), "junction:ref"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXKatakana)); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kKatakana)); } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; ++sign_index; } @@ -1055,45 +1261,52 @@ TEST(Standalone, PhonemesWithAltandDirection) { tile = graph_reader.GetGraphTile(node_id); node_info = tile->node(node_id); - pronunciations.clear(); + index_linguistic_map.clear(); signs.clear(); - signs = tile->GetSigns(BC_edge_id.id(), pronunciations); + signs = tile->GetSigns(BC_edge_id.id(), index_linguistic_map); sign_index = 0; ASSERT_EQ(signs.size(), 6); - ASSERT_EQ(pronunciations.size(), 6); + ASSERT_EQ(index_linguistic_map.size(), 6); for (const auto& sign : signs) { - std::unordered_map>::const_iterator iter = - pronunciations.find(sign_index); - ASSERT_NE(iter, pronunciations.end()); + std::unordered_map>::const_iterator iter = + index_linguistic_map.find(sign_index); + ASSERT_NE(iter, index_linguistic_map.end()); - if ((iter->second).second == "destination:forward:pronunciation:x-jeita") { + if (std::get(iter->second) == + "destination:forward:pronunciation:jeita") { EXPECT_EQ(signs.at(sign_index).text(), "destination:forward"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXJeita)); - } else if ((iter->second).second == "destination:street:to:pronunciation:x-jeita") { + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kJeita)); + } else if (std::get(iter->second) == + "destination:street:to:pronunciation:jeita") { EXPECT_EQ(signs.at(sign_index).text(), "destination:street:to"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXJeita)); - } else if ((iter->second).second == "destination:ref:to:pronunciation:x-jeita") { + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kJeita)); + } else if (std::get(iter->second) == + "destination:ref:to:pronunciation:jeita") { EXPECT_EQ(signs.at(sign_index).text(), "destination:ref:to"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXJeita)); - } else if ((iter->second).second == "destination:ref:pronunciation:x-jeita") { + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kJeita)); + } else if (std::get(iter->second) == + "destination:ref:pronunciation:jeita") { EXPECT_EQ(signs.at(sign_index).text(), "destination:ref"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXJeita)); - } else if ((iter->second).second == "destination:street:pronunciation:x-jeita") { + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kJeita)); + } else if (std::get(iter->second) == + "destination:street:pronunciation:jeita") { EXPECT_EQ(signs.at(sign_index).text(), "destination:street"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXJeita)); - } else if ((iter->second).second == "junction:ref:pronunciation:x-jeita") { + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kJeita)); + } else if (std::get(iter->second) == + "junction:ref:pronunciation:jeita") { EXPECT_EQ(signs.at(sign_index).text(), "junction:ref"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXJeita)); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kJeita)); } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; ++sign_index; } @@ -1106,10 +1319,10 @@ TEST(Standalone, PhonemesWithAltandDirection) { auto edgeinfo = tile->edgeinfo(BC_edge); std::vector types; auto names_and_types = edgeinfo.GetNamesAndTypes(true); - ASSERT_EQ(names_and_types.size(), 4); + ASSERT_EQ(names_and_types.size(), 3); - std::unordered_map> pronunciations = - edgeinfo.GetPronunciationsMap(); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); uint8_t name_index = 0; for (const auto& name_and_type : names_and_types) { if (std::get<2>(name_and_type) != 0) { @@ -1117,28 +1330,37 @@ TEST(Standalone, PhonemesWithAltandDirection) { ++name_index; continue; } - std::unordered_map>::const_iterator iter = - pronunciations.find(name_index); - ASSERT_NE(iter, pronunciations.end()); - - if ((iter->second).second == "name:pronunciation") { - EXPECT_EQ(std::get<0>(name_and_type), "BC"); - EXPECT_EQ(static_cast((iter->second).first), + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + + if (std::get(iter->second) == "name:pronunciation") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "BC"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); - } else if ((iter->second).second == "alt_name:pronunciation:nt-sampa") { - EXPECT_EQ(std::get<0>(name_and_type), "alt_name"); - EXPECT_EQ(static_cast((iter->second).first), + } else if (std::get(iter->second) == + "alt_name:pronunciation:nt-sampa") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "alt_name"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kNtSampa)); - } else if ((iter->second).second == "official_name:pronunciation:x-jeita") { - EXPECT_EQ(std::get<0>(name_and_type), "official_name"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXJeita)); - } else if ((iter->second).second == "name:en:pronunciation:x-katakana") { - EXPECT_EQ(std::get<0>(name_and_type), "name:en"); - EXPECT_EQ(static_cast((iter->second).first), - static_cast(baldr::PronunciationAlphabet::kXKatakana)); + } else if (std::get(iter->second) == + "official_name:pronunciation:jeita") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "official_name"); + EXPECT_EQ(static_cast( + std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kJeita)); + } else if (std::get(iter->second) == + "name:en:pronunciation:katakana") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "name:en"); + + EXPECT_EQ(static_cast( + std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kKatakana)); } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; ++name_index; } @@ -1149,43 +1371,47 @@ TEST(Standalone, PhonemesWithAltandDirection) { graph_reader.GetGraphTile(node_id); node_info = tile->node(node_id); - pronunciations.clear(); + index_linguistic_map.clear(); signs.clear(); - signs = tile->GetSigns(DE_edge_id.id(), pronunciations); + signs = tile->GetSigns(DE_edge_id.id(), index_linguistic_map); sign_index = 0; ASSERT_EQ(signs.size(), 2); - ASSERT_EQ(pronunciations.size(), 2); + ASSERT_EQ(index_linguistic_map.size(), 2); for (const auto& sign : signs) { - std::unordered_map>::const_iterator iter = - pronunciations.find(sign_index); - ASSERT_NE(iter, pronunciations.end()); + std::unordered_map>::const_iterator iter = + index_linguistic_map.find(sign_index); + ASSERT_NE(iter, index_linguistic_map.end()); - if ((iter->second).second == "destination:pronunciation1") { + if (std::get(iter->second) == + "destination:pronunciation1") { EXPECT_EQ(signs.at(sign_index).text(), "destination1"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(static_cast(std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); - } else if ((iter->second).second == "destination:pronunciation2") { + } else if (std::get(iter->second) == + "destination:pronunciation2") { EXPECT_EQ(signs.at(sign_index).text(), "destination2"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(static_cast(std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; ++sign_index; } - // blank pronunciations for names. + // blank linguistics for names. { auto edgeinfo = tile->edgeinfo(DE_edge); + std::vector types; auto names_and_types = edgeinfo.GetNamesAndTypes(true); ASSERT_EQ(names_and_types.size(), 3); - std::unordered_map> pronunciations = - edgeinfo.GetPronunciationsMap(); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); uint8_t name_index = 0; for (const auto& name_and_type : names_and_types) { if (std::get<2>(name_and_type) != 0) { @@ -1193,18 +1419,29 @@ TEST(Standalone, PhonemesWithAltandDirection) { ++name_index; continue; } - std::unordered_map>::const_iterator iter = - pronunciations.find(name_index); - if (iter == pronunciations.end()) { - ASSERT_NE(name_index, 2); + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + if (name_index == 0 || name_index == 1) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), ""); + EXPECT_EQ(static_cast( + std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kNone)); + } else { EXPECT_EQ(name_index, 2); - if ((iter->second).second == "name:pronunciation3") { - EXPECT_EQ(std::get<0>(name_and_type), "abc ave"); - EXPECT_EQ(static_cast((iter->second).first), + if (std::get(iter->second) == + "name:pronunciation3") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "abc ave"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; } ++name_index; } @@ -1215,29 +1452,32 @@ TEST(Standalone, PhonemesWithAltandDirection) { graph_reader.GetGraphTile(node_id); node_info = tile->node(node_id); - pronunciations.clear(); + index_linguistic_map.clear(); signs.clear(); - signs = tile->GetSigns(ED_edge_id.id(), pronunciations); + signs = tile->GetSigns(ED_edge_id.id(), index_linguistic_map); sign_index = 0; ASSERT_EQ(signs.size(), 2); - ASSERT_EQ(pronunciations.size(), 2); + ASSERT_EQ(index_linguistic_map.size(), 2); for (const auto& sign : signs) { - std::unordered_map>::const_iterator iter = - pronunciations.find(sign_index); - ASSERT_NE(iter, pronunciations.end()); + std::unordered_map>::const_iterator iter = + index_linguistic_map.find(sign_index); + ASSERT_NE(iter, index_linguistic_map.end()); - if ((iter->second).second == "destination:pronunciation1") { + if (std::get(iter->second) == + "destination:pronunciation1") { EXPECT_EQ(signs.at(sign_index).text(), "destination1"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(static_cast(std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); - } else if ((iter->second).second == "destination:pronunciation2") { + } else if (std::get(iter->second) == + "destination:pronunciation2") { EXPECT_EQ(signs.at(sign_index).text(), "destination2"); - EXPECT_EQ(static_cast((iter->second).first), + EXPECT_EQ(static_cast(std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kIpa)); } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; ++sign_index; } @@ -1281,11 +1521,12 @@ TEST(Standalone, PhonemesWithNoAltandDirection) { GraphId node_id = AB_edge->endnode(); auto tile = graph_reader.GetGraphTile(node_id); auto edgeinfo = tile->edgeinfo(AB_edge); + std::vector types; auto names_and_types = edgeinfo.GetNamesAndTypes(true); - ASSERT_EQ(names_and_types.size(), 6); + ASSERT_EQ(names_and_types.size(), 5); - std::unordered_map> pronunciations = - edgeinfo.GetPronunciationsMap(); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); uint8_t name_index = 0; for (const auto& name_and_type : names_and_types) { if (std::get<2>(name_and_type) != 0) { @@ -1294,39 +1535,52 @@ TEST(Standalone, PhonemesWithNoAltandDirection) { continue; } - std::unordered_map>::const_iterator iter = - pronunciations.find(name_index); + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); - if (iter == pronunciations.end()) { + if (iter == linguistics.end()) { // all should have a pronunciation - EXPECT_NE(iter, pronunciations.end()); + EXPECT_NE(iter, linguistics.end()); } else { - if ((iter->second).second == "name:pronunciation:nt-sampa") { - EXPECT_EQ(std::get<0>(name_and_type), "AB"); - EXPECT_EQ(static_cast((iter->second).first), + if (std::get(iter->second) == + "name:pronunciation:nt-sampa") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "AB"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kNtSampa)); - } else if ((iter->second).second == "tunnel:name:pronunciation:nt-sampa") { - EXPECT_EQ(std::get<0>(name_and_type), "tunnel:name"); - EXPECT_EQ(static_cast((iter->second).first), + } else if (std::get(iter->second) == + "tunnel:name:pronunciation:nt-sampa") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "tunnel:name"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kNtSampa)); - } else if ((iter->second).second == "int_ref:pronunciation:nt-sampa") { - EXPECT_EQ(std::get<0>(name_and_type), "int_ref"); - EXPECT_EQ(static_cast((iter->second).first), + } else if (std::get(iter->second) == + "int_ref:pronunciation:nt-sampa") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "int_ref"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kNtSampa)); - } else if ((iter->second).second == "ref:pronunciation:nt-sampa") { - EXPECT_EQ(std::get<0>(name_and_type), "ref"); - EXPECT_EQ(static_cast((iter->second).first), + } else if (std::get(iter->second) == + "ref:pronunciation:nt-sampa") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "ref"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kNtSampa)); - } else if ((iter->second).second == "official_name:pronunciation:nt-sampa") { - EXPECT_EQ(std::get<0>(name_and_type), "official_name"); - EXPECT_EQ(static_cast((iter->second).first), + } else if (std::get(iter->second) == + "official_name:pronunciation:nt-sampa") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "official_name"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kNtSampa)); - } else if ((iter->second).second == "name:en:pronunciation:nt-sampa") { - EXPECT_EQ(std::get<0>(name_and_type), "name:en"); - EXPECT_EQ(static_cast((iter->second).first), + } else if (std::get(iter->second) == + "name:en:pronunciation:nt-sampa") { + EXPECT_EQ(std::get<0>(names_and_types.at(name_index)), "name:en"); + EXPECT_EQ(static_cast( + std::get(iter->second)), static_cast(baldr::PronunciationAlphabet::kNtSampa)); } else - FAIL() << (iter->second).second << " Extra key. This should not happen."; + FAIL() << std::get(iter->second) + << " Extra key. This should not happen."; } ++name_index; } diff --git a/test/gurka/test_phonemes_w_langs.cc b/test/gurka/test_phonemes_w_langs.cc new file mode 100644 index 0000000000..9caf334c9a --- /dev/null +++ b/test/gurka/test_phonemes_w_langs.cc @@ -0,0 +1,2212 @@ +#include "baldr/graphreader.h" +#include "mjolnir/util.h" + +#include "gurka.h" +#include "test/test.h" + +#if !defined(VALHALLA_SOURCE_DIR) +#define VALHALLA_SOURCE_DIR +#endif + +using namespace valhalla; +using namespace valhalla::baldr; +using namespace valhalla::gurka; +using namespace valhalla::mjolnir; + +const std::string workdir = "test/data/gurka_phonemes_w_langs"; +const auto pbf_filename = workdir + "/map.pbf"; +const std::vector input_files = {workdir + "/map.pbf"}; +const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}; +constexpr double gridsize = 100; + +void CreateWorkdir() { + if (filesystem::is_directory(workdir)) { + filesystem::remove_all(workdir); + } + + if (!filesystem::exists(workdir)) { + bool created = filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } +} + +// parameterized test class to test all the different types of pronunciations +class PhonemesWithLangsTest + : public ::testing::TestWithParam> { +protected: + static gurka::ways ways; + static std::string ascii_map; + static std::map layout; + static boost::property_tree::ptree pt; + static void SetUpTestSuite() { + ascii_map = R"( + B----C + )"; + + valhalla::gurka::map map; + pt = map.config; + pt.put("mjolnir.data_processing.allow_alt_name", "true"); + pt.put("mjolnir.data_processing.use_admin_db", "true"); + pt.put("mjolnir.tile_dir", workdir + "/tiles"); + pt.put("mjolnir.admin", sqlite); + } +}; +gurka::ways PhonemesWithLangsTest::ways = {}; +std::string PhonemesWithLangsTest::ascii_map = {}; +std::map PhonemesWithLangsTest::layout = {}; +boost::property_tree::ptree PhonemesWithLangsTest::pt = {}; + +TEST_P(PhonemesWithLangsTest, Names) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"name", "Chaussée de Gand - Steenweg op Gent/Gentsesteenweg"}, + {"name:fr", "Chaussée de Gand"}, + {"name:left", "Chaussée de Gand - Gentsesteenweg"}, + {"name:left:nl", "Gentsesteenweg"}, + {"name:right", "Chaussée de Gand - Steenweg op Gent"}, + {"name:right:nl", "Steenweg op Gent"}, + + {"name" + param_tag, "Chaussée de Gand P - Steenweg op Gent P/Gentsesteenweg P"}, + {"name:fr" + param_tag, "Chaussée de Gand P"}, + {"name:left" + param_tag, "Chaussée de Gand P - Gentsesteenweg P"}, + {"name:left:nl" + param_tag, "Gentsesteenweg P"}, + {"name:right" + param_tag, "Chaussée de Gand P - Steenweg op Gent P"}, + {"name:right:nl" + param_tag, "Steenweg op Gent P"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {4.3516970, 50.8465573}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + { + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(BC_edge); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + + ASSERT_EQ(names_and_types.size(), 2); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + if (std::get<2>(name_and_type) != 0) { + // Skip the tagged names + ++name_index; + continue; + } + + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + + if (name_index == 0) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "fr"); + EXPECT_EQ(std::get(iter->second), + "Chaussée de Gand P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (name_index == 1) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), + "Steenweg op Gent P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++name_index; + } + } + + { + GraphId node_id = CB_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(CB_edge); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + + ASSERT_EQ(names_and_types.size(), 2); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + if (std::get<2>(name_and_type) != 0) { + // Skip the tagged names + ++name_index; + continue; + } + + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + if (name_index == 0) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "fr"); + EXPECT_EQ(std::get(iter->second), + "Chaussée de Gand P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (name_index == 1) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), "Gentsesteenweg P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++name_index; + } + } +} + +TEST_P(PhonemesWithLangsTest, Alts) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"alt_name", "Chaussée de Gand - Steenweg op Gent/Gentsesteenweg"}, + {"alt_name:fr", "Chaussée de Gand"}, + {"alt_name:left", "Chaussée de Gand - Gentsesteenweg"}, + {"alt_name:left:nl", "Gentsesteenweg"}, + {"alt_name:right", "Chaussée de Gand - Steenweg op Gent"}, + {"alt_name:right:nl", "Steenweg op Gent"}, + + {"alt_name" + param_tag, "Chaussée de Gand P - Steenweg op Gent P/Gentsesteenweg P"}, + {"alt_name:fr" + param_tag, "Chaussée de Gand P"}, + {"alt_name:left" + param_tag, "Chaussée de Gand P - Gentsesteenweg P"}, + {"alt_name:left:nl" + param_tag, "Gentsesteenweg P"}, + {"alt_name:right" + param_tag, "Chaussée de Gand P - Steenweg op Gent P"}, + {"alt_name:right:nl" + param_tag, "Steenweg op Gent P"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {4.3516970, 50.8465573}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + { + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(BC_edge); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + + ASSERT_EQ(names_and_types.size(), 3); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + if (std::get<2>(name_and_type) != 0) { + // Skip the tagged names + ++name_index; + continue; + } + + if (name_index == 0) { + ++name_index; + continue; + } + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + + if (name_index == 1) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "fr"); + EXPECT_EQ(std::get(iter->second), + "Chaussée de Gand P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + + } else if (name_index == 2) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), + "Steenweg op Gent P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++name_index; + } + } + + { + GraphId node_id = CB_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(CB_edge); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + + ASSERT_EQ(names_and_types.size(), 3); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + if (std::get<2>(name_and_type) != 0) { + // Skip the tagged names + ++name_index; + continue; + } + + if (name_index == 0) { + ++name_index; + continue; + } + + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + if (name_index == 1) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "fr"); + EXPECT_EQ(std::get(iter->second), + "Chaussée de Gand P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + + } else if (name_index == 2) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), "Gentsesteenweg P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + + ++name_index; + } + } +} + +TEST_P(PhonemesWithLangsTest, Official) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"official_name", "Chaussée de Gand - Steenweg op Gent/Gentsesteenweg"}, + {"official_name:fr", "Chaussée de Gand"}, + {"official_name:left", "Chaussée de Gand - Gentsesteenweg"}, + {"official_name:left:nl", "Gentsesteenweg"}, + {"official_name:right", "Chaussée de Gand - Steenweg op Gent"}, + {"official_name:right:nl", "Steenweg op Gent"}, + + {"official_name" + param_tag, "Chaussée de Gand P - Steenweg op Gent P/Gentsesteenweg P"}, + {"official_name:fr" + param_tag, "Chaussée de Gand P"}, + {"official_name:left" + param_tag, "Chaussée de Gand P - Gentsesteenweg P"}, + {"official_name:left:nl" + param_tag, "Gentsesteenweg P"}, + {"official_name:right" + param_tag, "Chaussée de Gand P - Steenweg op Gent P"}, + {"official_name:right:nl" + param_tag, "Steenweg op Gent P"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {4.3516970, 50.8465573}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + { + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(BC_edge); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + + ASSERT_EQ(names_and_types.size(), 3); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + if (std::get<2>(name_and_type) != 0) { + // Skip the tagged names + ++name_index; + continue; + } + + if (name_index == 0) { + ++name_index; + continue; + } + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + + if (name_index == 1) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "fr"); + EXPECT_EQ(std::get(iter->second), + "Chaussée de Gand P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (name_index == 2) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), + "Steenweg op Gent P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++name_index; + } + } + + { + GraphId node_id = CB_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(CB_edge); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + ASSERT_EQ(names_and_types.size(), 3); + + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + if (std::get<2>(name_and_type) != 0) { + // Skip the tagged names + ++name_index; + continue; + } + + if (name_index == 0) { + ++name_index; + continue; + } + + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + if (name_index == 1) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "fr"); + EXPECT_EQ(std::get(iter->second), + "Chaussée de Gand P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + + } else if (name_index == 2) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), "Gentsesteenweg P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++name_index; + } + } +} + +TEST_P(PhonemesWithLangsTest, Tunnel) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"tunnel:name", "Chaussée de Gand - Steenweg op Gent/Gentsesteenweg"}, + {"tunnel:name:fr", "Chaussée de Gand"}, + {"tunnel:name:left", "Chaussée de Gand - Gentsesteenweg"}, + {"tunnel:name:left:nl", "Gentsesteenweg"}, + {"tunnel:name:right", "Chaussée de Gand - Steenweg op Gent"}, + {"tunnel:name:right:nl", "Steenweg op Gent"}, + + {"tunnel:name" + param_tag, "Chaussée de Gand P - Steenweg op Gent P/Gentsesteenweg P"}, + {"tunnel:name:fr" + param_tag, "Chaussée de Gand P"}, + {"tunnel:name:left" + param_tag, "Chaussée de Gand P - Gentsesteenweg P"}, + {"tunnel:name:left:nl" + param_tag, "Gentsesteenweg P"}, + {"tunnel:name:right" + param_tag, "Chaussée de Gand P - Steenweg op Gent P"}, + {"tunnel:name:right:nl" + param_tag, "Steenweg op Gent P"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {4.3516970, 50.8465573}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + { + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(BC_edge); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + + ASSERT_EQ(names_and_types.size(), 3); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + if (std::get<2>(name_and_type) == 0) { + // Skip the non tagged names + ++name_index; + continue; + } + + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + + if (name_index == 1) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "fr"); + EXPECT_EQ(std::get(iter->second), + "Chaussée de Gand P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + + } else if (name_index == 2) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), + "Steenweg op Gent P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++name_index; + } + } + + { + GraphId node_id = CB_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(CB_edge); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + + ASSERT_EQ(names_and_types.size(), 3); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + if (std::get<2>(name_and_type) == 0) { + // Skip the non tagged names + ++name_index; + continue; + } + + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + if (name_index == 1) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "fr"); + EXPECT_EQ(std::get(iter->second), + "Chaussée de Gand P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (name_index == 2) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), "Gentsesteenweg P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++name_index; + } + } +} + +TEST_P(PhonemesWithLangsTest, NamesFB) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"name", "Gentsesteenweg;Chaussée de Gand"}, + {"name:forward", "Gentsesteenweg"}, + {"name:forward:nl", "Gentsesteenweg"}, + {"name:backward", "Chaussée de Gand"}, + {"name:backward:fr", "Chaussée de Gand"}, + + {"name" + param_tag, "Gentsesteenweg P;Chaussée de Gand P"}, + {"name:forward" + param_tag, "Gentsesteenweg P"}, + {"name:forward:nl" + param_tag, "Gentsesteenweg P"}, + {"name:backward" + param_tag, "Chaussée de Gand P"}, + {"name:backward:fr" + param_tag, "Chaussée de Gand P"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {4.3516970, 50.8465573}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + { + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(BC_edge); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + + ASSERT_EQ(names_and_types.size(), 1); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + if (std::get<2>(name_and_type) != 0) { + // Skip the tagged names + ++name_index; + continue; + } + + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + if (name_index == 0) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), "Gentsesteenweg P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++name_index; + } + } + + { + GraphId node_id = CB_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(CB_edge); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + + ASSERT_EQ(names_and_types.size(), 1); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + if (std::get<2>(name_and_type) != 0) { + // Skip the tagged names + ++name_index; + continue; + } + + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + if (name_index == 0) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "fr"); + EXPECT_EQ(std::get(iter->second), + "Chaussée de Gand P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++name_index; + } + } +} + +TEST_P(PhonemesWithLangsTest, RefLR) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"ref", "10;11"}, + {"ref:right", "10"}, + {"ref:right:nl", "10"}, + {"ref:left", "11"}, + {"ref:left:fr", "11"}, + + {"ref" + param_tag, "10 P;11 P"}, + {"ref:right" + param_tag, "10 P"}, + {"ref:right:nl" + param_tag, "10 P"}, + {"ref:left" + param_tag, "11 P"}, + {"ref:left:fr" + param_tag, "11 P"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {4.3516970, 50.8465573}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + { + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(BC_edge); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + + ASSERT_EQ(names_and_types.size(), 2); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + if (std::get<2>(name_and_type) != 0) { + // Skip the tagged names + ++name_index; + continue; + } + + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + if (name_index == 0) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), "10 P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + break; + } + } + + { + GraphId node_id = CB_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(CB_edge); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + + ASSERT_EQ(names_and_types.size(), 2); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + if (std::get<2>(name_and_type) != 0) { + // Skip the tagged names + ++name_index; + continue; + } + + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + if (name_index == 0) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "fr"); + EXPECT_EQ(std::get(iter->second), "11 P"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + break; + } + } +} + +TEST_P(PhonemesWithLangsTest, Destinations) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"ref", "10"}, + {"destination", "destination:lang:nl"}, + // {"destination:lang:nl", "destination:lang:nl"}, not needed as lang is in next tag + {"destination:lang:nl" + param_tag, "destination:lang:nl:pronunciation"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {4.3516970, 50.8465573}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(BC_edge_id.id(), linguistics); + + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 1); + ASSERT_EQ(linguistics.size(), 1); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "destination:lang:nl"); + EXPECT_EQ(to_string( + static_cast(std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), + "destination:lang:nl:pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++sign_index; + } +} + +TEST_P(PhonemesWithLangsTest, DestinationStreet) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = { + {"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"ref", "10"}, + {"destination:street", "destination:lang:nl"}, + //{"destination:street:lang:nl", "destination:lang:nl"}, //not needed as lang is in next tag + {"destination:street:lang:nl" + param_tag, "destination:lang:nl:pronunciation"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {4.3516970, 50.8465573}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(BC_edge_id.id(), linguistics); + + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 1); + ASSERT_EQ(linguistics.size(), 1); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "destination:lang:nl"); + EXPECT_EQ(to_string( + static_cast(std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), + "destination:lang:nl:pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++sign_index; + } +} + +TEST_P(PhonemesWithLangsTest, DestinationStreetTo) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"ref", "10"}, + {"destination:street:to", "destination:lang:nl"}, + //{"destination:street:to:lang:nl", "destination:lang:nl"}, //not needed + // as lang is in next tag + {"destination:street:to:lang:nl" + param_tag, "destination:lang:nl:pronunciation"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {4.3516970, 50.8465573}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(BC_edge_id.id(), linguistics); + + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 1); + ASSERT_EQ(linguistics.size(), 1); + + for (const auto& sign : signs) { + + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "destination:lang:nl"); + EXPECT_EQ(to_string( + static_cast(std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), + "destination:lang:nl:pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++sign_index; + } +} + +TEST_P(PhonemesWithLangsTest, DestinationsFB) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = { + {"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"ref", "10"}, + {"destination:forward", "destination:forward:nl"}, + {"destination:backward", "destination:backward:fr"}, + {"destination:forward" + param_tag, + "destination:forward:lang:nl:pronunciation"}, // this is really optional + {"destination:backward" + param_tag, + "destination:backward:lang:fr:pronunciation"}, // this is really optional + {"destination:forward:lang:nl" + param_tag, "destination:forward:lang:nl:pronunciation"}, + {"destination:backward:lang:fr" + param_tag, "destination:backward:lang:fr:pronunciation"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {4.3516970, 50.8465573}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + { + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(BC_edge_id.id(), linguistics); + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 1); + ASSERT_EQ(linguistics.size(), 1); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "destination:forward:nl"); + + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), + "destination:forward:lang:nl:pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++sign_index; + } + } + + { + { + GraphId node_id = CB_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(CB_edge_id.id(), linguistics); + + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 1); + ASSERT_EQ(linguistics.size(), 1); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "destination:backward:fr"); + + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "fr"); + EXPECT_EQ(std::get(iter->second), + "destination:backward:lang:fr:pronunciation"); + EXPECT_EQ(static_cast( + std::get(iter->second)), + static_cast(param_alphabet)); + } + ++sign_index; + } + } + } +} + +TEST_P(PhonemesWithLangsTest, DestinationsMultiLangs) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"ref", "10"}, + {"destination:lang:nl", "destination in dutch"}, + {"destination:lang:fr", "destination in french"}, + {"destination:lang:nl" + param_tag, "destination pronunciation in dutch"}, + {"destination:lang:fr" + param_tag, "destination pronunciation in french"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {4.34999, 50.84643}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + { + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(BC_edge_id.id(), linguistics); + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 2); + ASSERT_EQ(linguistics.size(), 2); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "destination in french"); + + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "fr"); + EXPECT_EQ(std::get(iter->second), + "destination pronunciation in french"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (sign_index == 1) { + EXPECT_EQ(signs.at(sign_index).text(), "destination in dutch"); + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), + "destination pronunciation in dutch"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++sign_index; + } + } + + { + { + GraphId node_id = CB_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(CB_edge_id.id(), linguistics); + + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 2); + ASSERT_EQ(linguistics.size(), 2); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "destination in french"); + + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "fr"); + EXPECT_EQ(std::get(iter->second), + "destination pronunciation in french"); + EXPECT_EQ(static_cast( + std::get(iter->second)), + static_cast(param_alphabet)); + } else if (sign_index == 1) { + EXPECT_EQ(signs.at(sign_index).text(), "destination in dutch"); + + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), + "destination pronunciation in dutch"); + EXPECT_EQ(static_cast( + std::get(iter->second)), + static_cast(param_alphabet)); + } + ++sign_index; + } + } + } +} + +TEST_P(PhonemesWithLangsTest, DestinationsFBNoOptionalTag) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = { + {"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"ref", "10"}, + {"destination:forward", "destination:forward:nl"}, + {"destination:backward", "destination:backward:fr"}, + //{"destination:forward" + param_tag, + //"destination:forward:lang:nl:pronunciation"}, //this is really optional + //{"destination:backward" + param_tag, + //"destination:backward:lang:fr:pronunciation"}, //this is really optional + {"destination:forward:lang:nl" + param_tag, "destination:forward:lang:nl:pronunciation"}, + {"destination:backward:lang:fr" + param_tag, "destination:backward:lang:fr:pronunciation"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {4.3516970, 50.8465573}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + { + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(BC_edge_id.id(), linguistics); + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 1); + ASSERT_EQ(linguistics.size(), 1); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "destination:forward:nl"); + + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), + "destination:forward:lang:nl:pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++sign_index; + } + } + + { + { + GraphId node_id = CB_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(CB_edge_id.id(), linguistics); + + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 1); + ASSERT_EQ(linguistics.size(), 1); + + for (const auto& sign : signs) { + + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "destination:backward:fr"); + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "fr"); + EXPECT_EQ(std::get(iter->second), + "destination:backward:lang:fr:pronunciation"); + EXPECT_EQ(static_cast( + std::get(iter->second)), + static_cast(param_alphabet)); + } + ++sign_index; + } + } + } +} + +TEST_P(PhonemesWithLangsTest, DestinationRef) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"ref", "10"}, + {"destination:ref", "destination:lang:nl"}, + //{"destination:ref:lang:nl", "destination:lang:nl"}, //not needed as lang is in next tag + {"destination:ref:lang:nl" + param_tag, "destination:lang:nl:pronunciation"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {4.3516970, 50.8465573}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(BC_edge_id.id(), linguistics); + + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 1); + ASSERT_EQ(linguistics.size(), 1); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "destination:lang:nl"); + EXPECT_EQ(to_string( + static_cast(std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), + "destination:lang:nl:pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++sign_index; + } +} + +TEST_P(PhonemesWithLangsTest, DestinationRefTo) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = { + {"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"ref", "10"}, + {"destination:ref:to", "destination:lang:nl"}, + //{"destination:ref:to:lang:nl", "destination:lang:nl"}, //not needed as lang is in next tag + {"destination:ref:to:lang:nl" + param_tag, "destination:lang:nl:pronunciation"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {4.3516970, 50.8465573}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(BC_edge_id.id(), linguistics); + + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 1); + ASSERT_EQ(linguistics.size(), 1); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "destination:lang:nl"); + EXPECT_EQ(to_string( + static_cast(std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), + "destination:lang:nl:pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++sign_index; + } +} + +TEST_P(PhonemesWithLangsTest, DestinationJunctionRef) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "trunk_link"}, + {"osm_id", "101"}, + {"ref", "10"}, + {"junction:ref", "junction:nl"}, + //{"junction:ref:nl", "junction:ref:nl"}, //not needed as lang is in next tag + {"junction:ref:nl" + param_tag, "junction:nl:pronunciation"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {4.3516970, 50.8465573}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(BC_edge_id.id(), linguistics); + + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 1); + ASSERT_EQ(linguistics.size(), 1); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "junction:nl"); + EXPECT_EQ(to_string( + static_cast(std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), + "junction:nl:pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++sign_index; + } +} + +TEST_P(PhonemesWithLangsTest, NodeRef) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", {{"highway", "trunk_link"}, {"osm_id", "101"}}}}; + + const gurka::nodes nodes = {{"B", + {{"ref", "node ref"}, + {"highway", "motorway_junction"}, + {"ref:nl" + param_tag, "ref:nl:pronunciation"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {4.3516970, 50.8465573}); + detail::build_pbf(layout, ways, nodes, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(BC_edge_id.id(), linguistics); + + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 2); + ASSERT_EQ(linguistics.size(), 1); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + + if (sign_index == 1) { + EXPECT_EQ(iter, linguistics.end()); + } else if (sign_index == 0) { + ASSERT_NE(iter, linguistics.end()); + EXPECT_EQ(signs.at(sign_index).text(), "node ref"); + EXPECT_EQ(to_string( + static_cast(std::get(iter->second))), + "nl"); + EXPECT_EQ(std::get(iter->second), + "ref:nl:pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++sign_index; + } +} + +TEST_P(PhonemesWithLangsTest, NodeName) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", {{"highway", "trunk"}, {"osm_id", "101"}}}}; + + const gurka::nodes nodes = {{"C", + {{"highway", "traffic_signals"}, + {"name", "両国二丁目"}, + {"name:ja", "両国二丁目"}, + {"name:en", "Ryogoku 2-chome"}, + {"name:ja" + param_tag, "両国二丁目 pronunciation"}, + {"name:en" + param_tag, "Ryogoku 2-chome pronunciation"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {139.79079, 35.69194}); + detail::build_pbf(layout, ways, nodes, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(node_id.id(), linguistics, true); + + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 2); + ASSERT_EQ(linguistics.size(), 2); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "両国二丁目"); + EXPECT_EQ(to_string( + static_cast(std::get(iter->second))), + "ja"); + EXPECT_EQ(std::get(iter->second), + "両国二丁目 pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (sign_index == 1) { + EXPECT_EQ(signs.at(sign_index).text(), "Ryogoku 2-chome"); + EXPECT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(std::get(iter->second), + "Ryogoku 2-chome pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++sign_index; + } +} + +TEST_P(PhonemesWithLangsTest, NamesPart2) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"name:en" + param_tag, "dV|fi \"lek *\"rod"}, + {"name" + param_tag, "dV|fi \"lek *\"rod"}, + {"name:en", "Duffy Lake Road"}, + {"name", "Duffy Lake Road"}, + {"ref" + param_tag, "haI|%we \"naIn|ti *\"naIn \"nORt"}, + {"ref", "HWY-99"}}}}; + + // note: In Canada which is multilingual. Must specify the lang + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {-121.9281, 50.6827}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + { + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(BC_edge); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + + ASSERT_EQ(names_and_types.size(), 2); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + ASSERT_EQ(linguistics.size(), 2); + + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + if (std::get<2>(name_and_type) != 0) { + // Skip the tagged names + ++name_index; + continue; + } + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + + if (name_index == 0) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "none"); + EXPECT_EQ(std::get(iter->second), + "haI|%we \"naIn|ti *\"naIn \"nORt"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (name_index == 1) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "en"); + EXPECT_EQ(std::get(iter->second), + "dV|fi \"lek *\"rod"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++name_index; + } + } + + { + GraphId node_id = CB_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(CB_edge); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + + ASSERT_EQ(names_and_types.size(), 2); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + ASSERT_EQ(linguistics.size(), 2); + + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + if (std::get<2>(name_and_type) != 0) { + // Skip the tagged names + ++name_index; + continue; + } + + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + if (name_index == 0) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "none"); + EXPECT_EQ(std::get(iter->second), + "haI|%we \"naIn|ti *\"naIn \"nORt"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (name_index == 1) { + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "en"); + EXPECT_EQ(std::get(iter->second), + "dV|fi \"lek *\"rod"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++name_index; + } + } +} + +TEST_P(PhonemesWithLangsTest, OldDataDestination) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"ref", "10"}, + {"destination", "destination in ja"}, + {"destination:en", "destination in en"}, + {"destination" + param_tag, "ipa phoneme"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {139.79079, 35.69194}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(BC_edge_id.id(), linguistics); + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 2); + ASSERT_EQ(linguistics.size(), 2); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "destination in ja"); + EXPECT_EQ(to_string( + static_cast(std::get(iter->second))), + "ja"); + EXPECT_EQ(std::get(iter->second), ""); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kNone)); + } else if (sign_index == 1) { + EXPECT_EQ(signs.at(sign_index).text(), "destination in en"); + EXPECT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(std::get(iter->second), ""); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kNone)); + } + ++sign_index; + } +} + +TEST_P(PhonemesWithLangsTest, ForwardDestination) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"ref", "10"}, + {"destination:forward:lang:en", "Koriyama"}, + {"destination:forward", "郡山"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {139.79079, 35.69194}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + { + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(BC_edge_id.id(), linguistics); + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 2); + ASSERT_EQ(linguistics.size(), 2); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "郡山"); + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "ja"); + EXPECT_EQ(std::get(iter->second), ""); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kNone)); + } else if (sign_index == 1) { + EXPECT_EQ(signs.at(sign_index).text(), "Koriyama"); + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "en"); + EXPECT_EQ(std::get(iter->second), ""); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kNone)); + } + ++sign_index; + } + } + { + GraphId node_id = CB_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(CB_edge_id.id(), linguistics); + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 0); // signs are only in the forward direction + ASSERT_EQ(linguistics.size(), 0); + } +} + +TEST_P(PhonemesWithLangsTest, Junction) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "trunk_link"}, + {"osm_id", "101"}, + {"oneway", "yes"}, + {"ref", "10"}, + {"junction:name:en", "Iwakimiwa IC"}, + {"junction:name", "いわき三和IC"}, + {"junction:name:ja" + param_tag, "ja ipa"}, + {"junction:name:ja", "いわき三和IC"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {139.79079, 35.69194}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(BC_edge_id.id(), linguistics); + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 2); + ASSERT_EQ(linguistics.size(), 2); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "いわき三和IC"); + EXPECT_EQ(to_string( + static_cast(std::get(iter->second))), + "ja"); + EXPECT_EQ(std::get(iter->second), "ja ipa"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (sign_index == 1) { + EXPECT_EQ(signs.at(sign_index).text(), "Iwakimiwa IC"); + EXPECT_EQ(to_string( + static_cast(std::get(iter->second))), + "en"); + EXPECT_EQ(std::get(iter->second), ""); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kNone)); + } + ++sign_index; + } + { + GraphId node_id = CB_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(CB_edge_id.id(), linguistics); + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), + 0); // signs are only in the forward direction due to trunk_link and oneway + ASSERT_EQ(linguistics.size(), 0); + } +} + +TEST_P(PhonemesWithLangsTest, MultiPhonemes) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "secondary"}, + {"osm_id", "101"}, + {"name", "Rochor"}, + {"name:en", "Rochor"}, + {"name:ms", "Rochor"}, + {"name:ta", "ரோச்சோர்"}, + {"name:zh", "梧槽"}, + {"name:en" + param_tag, "English Language pronunciation"}, + {"name:zh" + param_tag, "Native zh Language pronunciation"}, + // removed for testing dropping of phonemes + // {"name:ms" + param_tag, "Native ms Language pronunciation"}, + {"name:ta" + param_tag, "Native ta Language pronunciation"}, + {"name" + param_tag, "English Language pronunciation"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {103.87149, 1.32510}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + { + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(BC_edge); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + + ASSERT_EQ(names_and_types.size(), 4); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + if (std::get<2>(name_and_type) != 0) { + // Skip the tagged names + ++name_index; + continue; + } + + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + + if (name_index == 0) { + EXPECT_EQ(std::get<0>(name_and_type), "Rochor"); + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "en"); + EXPECT_EQ(std::get(iter->second), + "English Language pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (name_index == 1) { + EXPECT_EQ(std::get<0>(name_and_type), "梧槽"); + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "zh"); + EXPECT_EQ(std::get(iter->second), + "Native zh Language pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (name_index == 2) { + EXPECT_EQ(std::get<0>(name_and_type), "Rochor"); + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "ms"); + EXPECT_EQ(std::get(iter->second), ""); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kNone)); + } else if (name_index == 3) { + EXPECT_EQ(std::get<0>(name_and_type), "ரோச்சோர்"); + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "ta"); + EXPECT_EQ(std::get(iter->second), + "Native ta Language pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++name_index; + } + } + + { + GraphId node_id = CB_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + auto edgeinfo = tile->edgeinfo(CB_edge); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + + ASSERT_EQ(names_and_types.size(), 4); + std::unordered_map> linguistics = + edgeinfo.GetLinguisticMap(); + uint8_t name_index = 0; + for (const auto& name_and_type : names_and_types) { + if (std::get<2>(name_and_type) != 0) { + // Skip the tagged names + ++name_index; + continue; + } + + std::unordered_map>::const_iterator iter = + linguistics.find(name_index); + ASSERT_NE(iter, linguistics.end()); + if (name_index == 0) { + EXPECT_EQ(std::get<0>(name_and_type), "Rochor"); + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "en"); + EXPECT_EQ(std::get(iter->second), + "English Language pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (name_index == 1) { + EXPECT_EQ(std::get<0>(name_and_type), "梧槽"); + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "zh"); + EXPECT_EQ(std::get(iter->second), + "Native zh Language pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (name_index == 2) { + EXPECT_EQ(std::get<0>(name_and_type), "Rochor"); + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "ms"); + EXPECT_EQ(std::get(iter->second), ""); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kNone)); + } else if (name_index == 3) { + EXPECT_EQ(std::get<0>(name_and_type), "ரோச்சோர்"); + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "ta"); + EXPECT_EQ(std::get(iter->second), + "Native ta Language pronunciation"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++name_index; + } + } +} + +TEST_P(PhonemesWithLangsTest, MultiPhonemes2) { + const auto& param_tag = std::get<0>(GetParam()); + const auto& param_alphabet = std::get<1>(GetParam()); + CreateWorkdir(); + + ways = {{"BC", + {{"highway", "trunk"}, + {"osm_id", "101"}, + {"ref", "10"}, + {"destination", "Rochor"}, + {"destination:lang:en", "Rochor"}, + {"destination:lang:ms", "Rochor"}, + {"destination:lang:ta", "ரோச்சோர்"}, + {"destination:lang:zh", "梧槽"}, + {"destination:lang:en" + param_tag, "Rochor ipa"}, + // removed for testing dropping of phonems + //{"destination:lang:ms" + param_tag, "Rochor ipa"}, + {"destination:lang:ta" + param_tag, "ரோச்சோர் ipa"}, + {"destination:lang:zh" + param_tag, "梧槽 ipa"}}}}; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {103.87149, 1.32510}); + detail::build_pbf(layout, ways, {}, {}, pbf_filename); + valhalla::gurka::map map; + map.nodes = layout; + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + GraphId BC_edge_id; + const DirectedEdge* BC_edge = nullptr; + GraphId CB_edge_id; + const DirectedEdge* CB_edge = nullptr; + std::tie(BC_edge_id, BC_edge, CB_edge_id, CB_edge) = + findEdge(graph_reader, map.nodes, "", "C", baldr::GraphId{}, 101); + EXPECT_NE(BC_edge, nullptr); + EXPECT_NE(CB_edge, nullptr); + { + GraphId node_id = BC_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(BC_edge_id.id(), linguistics); + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 4); + ASSERT_EQ(linguistics.size(), 4); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "Rochor"); + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "en"); + EXPECT_EQ(std::get(iter->second), "Rochor ipa"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (sign_index == 1) { + EXPECT_EQ(signs.at(sign_index).text(), "梧槽"); + + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "zh"); + EXPECT_EQ(std::get(iter->second), "梧槽 ipa"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (sign_index == 2) { + EXPECT_EQ(signs.at(sign_index).text(), "Rochor"); + + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "ms"); + EXPECT_EQ(std::get(iter->second), ""); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kNone)); + } else if (sign_index == 3) { + EXPECT_EQ(signs.at(sign_index).text(), "ரோச்சோர்"); + + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "ta"); + EXPECT_EQ(std::get(iter->second), "ரோச்சோர் ipa"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++sign_index; + } + } + { + GraphId node_id = CB_edge->endnode(); + auto tile = graph_reader.GetGraphTile(node_id); + std::unordered_map> linguistics; + std::vector signs = tile->GetSigns(CB_edge_id.id(), linguistics); + uint32_t sign_index = 0; + ASSERT_EQ(signs.size(), 4); + ASSERT_EQ(linguistics.size(), 4); + + for (const auto& sign : signs) { + std::unordered_map>::const_iterator iter = + linguistics.find(sign_index); + ASSERT_NE(iter, linguistics.end()); + + if (sign_index == 0) { + EXPECT_EQ(signs.at(sign_index).text(), "Rochor"); + + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "en"); + EXPECT_EQ(std::get(iter->second), "Rochor ipa"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (sign_index == 1) { + EXPECT_EQ(signs.at(sign_index).text(), "梧槽"); + + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "zh"); + EXPECT_EQ(std::get(iter->second), "梧槽 ipa"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } else if (sign_index == 2) { + EXPECT_EQ(signs.at(sign_index).text(), "Rochor"); + + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "ms"); + EXPECT_EQ(std::get(iter->second), ""); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(baldr::PronunciationAlphabet::kNone)); + } else if (sign_index == 3) { + EXPECT_EQ(signs.at(sign_index).text(), "ரோச்சோர்"); + + EXPECT_EQ(to_string(static_cast( + std::get(iter->second))), + "ta"); + EXPECT_EQ(std::get(iter->second), "ரோச்சோர் ipa"); + EXPECT_EQ(static_cast(std::get(iter->second)), + static_cast(param_alphabet)); + } + ++sign_index; + } + } +} + +INSTANTIATE_TEST_SUITE_P( + PhonemesWithLangsTest, + PhonemesWithLangsTest, + ::testing::Values(std::make_tuple(":pronunciation", PronunciationAlphabet::kIpa), + std::make_tuple(":pronunciation:jeita", PronunciationAlphabet::kJeita), + std::make_tuple(":pronunciation:katakana", PronunciationAlphabet::kKatakana), + std::make_tuple(":pronunciation:nt-sampa", PronunciationAlphabet::kNtSampa))); diff --git a/test/gurka/test_precision.cc b/test/gurka/test_precision.cc index 1eacd7651a..a7ed8750d7 100644 --- a/test/gurka/test_precision.cc +++ b/test/gurka/test_precision.cc @@ -1,6 +1,5 @@ #include "gurka.h" #include -#include using namespace valhalla; @@ -9,7 +8,6 @@ class Precision : public ::testing::Test { static gurka::map map; static void SetUpTestSuite() { - constexpr double gridsize = 100; const std::string ascii_map = R"( A-1--B-C----\ @@ -35,7 +33,7 @@ TEST_F(Precision, WaypointsOnNodes) { map.nodes["D"], map.nodes["E"], map.nodes["F"]}; EXPECT_EQ(shape.size(), expected_shape.size()); - for (int i = 0; i < shape.size(); ++i) { + for (size_t i = 0; i < shape.size(); ++i) { EXPECT_NEAR(shape[i].lat(), expected_shape[i].lat(), 0.000001); EXPECT_NEAR(shape[i].lng(), expected_shape[i].lng(), 0.000001); } @@ -49,7 +47,7 @@ TEST_F(Precision, PartialOffsetCheckOne) { map.nodes["D"], map.nodes["E"], map.nodes["2"]}; EXPECT_EQ(shape.size(), expected_shape.size()); - for (int i = 0; i < shape.size(); ++i) { + for (size_t i = 0; i < shape.size(); ++i) { EXPECT_NEAR(shape[i].lat(), expected_shape[i].lat(), 0.000001); EXPECT_NEAR(shape[i].lng(), expected_shape[i].lng(), 0.000001); } @@ -63,7 +61,7 @@ TEST_F(Precision, PartialOffsetCheckTwo) { map.nodes["D"], map.nodes["E"], map.nodes["2"]}; EXPECT_EQ(shape.size(), expected_shape.size()); - for (int i = 0; i < shape.size(); ++i) { + for (size_t i = 0; i < shape.size(); ++i) { EXPECT_NEAR(shape[i].lat(), expected_shape[i].lat(), 0.000001); EXPECT_NEAR(shape[i].lng(), expected_shape[i].lng(), 0.000001); } @@ -77,8 +75,8 @@ TEST_F(Precision, PartialOffsetCheckThree) { map.nodes["D"], map.nodes["E"], map.nodes["F"]}; EXPECT_EQ(shape.size(), expected_shape.size()); - for (int i = 0; i < shape.size(); ++i) { + for (size_t i = 0; i < shape.size(); ++i) { EXPECT_NEAR(shape[i].lat(), expected_shape[i].lat(), 0.000001); EXPECT_NEAR(shape[i].lng(), expected_shape[i].lng(), 0.000001); } -} \ No newline at end of file +} diff --git a/test/gurka/test_ramps_tc.cc b/test/gurka/test_ramps_tc.cc index e7109056b7..fa0b95bde1 100644 --- a/test/gurka/test_ramps_tc.cc +++ b/test/gurka/test_ramps_tc.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) @@ -290,8 +289,115 @@ void check_edge_classification(baldr::GraphReader& graph_reader, const auto edge = std::get<1>(gurka::findEdgeByNodes(graph_reader, nodes, b, e)); EXPECT_EQ(edge->classification(), rc); } + +void check_edge_use(baldr::GraphReader& graph_reader, + const gurka::nodelayout& nodes, + const std::string& b, + const std::string& e, + baldr::Use tc) { + const auto edge = std::get<1>(gurka::findEdgeByNodes(graph_reader, nodes, b, e)); + EXPECT_EQ(edge->use(), tc); +} + } // namespace +TEST(RampsNoReclass, test_tc_infer) { + + constexpr double gridsize_metres = 10; + + const std::string ascii_map = R"( + + A---B-------------------------------C------D + \ / + \ F J / + \ | | / + \ | | / + \ | | / + \ | | / + --E-----G-K-----N-- + \ | | / + \ | | / + \ | | / + \ | | / + H L + | | + | | + | | + I M + + +)"; + + const gurka::ways ways = { + {"AB", {{"highway", "motorway"}, {"oneway", "yes"}}}, + {"BC", {{"highway", "motorway"}, {"oneway", "yes"}}}, + {"CD", {{"highway", "motorway"}, {"oneway", "yes"}}}, + + {"BE", {{"highway", "motorway_link"}, {"oneway", "yes"}}}, + {"EG", {{"highway", "motorway_link"}, {"oneway", "yes"}}}, + {"EH", {{"highway", "motorway_link"}, {"oneway", "yes"}}}, + {"GK", {{"highway", "motorway_link"}, {"oneway", "yes"}}}, + {"FG", {{"highway", "primary"}, {"oneway", "yes"}}}, + {"GH", {{"highway", "primary"}, {"oneway", "yes"}}}, + {"HI", {{"highway", "primary"}, {"oneway", "yes"}}}, + {"ML", {{"highway", "primary"}, {"oneway", "yes"}}}, + {"LK", {{"highway", "primary"}, {"oneway", "yes"}}}, + {"KJ", {{"highway", "primary"}, {"oneway", "yes"}}}, + {"KN", {{"highway", "motorway_link"}, {"oneway", "yes"}}}, + {"LN", {{"highway", "motorway_link"}, {"oneway", "yes"}}}, + {"NC", {{"highway", "motorway_link"}, {"oneway", "yes"}}}, + }; + + const gurka::nodes nodes = {{"B", {{"highway", "motorway_junction"}, {"ref", "4"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_ramps_tc_infer_no_reclass", + {{"mjolnir.data_processing.infer_internal_intersections", "true"}, + {"mjolnir.data_processing.infer_turn_channels", "true"}, + {"mjolnir.reclassify_links", "false"}}); + + // check that motorway links were not reclassified + baldr::GraphReader graph_reader(map.config.get_child("mjolnir")); + check_edge_classification(graph_reader, layout, "B", "E", baldr::RoadClass::kMotorway); + check_edge_classification(graph_reader, layout, "E", "G", baldr::RoadClass::kMotorway); + check_edge_classification(graph_reader, layout, "E", "H", baldr::RoadClass::kMotorway); + check_edge_classification(graph_reader, layout, "G", "K", baldr::RoadClass::kMotorway); + check_edge_classification(graph_reader, layout, "K", "N", baldr::RoadClass::kMotorway); + check_edge_classification(graph_reader, layout, "L", "N", baldr::RoadClass::kMotorway); + check_edge_classification(graph_reader, layout, "N", "C", baldr::RoadClass::kMotorway); + + check_edge_use(graph_reader, layout, "G", "K", baldr::Use::kTurnChannel); + + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "J"}, "auto"); + ASSERT_EQ(result.trip().routes(0).legs_size(), 1); + auto leg = result.trip().routes(0).legs(0); + gurka::assert::raw::expect_path(result, {"AB", "BE", "EG", "GK", "KJ"}); + + EXPECT_EQ(leg.node(0).edge().use(), valhalla::TripLeg_Use::TripLeg_Use_kRoadUse); + EXPECT_EQ(leg.node(0).edge().internal_intersection(), 0); + + EXPECT_EQ(leg.node(1).edge().use(), valhalla::TripLeg_Use::TripLeg_Use_kRampUse); + EXPECT_EQ(leg.node(1).edge().internal_intersection(), 0); + + EXPECT_EQ(leg.node(2).edge().use(), valhalla::TripLeg_Use::TripLeg_Use_kRampUse); + EXPECT_EQ(leg.node(2).edge().internal_intersection(), 0); + + EXPECT_EQ(leg.node(3).edge().use(), valhalla::TripLeg_Use::TripLeg_Use_kTurnChannelUse); + EXPECT_EQ(leg.node(3).edge().internal_intersection(), 1); + + EXPECT_EQ(leg.node(4).edge().use(), valhalla::TripLeg_Use::TripLeg_Use_kRoadUse); + EXPECT_EQ(leg.node(4).edge().internal_intersection(), 0); + + gurka::assert::raw::expect_maneuver_begin_path_indexes(result, {0, 1, 4, 5}); + + // Verify that we are marking the internal edge. If not, there will be a continue instruction + int maneuver_index = 2; + gurka::assert::raw::expect_instructions_at_maneuver_index( + result, maneuver_index, "Turn left onto KJ.", + "Turn left. Then You will arrive at your destination.", "Turn left onto KJ.", + "Turn left onto KJ. Then You will arrive at your destination.", "Continue for 50 meters."); +} + TEST(LinkReclassification, test_use_refs) { // Check that linkreclassification algorithm takes into account 'ref' tag constexpr double gridsize_metres = 10; diff --git a/test/gurka/test_reach.cc b/test/gurka/test_reach.cc index 55ab4b6e10..76170d1180 100644 --- a/test/gurka/test_reach.cc +++ b/test/gurka/test_reach.cc @@ -4,7 +4,6 @@ #include "sif/dynamiccost.h" #include "test.h" -#include #include #include diff --git a/test/gurka/test_recost.cc b/test/gurka/test_recost.cc index 35dc5216d8..ab97204e00 100644 --- a/test/gurka/test_recost.cc +++ b/test/gurka/test_recost.cc @@ -344,7 +344,7 @@ TEST(recosting, all_algorithms) { double length = 0; uint32_t pred = baldr::kInvalidLabel; sif::LabelCallback label_cb = [&elapsed_itr, &length, &pred, - reverse](const sif::EdgeLabel& label) -> void { + reverse](const sif::PathEdgeLabel& label) -> void { length += elapsed_itr->edge().length_km() * 1000.0; EXPECT_EQ(elapsed_itr->edge().id(), label.edgeid()); EXPECT_EQ(pred++, label.predecessor()); @@ -433,7 +433,7 @@ TEST(recosting, throwing) { // setup a callback for the recosting to tell us about the new label each made bool called = false; - sif::LabelCallback label_cb = [&called](const sif::EdgeLabel& label) -> void { called = true; }; + sif::LabelCallback label_cb = [&called](const sif::PathEdgeLabel& label) -> void { called = true; }; // build up the costing object auto costing = sif::CostFactory().Create(Costing::auto_); diff --git a/test/gurka/test_route.cc b/test/gurka/test_route.cc index fa04274029..20b52b02ce 100644 --- a/test/gurka/test_route.cc +++ b/test/gurka/test_route.cc @@ -1,5 +1,6 @@ #include "gurka.h" #include "test.h" +#include using namespace valhalla; @@ -318,7 +319,7 @@ uint32_t AlgorithmTest::current = 0, AlgorithmTest::historical = 0, AlgorithmTes AlgorithmTest::freeflow = 0; uint32_t speed_from_edge(const valhalla::Api& api, bool compare_with_previous_edge = true) { - uint32_t kmh = -1; + uint32_t kmh = invalid(); const auto& nodes = api.trip().routes(0).legs(0).node(); for (int i = 0; i < nodes.size() - 1; ++i) { const auto& node = nodes.Get(i); @@ -329,7 +330,7 @@ uint32_t speed_from_edge(const valhalla::Api& api, bool compare_with_previous_ed node.cost().elapsed_cost().seconds() - node.cost().transition_cost().seconds()) / 3600.0; auto new_kmh = static_cast(km / h + .5); - if (kmh != -1 && compare_with_previous_edge) { + if (is_valid(kmh) && compare_with_previous_edge) { EXPECT_EQ(kmh, new_kmh); } kmh = new_kmh; @@ -996,4 +997,242 @@ TEST(AlgorithmTestDest, TestAlgoMultiOriginDestination) { check("7", "8", {"CD"}); check("8", "8", {"CD"}); -} \ No newline at end of file +} + +class DateTimeTest : public ::testing::Test { +protected: + // check both with and without time zones present + static gurka::map map; + static gurka::map map_tz; + + static void SetUpTestSuite() { + constexpr double gridsize = 1500; + + // ~ are approximate time zone crossings + const std::string ascii_map = R"( + A----------B + | | + C D + | | + ~ ~ + | | + | | + E F + | | + G----------H + )"; + + const gurka::ways ways = {{"AC", {{"highway", "residential"}}}, + {"CE", {{"highway", "residential"}}}, + {"EG", {{"highway", "residential"}}}, + {"GH", {{"highway", "residential"}}}, + {"HF", {{"highway", "residential"}}}, + {"FD", {{"highway", "residential"}}}, + {"DB", {{"highway", "residential"}}}, + {"BA", {{"highway", "residential"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {-8.5755, 42.1079}); + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/time_zone_route_no_tz"); + map_tz = gurka::buildtiles(layout, ways, {}, {}, "test/data/time_zone_route", + {{"mjolnir.timezone", VALHALLA_BUILD_DIR "test/data/tz.sqlite"}}); + } +}; +gurka::map DateTimeTest::map = {}; +gurka::map DateTimeTest::map_tz = {}; + +TEST_F(DateTimeTest, DepartAt) { + { + // one time zone crossing + auto api = gurka::do_action(valhalla::Options::route, map_tz, {"A", "G"}, "auto", + {{"/date_time/type", "1"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(1).date_time(), "2020-10-30T08:23"); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), "+01:00"); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), "+00:00"); + } + { + // no time zone crossing + auto api = gurka::do_action(valhalla::Options::route, map_tz, {"A", "B"}, "auto", + {{"/date_time/type", "1"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(1).date_time(), "2020-10-30T09:21"); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), "+01:00"); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), "+01:00"); + } + { + // two time zone crossings + auto api = gurka::do_action(valhalla::Options::route, map_tz, {"A", "G", "H", "B"}, "auto", + {{"/date_time/type", "1"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(1).date_time(), "2020-10-30T08:23"); + EXPECT_EQ(api.options().locations(2).date_time(), "2020-10-30T08:44"); + EXPECT_EQ(api.options().locations(3).date_time(), "2020-10-30T10:07"); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), "+01:00"); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), "+00:00"); + EXPECT_EQ(api.options().locations(2).time_zone_offset(), "+00:00"); + EXPECT_EQ(api.options().locations(3).time_zone_offset(), "+01:00"); + } +} + +TEST_F(DateTimeTest, DepartAtNoTz) { + { + // one time zone crossing + auto api = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, "auto", + {{"/date_time/type", "1"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(1).date_time(), ""); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), ""); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), ""); + } +} + +TEST_F(DateTimeTest, ArriveBy) { + { + // one time zone crossing + auto api = gurka::do_action(valhalla::Options::route, map_tz, {"A", "G"}, "auto", + {{"/date_time/type", "2"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), "2020-10-30T09:37"); + EXPECT_EQ(api.options().locations(1).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), "+01:00"); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), "+00:00"); + } + { + // no time zone crossing + auto api = gurka::do_action(valhalla::Options::route, map_tz, {"A", "B"}, "auto", + {{"/date_time/type", "2"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), "2020-10-30T08:39"); + EXPECT_EQ(api.options().locations(1).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), "+01:00"); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), "+01:00"); + } + { + // two time zone crossings + auto api = gurka::do_action(valhalla::Options::route, map_tz, {"A", "G", "H", "B"}, "auto", + {{"/date_time/type", "2"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), "2020-10-30T07:53"); + EXPECT_EQ(api.options().locations(1).date_time(), "2020-10-30T07:16"); + EXPECT_EQ(api.options().locations(2).date_time(), "2020-10-30T07:37"); + EXPECT_EQ(api.options().locations(3).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), "+01:00"); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), "+00:00"); + EXPECT_EQ(api.options().locations(2).time_zone_offset(), "+00:00"); + EXPECT_EQ(api.options().locations(3).time_zone_offset(), "+01:00"); + } +} + +TEST_F(DateTimeTest, ArriveByNoTz) { + { + auto api = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, "auto", + {{"/date_time/type", "2"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), ""); + EXPECT_EQ(api.options().locations(1).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), ""); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), ""); + } + { + // multiple locations to check we do not propagate date time + auto api = gurka::do_action(valhalla::Options::route, map, {"A", "G", "F", "D"}, "auto", + {{"/date_time/type", "2"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), ""); + EXPECT_EQ(api.options().locations(1).date_time(), ""); + EXPECT_EQ(api.options().locations(2).date_time(), ""); + EXPECT_EQ(api.options().locations(3).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), ""); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), ""); + EXPECT_EQ(api.options().locations(2).time_zone_offset(), ""); + EXPECT_EQ(api.options().locations(3).time_zone_offset(), ""); + } +} + +TEST_F(DateTimeTest, Invariant) { + { + auto api = gurka::do_action(valhalla::Options::route, map, {"A", "G", "D", "B"}, "auto", + {{"/date_time/type", "3"}, {"/date_time/value", "2020-10-30T09:00"}}); + for (int i = 0; i < 4; ++i) { + EXPECT_EQ(api.options().locations(i).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(i).time_zone_offset(), ""); + } + } +} + +TEST(StandAlone, HGVNoAccessPenalty) { + // if hgv_no_penalty is on we should still respect the maxweight restriction on CD + // so we should take the next-best hgv=no edge with JK + const std::string ascii_map = R"( + A-1--B----C----D----E--2-F----G----H--3-I + | | + J----K + | | + | | + L----M + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "residential"}, {"hgv", "no"}}}, + {"BC", {{"highway", "residential"}}}, + {"CD", {{"highway", "residential"}, {"hgv", "no"}, {"maxweight", "3.5"}}}, + {"DE", {{"highway", "residential"}}}, + {"EF", {{"highway", "residential"}, {"hgv", "no"}}}, + {"FG", {{"highway", "residential"}, {"hgv", "no"}}}, + {"GH", {{"highway", "residential"}, {"hgv", "no"}}}, + {"HI", {{"highway", "residential"}, {"hgv", "no"}}}, + {"CJ", {{"highway", "residential"}}}, + {"JK", {{"highway", "residential"}, {"hgv", "no"}}}, + {"JLMK", {{"highway", "residential"}}}, + {"KD", {{"highway", "residential"}}}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); + gurka::map map = gurka::buildtiles(layout, ways, {}, {}, "test/data/hgv_no_access_penalty"); + + std::unordered_map no_time = { + {"/costing_options/truck/hgv_no_access_penalty", "2000"}}; + std::unordered_map with_depart_at = + {{"/costing_options/truck/hgv_no_access_penalty", "2000"}, + {"/locations/0/date_time", "2024-03-20T09:00"}}; + std::unordered_map with_arrive_by = + {{"/costing_options/truck/hgv_no_access_penalty", "2000"}, + {"/locations/1/date_time", "2024-03-20T09:00"}}; + + auto get_leg_cost = [](const valhalla::Api& response) { + return response.trip().routes(0).legs(0).node().rbegin()->cost().elapsed_cost().cost(); + }; + + // do both bidirectional & both unidirectional a* + std::vector> options = {no_time, with_depart_at, + with_arrive_by}; + for (auto& truck_options : options) { + + // by default, take the detour via LM + // NOTE, we're not snapping to the hgv=no edges either + { + auto route = gurka::do_action(valhalla::Options::route, map, {"1", "2"}, "truck"); + gurka::assert::raw::expect_path(route, {"BC", "CJ", "JLMK", "KD", "DE"}); + } + + // with a high hgv_no_penalty also take the detour via LM, but do snap to the hgv=no edges + { + auto route = + gurka::do_action(valhalla::Options::route, map, {"1", "2"}, "truck", truck_options); + gurka::assert::raw::expect_path(route, {"AB", "BC", "CJ", "JLMK", "KD", "DE", "EF"}); + } + + // with a low hgv_no_penalty take the JK edge + { + truck_options["/costing_options/truck/hgv_no_access_penalty"] = "10"; + auto route = + gurka::do_action(valhalla::Options::route, map, {"1", "2"}, "truck", truck_options); + gurka::assert::raw::expect_path(route, {"AB", "BC", "CJ", "JK", "KD", "DE", "EF"}); + } + + // if all hgv=no and a high hgv_no_penalty, truck should not trigger the penalty at all + // so cost should be similar to car + { + truck_options["/costing_options/truck/hgv_no_access_penalty"] = "2000"; + auto route_car = gurka::do_action(valhalla::Options::route, map, {"2", "3"}, "auto"); + auto route_truck = + gurka::do_action(valhalla::Options::route, map, {"2", "3"}, "truck", truck_options); + EXPECT_NEAR(get_leg_cost(route_car), get_leg_cost(route_truck), 300.0); + } + } +} diff --git a/test/gurka/test_route_on_private_access.cc b/test/gurka/test_route_on_private_access.cc deleted file mode 100644 index 57ebbdde9a..0000000000 --- a/test/gurka/test_route_on_private_access.cc +++ /dev/null @@ -1,49 +0,0 @@ -#include "gurka.h" -#include - -#if !defined(VALHALLA_SOURCE_DIR) -#define VALHALLA_SOURCE_DIR -#endif - -using namespace valhalla; - -const std::unordered_map build_config{ - {"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/netherlands_admin.sqlite"}}}; - -const std::vector& costing = {"auto", "taxi", "bus", "truck", - "bicycle", "motor_scooter", "motorcycle", "pedestrian"}; - -TEST(Standalone, RouteOnPrivateAccess) { - constexpr double gridsize_metres = 10; - - const std::string ascii_map = R"( - A---B---C---D - | | | - E F G - )"; - - const gurka::ways ways = { - {"AB", {{"highway", "primary"}}}, - {"BC", {{"highway", "primary"}}}, - {"CD", {{"highway", "primary"}}}, - {"BE", {{"highway", "service"}, {"access", "private"}}}, - {"CF", {{"highway", "service"}, {"access", "private"}, {"service", "driveway"}}}, - {"DG", {{"highway", "service"}, {"access", "private"}, {"service", "parking_aisle"}}}, - }; - - const auto layout = - gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {5.1079374, 52.0887174}); - auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_route_on_private_access", - build_config); - - for (auto& c : costing) { - auto result = gurka::do_action(valhalla::Options::route, map, {"A", "E"}, c); - gurka::assert::raw::expect_path(result, {"AB", "BE"}); - - result = gurka::do_action(valhalla::Options::route, map, {"A", "F"}, c); - gurka::assert::raw::expect_path(result, {"AB", "BC", "CF"}); - - result = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, c); - gurka::assert::raw::expect_path(result, {"AB", "BC", "CD", "DG"}); - } -} diff --git a/test/gurka/test_route_summary.cc b/test/gurka/test_route_summary.cc index cf4d3d96a6..bd292f97f2 100644 --- a/test/gurka/test_route_summary.cc +++ b/test/gurka/test_route_summary.cc @@ -1,5 +1,5 @@ #include -#include +#include #include #include @@ -9,7 +9,6 @@ #include "mjolnir/adminbuilder.h" #include "test/test.h" -#include #include #include @@ -112,7 +111,7 @@ TEST(TestRouteSummary, GetSummary) { gurka::assert::osrm::expect_summaries(result, {expected_route_summary0, expected_route_summary1, expected_route_summary2}); - filesystem::remove_all(workdir); + std::filesystem::remove_all(workdir); } TEST(TestRouteSummary, DupSummaryFix) { @@ -202,5 +201,85 @@ TEST(TestRouteSummary, DupSummaryFix) { const std::string expected_route_summary1 = "RT 1, RT 2, RT 4"; gurka::assert::osrm::expect_summaries(result, {expected_route_summary0, expected_route_summary1}); - filesystem::remove_all(workdir); + std::filesystem::remove_all(workdir); +} + +TEST(Standalone, TripLegSummary) { + const std::string ascii_map = R"( + A---B---C---D + )"; + + const gurka::ways ways = {{"AB", {{"highway", "motorway"}}}, + {"BC", {{"highway", "motorway"}, {"toll", "yes"}}}, + {"CD", {{"route", "ferry"}}}}; + const double gridsize = 100; + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + const std::string workdir = "test/data/gurka_test_route_summary"; + + std::string result_json; + rapidjson::Document result; + + valhalla::gurka::map map = gurka::buildtiles(layout, ways, {}, {}, workdir); + + valhalla::Api result0 = gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "auto", + {{"/directions_type", "none"}}, {}, &result_json); + EXPECT_TRUE(result0.trip().routes(0).legs(0).summary().has_highway()); + EXPECT_FALSE(result0.trip().routes(0).legs(0).summary().has_toll()); + EXPECT_FALSE(result0.trip().routes(0).legs(0).summary().has_ferry()); + + result.Parse(result_json.c_str()); + auto trip = result["trip"].GetObject(); + auto summary = trip["summary"].GetObject(); + + EXPECT_TRUE(summary["has_highway"].GetBool()); + EXPECT_FALSE(summary["has_toll"].GetBool()); + EXPECT_FALSE(summary["has_ferry"].GetBool()); + result_json.erase(); + + valhalla::Api result1 = gurka::do_action(valhalla::Options::route, map, {"B", "C"}, "auto", + {{"/directions_type", "none"}}); + EXPECT_TRUE(result1.trip().routes(0).legs(0).summary().has_highway()); + EXPECT_TRUE(result1.trip().routes(0).legs(0).summary().has_toll()); + EXPECT_FALSE(result1.trip().routes(0).legs(0).summary().has_ferry()); + + valhalla::Api result2 = gurka::do_action(valhalla::Options::route, map, {"C", "D"}, "auto", + {{"/directions_type", "none"}}); + EXPECT_FALSE(result2.trip().routes(0).legs(0).summary().has_highway()); + EXPECT_FALSE(result2.trip().routes(0).legs(0).summary().has_toll()); + EXPECT_TRUE(result2.trip().routes(0).legs(0).summary().has_ferry()); + + // Validate that the presence of highway, toll, and ferry tags in route summaries is consistent + // and does not depend on the `directions_type` value + + valhalla::Api result3 = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "auto", + {{"/directions_type", "none"}}, {}, &result_json); + + EXPECT_TRUE(result3.trip().routes(0).legs(0).summary().has_highway()); + EXPECT_TRUE(result3.trip().routes(0).legs(0).summary().has_toll()); + EXPECT_TRUE(result3.trip().routes(0).legs(0).summary().has_ferry()); + + result.Parse(result_json.c_str()); + trip = result["trip"].GetObject(); + summary = trip["summary"].GetObject(); + + EXPECT_TRUE(summary["has_highway"].GetBool()); + EXPECT_TRUE(summary["has_toll"].GetBool()); + EXPECT_TRUE(summary["has_ferry"].GetBool()); + result_json.erase(); + + valhalla::Api result4 = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "auto", + {{"/directions_type", "maneuvers"}}, {}, &result_json); + EXPECT_TRUE(result4.trip().routes(0).legs(0).summary().has_highway()); + EXPECT_TRUE(result4.trip().routes(0).legs(0).summary().has_toll()); + EXPECT_TRUE(result4.trip().routes(0).legs(0).summary().has_ferry()); + + result.Parse(result_json.c_str()); + trip = result["trip"].GetObject(); + summary = trip["summary"].GetObject(); + + EXPECT_TRUE(summary["has_highway"].GetBool()); + EXPECT_TRUE(summary["has_toll"].GetBool()); + EXPECT_TRUE(summary["has_ferry"].GetBool()); + + std::filesystem::remove_all(workdir); } diff --git a/test/gurka/test_route_with_narrative_languages.cc b/test/gurka/test_route_with_narrative_languages.cc index bfcc3aee91..956ac4f2ff 100644 --- a/test/gurka/test_route_with_narrative_languages.cc +++ b/test/gurka/test_route_with_narrative_languages.cc @@ -1,6 +1,5 @@ #include "gurka.h" #include -#include #include #include @@ -86,8 +85,8 @@ const std::vector> RouteWithNarrativeLanguag {"nb-NO", "Ta til høyre inn på Main Street."}, {"nl", "Sla rechtsaf naar Main Street."}, {"nl-NL", "Sla rechtsaf naar Main Street."}, - {"pl", "Skręć w prawo w stronę: Main Street."}, - {"pl-PL", "Skręć w prawo w stronę: Main Street."}, + {"pl", "Skręć w prawo w Main Street."}, + {"pl-PL", "Skręć w prawo w Main Street."}, {"pt-BR", "Vire à direita para Main Street."}, {"pt", "Vire à direita em direção à Main Street."}, {"pt-PT", "Vire à direita em direção à Main Street."}, diff --git a/test/gurka/test_search_filter.cc b/test/gurka/test_search_filter.cc index d8094f818b..1bce9f18dd 100644 --- a/test/gurka/test_search_filter.cc +++ b/test/gurka/test_search_filter.cc @@ -17,6 +17,8 @@ class SearchFilter : public ::testing::Test { static void SetUpTestSuite() { constexpr double gridsize = 100; + // remark: without admin database, left-side driving is the + // default driving side. const std::string ascii_map = R"( B---------C | 2 8 | @@ -59,6 +61,23 @@ TEST_F(SearchFilter, Unfiltered) { gurka::assert::osrm::expect_steps(result, {"AB", "BC"}); gurka::assert::raw::expect_path(result, {"AB", "BC"}); } +TEST_F(SearchFilter, NodeSnapped) { + auto from = "B"; + auto to = "C"; + + const std::string& request = + (boost::format( + R"({"locations":[{"lat":%s,"lon":%s,"search_filter":{"exclude_tunnel":true}},{"lat":%s,"lon":%s}],"costing":"auto"})") % + std::to_string(map.nodes.at(from).lat()) % std::to_string(map.nodes.at(from).lng()) % + std::to_string(map.nodes.at(to).lat()) % std::to_string(map.nodes.at(to).lng())) + .str(); + + auto result = gurka::do_action(valhalla::Options::route, map, request); + + // should take the shortest path + gurka::assert::osrm::expect_steps(result, {"AB", "AD", "CD"}); + gurka::assert::raw::expect_path(result, {"AB", "AD", "CD"}); +} TEST_F(SearchFilter, Heading) { auto from = "1"; auto to = "2"; @@ -91,6 +110,22 @@ TEST_F(SearchFilter, PreferredSide) { gurka::assert::osrm::expect_steps(result, {"AB", "AD", "CD", "BC"}); gurka::assert::raw::expect_path(result, {"AB", "AD", "CD", "BC"}); } +TEST_F(SearchFilter, StreetSideCutoff) { + auto from = "7"; + auto to = "8"; + + const std::string& request = + (boost::format( + R"({"locations":[{"lat":%s,"lon":%s},{"lat":%s,"lon":%s,"preferred_side":"same","street_side_cutoff":"primary"}],"costing":"auto"})") % + std::to_string(map.nodes.at(from).lat()) % std::to_string(map.nodes.at(from).lng()) % + std::to_string(map.nodes.at(to).lat()) % std::to_string(map.nodes.at(to).lng())) + .str(); + auto result = gurka::do_action(valhalla::Options::route, map, request); + + // should take the short way in the north + gurka::assert::osrm::expect_steps(result, {"AB", "BC"}); + gurka::assert::raw::expect_path(result, {"AB", "BC"}); +} TEST_F(SearchFilter, MaxRoadClass) { auto from = "1"; auto to = "2"; @@ -243,7 +278,7 @@ class ExcludeClosuresOnWaypoints : public ::testing::TestWithParam | H - L-4---5-M-6-N + L4----5-M-6-N )"; const std::string speed_str = std::to_string(default_speed); diff --git a/test/gurka/test_service_turn.cc b/test/gurka/test_service_turn.cc index 1b8083cbb6..6fc1fc1558 100644 --- a/test/gurka/test_service_turn.cc +++ b/test/gurka/test_service_turn.cc @@ -72,7 +72,7 @@ gurka::map ServiceRoadsTest::service_streets_map = {}; TEST_F(ServiceRoadsTest, test_default_value) { for (const auto& c : costing) if (c == "motor_scooter" || c == "pedestrian") - // For current exmple and with these costings route is not affected + // For current example and with these costings route is not affected validate_path(gurka::do_action(valhalla::Options::route, service_streets_map, {"1", "2"}, c), {"AB", "BE", "EF"}); else diff --git a/test/gurka/test_shortcut.cc b/test/gurka/test_shortcut.cc index 09538c35d7..a0939881c3 100644 --- a/test/gurka/test_shortcut.cc +++ b/test/gurka/test_shortcut.cc @@ -42,36 +42,29 @@ TEST(Shortcuts, CreateValid) { EXPECT_EQ(opp_shortcut_edged->speed(), 90); } -// Here no shortcuts are created. There could be one from A to C with speed 80 but in the opposite -// direction speeds differ which blocks CA creation. -TEST(Shortcuts, CreateInvalid) { +TEST(Shortcuts, LoopWithoutShortcut) { constexpr double gridsize = 50; const std::string ascii_map = R"( - A--B--C + A--B + | | + | | + C--D )"; - - const gurka::ways ways = { - {"AB", - {{"highway", "primary"}, - {"name", "Independence Avenue"}, - {"maxspeed:forward", "80"}, - {"maxspeed:backward", "80"}}}, - {"BC", - {{"highway", "primary"}, - {"name", "Independence Avenue"}, - {"maxspeed:forward", "80"}, - {"maxspeed:backward", "90"}}}, - }; + const gurka::ways ways = {{"AB", {{"highway", "primary"}}}, + {"BD", {{"highway", "primary"}}}, + {"DC", {{"highway", "primary"}}}, + {"CA", {{"highway", "primary"}}}}; const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); - auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_openlrjoiner_shortcut_speed"); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_shortcut"); baldr::GraphReader graph_reader(map.config.get_child("mjolnir")); - // check that there are no shortcut edges - EXPECT_ANY_THROW(gurka::findEdgeByNodes(graph_reader, layout, "A", "C")); - EXPECT_ANY_THROW(gurka::findEdgeByNodes(graph_reader, layout, "C", "A")); + auto loopEdge = std::get<0>(gurka::findEdgeByNodes(graph_reader, layout, "A", "B")); + auto shortcut = graph_reader.GetShortcut(loopEdge); + + EXPECT_FALSE(shortcut.Is_Valid()) << "Shortcuts found. Check the map."; } TEST(Shortcuts, ShortcutSpeed) { @@ -143,7 +136,7 @@ TEST(Shortcuts, ShortcutSpeed) { ASSERT_EQ(shortcut_infos.size(), 2); - for (auto const shortcut_info : shortcut_infos) { + for (auto const& shortcut_info : shortcut_infos) { auto const shortcutid = std::get<0>(shortcut_info); auto const shortcut_speed = std::get<1>(shortcut_info); auto const shortcut_truck_speed = std::get<2>(shortcut_info); @@ -165,7 +158,7 @@ TEST(Shortcuts, ShortcutSpeed) { } TEST(Shortcuts, TruckSpeedNotSet) { - // When truck speed is not set normal speed is used to calculate shortcut truck speed. + // When truck speed is not set normal speed is used to calculate shortcut truck speed // As a result it should be equal to normal shortcut speed. const std::string ascii_map = R"(A-----B\ \C @@ -205,6 +198,7 @@ TEST(Shortcuts, TruckSpeedNotSet) { if (!edge->is_shortcut() || !(edge->forwardaccess() & baldr::kAutoAccess)) continue; + // truck speed should be equal to edge speed by default EXPECT_EQ(edge->speed(), edge->truck_speed()); found_shortcut = true; } @@ -221,12 +215,12 @@ TEST(Shortcuts, TruckSpeedPartiallySet) { {"AB", {{"highway", "motorway"}, {"maxspeed", "100"}, - {"maxspeed:hgv", "90"}, + {"maxspeed:hgv", "100"}, {"name", "High street"}}}, {"BC", {{"highway", "motorway"}, {"maxspeed", "100"}, - {"maxspeed:hgv", "90"}, + {"maxspeed:hgv", "100"}, {"name", "High street"}}}, {"CD", {{"highway", "motorway"}, {"maxspeed", "100"}, {"name", "High street"}}}, {"DE", {{"highway", "motorway"}, {"maxspeed", "100"}, {"name", "High street"}}}, @@ -257,6 +251,51 @@ TEST(Shortcuts, TruckSpeedPartiallySet) { EXPECT_TRUE(found_shortcut) << "No shortcuts found. Check the map."; } +TEST(Shortcuts, TruckSpeedPartiallySetLow) { + // When truck speed is not set normal speed is used to calculate shortcut truck speed. + // As a result, when truck speed is set only for some of constituent edges, resulting speed is + // range from truck speed to normal speed + const std::string ascii_map = R"(A---B---C---D---E)"; + const gurka::ways ways = { + {"AB", + {{"highway", "motorway"}, + {"maxspeed", "100"}, + {"maxspeed:hgv", "60"}, + {"name", "High street"}}}, + {"BC", + {{"highway", "motorway"}, + {"maxspeed", "100"}, + {"maxspeed:hgv", "60"}, + {"name", "High street"}}}, + {"CD", {{"highway", "motorway"}, {"maxspeed", "100"}, {"name", "High street"}}}, + {"DE", {{"highway", "motorway"}, {"maxspeed", "100"}, {"name", "High street"}}}, + }; + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 10); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_shortcut"); + baldr::GraphReader reader(map.config.get_child("mjolnir")); + + bool found_shortcut = false; + auto const tileset = reader.GetTileSet(0); + for (const auto tileid : tileset) { + if (reader.OverCommitted()) + reader.Trim(); + + // for each edge in the tile + auto tile = reader.GetGraphTile(tileid); + for (size_t j = 0; j < tile->header()->directededgecount(); ++j) { + // skip it if its not a shortcut or the shortcut is one we will never traverse + const auto* edge = tile->directededge(j); + if (!edge->is_shortcut() || !(edge->forwardaccess() & baldr::kAutoAccess)) + continue; + + EXPECT_LT(60, edge->truck_speed()); + EXPECT_GT(85, edge->truck_speed()); + found_shortcut = true; + } + } + EXPECT_TRUE(found_shortcut) << "No shortcuts found. Check the map."; +} + TEST(Shortcuts, ShortcutsInBins) { const std::string ascii_map = R"(A---B---C)"; const gurka::ways ways = {{"AB", {{"highway", "motorway"}, {"name", "High Street"}}}, @@ -289,3 +328,113 @@ TEST(Shortcuts, ShortcutsInBins) { EXPECT_EQ(shortcut_cnt, 1); } + +TEST(Shortcuts, ShortcutRestrictions) { + using node_pairs = std::vector>; + + // the first line should produce only one HUGE shortcut, the second line one small one + const std::string ascii_map = R"( + A--B--C--D--E--F + + G--H--I--J--K--L + )"; + + std::map high_access_res = {{"highway", "motorway"}, {"hazmat", "yes"}, + {"maxweight", "30"}, {"maxheight", "6"}, + {"maxlength", "10"}, {"maxaxles", "10"}}; + std::map low_access_res = {{"highway", "motorway"}, {"hazmat", "no"}, + {"maxweight", "3"}, {"maxheight", "3"}, + {"maxlength", "4"}, {"maxaxles", "4"}}; + + gurka::ways ways = { + {"AB", {{"highway", "motorway"}}}, + {"BC", {{"highway", "motorway"}}}, + {"CD", high_access_res}, + {"DE", low_access_res}, + {"EF", {{"highway", "motorway"}}}, + + {"GH", {{"highway", "motorway"}}}, + {"HI", {{"highway", "motorway"}, {"motorcar:conditional", "yes @ 00:00-07:00"}}}, + {"IJ", {{"highway", "motorway"}, {"motorcar:conditional", "no @ 00:00-07:00"}}}, + {"JK", {{"highway", "motorway"}}}, + {"KL", {{"highway", "motorway"}}}, + }; + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 500); + auto map = gurka::buildtiles(layout, ways, {}, {}, + VALHALLA_BUILD_DIR "test/data/gurka_shortcut_restrictions"); + baldr::GraphReader reader(map.config.get_child("mjolnir")); + + // test we got the right shortcuts edges for the second line of the map + // implicitly means they were broken properly + for (const auto& pair : node_pairs{{"A", "F"}, {"F", "A"}, {"J", "L"}, {"L", "J"}}) { + const auto shortcut = gurka::findEdgeByNodes(reader, layout, pair.first, pair.second); + EXPECT_TRUE(std::get<1>(shortcut)->is_shortcut()); + } + + // test that the long shortcut has the strictest non-conditional access restrictions + const auto AF = gurka::findEdgeByNodes(reader, layout, "A", "F"); + const auto AF_res = + reader.GetGraphTile(std::get<0>(AF))->GetAccessRestrictions(std::get<0>(AF).id(), kAllAccess); + EXPECT_EQ(AF_res.size(), 5); + for (const auto& res : AF_res) { + uint64_t expected_value = 0; + switch (res.type()) { + case AccessType::kHazmat: + // should be false/0 + break; + case AccessType::kMaxWeight: + expected_value = strtoull(low_access_res["maxweight"].c_str(), nullptr, 10) * 100; + break; + case AccessType::kMaxHeight: + expected_value = strtoull(low_access_res["maxheight"].c_str(), nullptr, 10) * 100; + break; + case AccessType::kMaxLength: + expected_value = strtoull(low_access_res["maxlength"].c_str(), nullptr, 10) * 100; + break; + case AccessType::kMaxAxles: + expected_value = strtoull(low_access_res["maxaxles"].c_str(), nullptr, 10); + break; + default: + break; + } + EXPECT_EQ(res.value(), expected_value); + } + + // test the right edges are really superseded by a shortcut + // forward + for (const auto& pair : node_pairs{{"A", "B"}, {"J", "K"}}) { + const auto edge = gurka::findEdgeByNodes(reader, layout, pair.first, pair.second); + EXPECT_NE(std::get<1>(edge)->superseded(), 0); + } + // reverse + for (const auto& pair : node_pairs{{"F", "E"}, {"L", "K"}}) { + const auto edge = gurka::findEdgeByNodes(reader, layout, pair.first, pair.second); + EXPECT_NE(std::get<1>(edge)->superseded(), 0); + } + + // test that without those restrictions we're still building all shortcuts + + // remove those access restrictions + ways["CD"] = {{"highway", "motorway"}}; + ways["DE"] = {{"highway", "motorway"}}; + ways["HI"] = {{"highway", "motorway"}}; + ways["IJ"] = {{"highway", "motorway"}}; + auto map2 = gurka::buildtiles(layout, ways, {}, {}, + VALHALLA_BUILD_DIR "test/data/gurka_shortcut_without_restrictions"); + baldr::GraphReader reader2(map2.config.get_child("mjolnir")); + + // we don't have those small shortcuts anymore + for (const auto& end_node : {"C", "J"}) { + const auto shortcut = + gurka::findEdge(reader2, layout, "highway", end_node, baldr::GraphId{}, 0, true); + EXPECT_EQ(std::get<1>(shortcut), nullptr); + EXPECT_EQ(std::get<3>(shortcut), nullptr); + } + + // we did build the long shorcuts across all edges + for (const auto& pair : node_pairs{{"A", "F"}, {"F", "A"}, {"G", "L"}, {"L", "G"}}) { + const auto shortcut = gurka::findEdgeByNodes(reader2, layout, pair.first, pair.second); + EXPECT_TRUE(std::get<1>(shortcut)->is_shortcut()); + EXPECT_NEAR(std::get<1>(shortcut)->length(), 7500, 1); + } +} diff --git a/test/gurka/test_shortest.cc b/test/gurka/test_shortest.cc index 994163440f..3c6dfec049 100644 --- a/test/gurka/test_shortest.cc +++ b/test/gurka/test_shortest.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include using namespace valhalla; diff --git a/test/gurka/test_simple_restrictions.cc b/test/gurka/test_simple_restrictions.cc index 03eeec3363..70d3b48c0a 100644 --- a/test/gurka/test_simple_restrictions.cc +++ b/test/gurka/test_simple_restrictions.cc @@ -9,7 +9,6 @@ class SimpleRestrictions : public ::testing::Test { static gurka::map map; static void SetUpTestSuite() { - constexpr double gridsize = 100; const std::string ascii_map = R"( A---1B----C diff --git a/test/gurka/test_stop_signs.cc b/test/gurka/test_stop_signs.cc index c09b6fe3ed..135ece6580 100644 --- a/test/gurka/test_stop_signs.cc +++ b/test/gurka/test_stop_signs.cc @@ -1,3 +1,5 @@ +#include + #include "gurka.h" #include "test/test.h" #include @@ -72,8 +74,8 @@ TEST(Standalone, StopSigns) { const std::string workdir = "test/data/gurka_stop_signs"; - if (!filesystem::exists(workdir)) { - bool created = filesystem::create_directories(workdir); + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); EXPECT_TRUE(created); } diff --git a/test/gurka/test_tagged_values.cc b/test/gurka/test_tagged_values.cc index 12c78dd044..328aea5040 100644 --- a/test/gurka/test_tagged_values.cc +++ b/test/gurka/test_tagged_values.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) @@ -31,6 +30,7 @@ class TaggedValues : public ::testing::Test { H )"; + // TODO: does the graphparser make sure we dont ever try to set layer to 0 since its the default const gurka::ways ways = {{"AB", {{"highway", "motorway"}}}, {"BC", {{"highway", "motorway"}, diff --git a/test/gurka/test_time_dependent_tags.cc b/test/gurka/test_time_dependent_tags.cc index e21668014a..a8e1efb19d 100644 --- a/test/gurka/test_time_dependent_tags.cc +++ b/test/gurka/test_time_dependent_tags.cc @@ -58,7 +58,7 @@ TEST_F(TimeDependentTags, HourRestrictions) { "test/data/gurka_time_dependent_restrictions_hour", {{"mjolnir.timezone", VALHALLA_BUILD_DIR "test/data/tz.sqlite"}}); - for (int x = 0; x < working_hours.size(); ++x) { + for (size_t x = 0; x < working_hours.size(); ++x) { auto result = gurka::do_action(valhalla::Options::route, map, {"A", "F"}, "auto", {{"/date_time/type", "1"}, {"/date_time/value", working_hours.at(x)}}); @@ -81,7 +81,7 @@ TEST_F(TimeDependentTags, DayRestrictions) { "test/data/gurka_time_dependent_restrictions_day", {{"mjolnir.timezone", VALHALLA_BUILD_DIR "test/data/tz.sqlite"}}); - for (int x = 0; x < working_hours.size(); ++x) { + for (size_t x = 0; x < working_hours.size(); ++x) { auto result = gurka::do_action(valhalla::Options::route, map, {"A", "F"}, "auto", {{"/date_time/type", "1"}, {"/date_time/value", working_hours.at(x)}}); @@ -103,7 +103,7 @@ TEST_F(TimeDependentTags, DayAndHourRestrictions) { "test/data/gurka_time_dependent_restrictions_day_and_hour", {{"mjolnir.timezone", VALHALLA_BUILD_DIR "test/data/tz.sqlite"}}); - for (int x = 0; x < working_hours.size(); ++x) { + for (size_t x = 0; x < working_hours.size(); ++x) { auto result = gurka::do_action(valhalla::Options::route, map, {"A", "F"}, "auto", {{"/date_time/type", "1"}, {"/date_time/value", working_hours.at(x)}}); @@ -125,7 +125,7 @@ TEST_F(TimeDependentTags, ConditionalEdgeRestriction) { "test/data/gurka_time_dependent_restrictions_day_and_hour", {{"mjolnir.timezone", VALHALLA_BUILD_DIR "test/data/tz.sqlite"}}); - for (int x = 0; x < working_hours.size(); ++x) { + for (size_t x = 0; x < working_hours.size(); ++x) { auto result = gurka::do_action(valhalla::Options::route, map, {"A", "F"}, "auto", {{"/date_time/type", "1"}, {"/date_time/value", working_hours.at(x)}}); diff --git a/test/gurka/test_top_speed.cc b/test/gurka/test_top_speed.cc index 32ed3bff20..7e4e13173f 100644 --- a/test/gurka/test_top_speed.cc +++ b/test/gurka/test_top_speed.cc @@ -1,7 +1,6 @@ #include "baldr/graphconstants.h" #include "gurka.h" #include "test.h" -#include #include using namespace valhalla; @@ -80,11 +79,11 @@ TEST_F(TopSpeedTest, TaxiTopSpeed) { TEST_F(TopSpeedTest, ClampMaxSpeed) { Options options; rapidjson::Document dom; - rapidjson::SetValueByPointer(dom, "/top_speed", 500); + rapidjson::SetValueByPointer(dom, "/auto/top_speed", 500); + Costing co; options.set_costing_type(Costing::auto_); - auto& co = (*options.mutable_costings())[Costing::auto_]; - sif::ParseBaseCostOptions(*rapidjson::GetValueByPointer(dom, ""), &co, {}); + sif::ParseAutoCostOptions(dom, "/auto", &co); ASSERT_EQ(co.options().top_speed(), baldr::kMaxAssumedSpeed); } diff --git a/test/gurka/test_traffic.cc b/test/gurka/test_traffic.cc index 2cc7959220..f2618a7b08 100644 --- a/test/gurka/test_traffic.cc +++ b/test/gurka/test_traffic.cc @@ -4,10 +4,7 @@ #include "baldr/graphreader.h" #include "baldr/traffictile.h" -#include - #include -#include #include #include @@ -48,7 +45,7 @@ TEST(Traffic, BasicUpdates) { "Mostly just updates every edge in the file to 24km/h, except for one " "specific edge (B->D) where we simulate a closure (speed=0, congestion high)" << std::endl; - auto cb_setter_24kmh = [&map](baldr::GraphReader& reader, baldr::TrafficTile& tile, int index, + auto cb_setter_24kmh = [&map](baldr::GraphReader& reader, baldr::TrafficTile& tile, uint32_t index, valhalla::baldr::TrafficSpeed* current) -> void { baldr::GraphId tile_id(tile.header->tile_id); auto BD = gurka::findEdge(reader, map.nodes, "BD", "D", tile_id); @@ -77,7 +74,7 @@ TEST(Traffic, BasicUpdates) { std::cout << "[ ] Next, set the speed to the highest possible to ensure nothing breaks" << std::endl; - auto cb_setter_max = [&map](baldr::GraphReader& reader, baldr::TrafficTile& tile, int index, + auto cb_setter_max = [&map](baldr::GraphReader& reader, baldr::TrafficTile& tile, uint32_t index, baldr::TrafficSpeed* current) -> void { baldr::GraphId tile_id(tile.header->tile_id); auto BD = gurka::findEdge(reader, map.nodes, "BD", "D", tile_id); @@ -113,7 +110,6 @@ TEST(Traffic, BasicUpdates) { "it's noticed the changes in the live traffic file" << std::endl; { - auto result = gurka::do_action(valhalla::Options::route, map, {"B", "D"}, "auto", {{"/date_time/type", "0"}}, clean_reader); gurka::assert::osrm::expect_steps(result, {"BC", "CE", "DE"}); @@ -219,7 +215,7 @@ TEST(Traffic, CutGeoms) { ts.breakpoint1 = 127; auto cb_setter_speed = [&map, &ts](baldr::GraphReader& reader, baldr::TrafficTile& tile, - int index, baldr::TrafficSpeed* current) -> void { + uint32_t index, baldr::TrafficSpeed* current) -> void { baldr::GraphId tile_id(tile.header->tile_id); auto BD = gurka::findEdge(reader, map.nodes, "BD", "D", tile_id); current->breakpoint1 = 255; @@ -293,11 +289,9 @@ TEST(Traffic, CutGeoms) { ts.breakpoint2 = 200; auto cb_setter_speed = [&map, &ts](baldr::GraphReader& reader, baldr::TrafficTile& tile, - int index, baldr::TrafficSpeed* current) -> void { + uint32_t index, baldr::TrafficSpeed* current) -> void { baldr::GraphId tile_id(tile.header->tile_id); auto BD = gurka::findEdge(reader, map.nodes, "BD", "D", tile_id); - baldr::TrafficSpeed* existing = - const_cast(tile.speeds + index); current->breakpoint1 = 255; if (std::get<1>(BD) != nullptr && std::get<0>(BD).id() == index) { current->overall_encoded_speed = 0; @@ -377,7 +371,7 @@ TEST(Traffic, CutGeoms) { ts.congestion3 = 1; auto cb_setter_speed = [&map, &ts](baldr::GraphReader& reader, baldr::TrafficTile& tile, - int index, baldr::TrafficSpeed* current) -> void { + uint32_t index, baldr::TrafficSpeed* current) -> void { baldr::GraphId tile_id(tile.header->tile_id); auto BD = gurka::findEdge(reader, map.nodes, "BD", "D", tile_id); current->breakpoint1 = 255; @@ -496,7 +490,7 @@ TEST(Traffic, CutGeoms) { ts.congestion3 = 50; auto cb_setter_speed = [&map, &ts](baldr::GraphReader& reader, baldr::TrafficTile& tile, - int index, baldr::TrafficSpeed* current) -> void { + uint32_t index, baldr::TrafficSpeed* current) -> void { baldr::GraphId tile_id(tile.header->tile_id); auto BD = gurka::findEdge(reader, map.nodes, "BD", "D", tile_id); current->breakpoint1 = 255; @@ -738,7 +732,7 @@ TEST(Traffic, CutGeoms) { ts.congestion3 = 50; auto cb_setter_speed = [&map, &ts](baldr::GraphReader& reader, baldr::TrafficTile& tile, - int index, baldr::TrafficSpeed* current) -> void { + uint32_t index, baldr::TrafficSpeed* current) -> void { baldr::GraphId tile_id(tile.header->tile_id); auto BD = gurka::findEdge(reader, map.nodes, "BD", "D", tile_id); current->breakpoint1 = 255; @@ -849,7 +843,7 @@ TEST_F(WaypointsOnClosuresTest, DepartPointAtClosure) { // start from an edge that is closed in a one direction { LiveTrafficCustomize close_edge = [](baldr::GraphReader& reader, baldr::TrafficTile& tile, - int index, baldr::TrafficSpeed* current) -> void { + uint32_t index, baldr::TrafficSpeed* current) -> void { baldr::GraphId tile_id(tile.header->tile_id); auto BC = gurka::findEdge(reader, closure_map.nodes, "BC", "C", tile_id); @@ -868,7 +862,7 @@ TEST_F(WaypointsOnClosuresTest, DepartPointAtClosure) { // start from an edge that is closed in a one direction { LiveTrafficCustomize close_edge = [](baldr::GraphReader& reader, baldr::TrafficTile& tile, - int index, baldr::TrafficSpeed* current) -> void { + uint32_t index, baldr::TrafficSpeed* current) -> void { baldr::GraphId tile_id(tile.header->tile_id); auto CB = gurka::findEdge(reader, closure_map.nodes, "BC", "B", tile_id); @@ -889,7 +883,7 @@ TEST_F(WaypointsOnClosuresTest, DepartPointAtClosure) { // - depart point should be matched to the nearest node; { LiveTrafficCustomize close_edge = [](baldr::GraphReader& reader, baldr::TrafficTile& tile, - int index, baldr::TrafficSpeed* current) -> void { + uint32_t index, baldr::TrafficSpeed* current) -> void { baldr::GraphId tile_id(tile.header->tile_id); auto BC = gurka::findEdge(reader, closure_map.nodes, "BC", "C", tile_id); @@ -915,7 +909,7 @@ TEST_F(WaypointsOnClosuresTest, ArrivePointAtClosure) { // end at edge that is closed in a one direction { LiveTrafficCustomize close_edge = [](baldr::GraphReader& reader, baldr::TrafficTile& tile, - int index, baldr::TrafficSpeed* current) -> void { + uint32_t index, baldr::TrafficSpeed* current) -> void { baldr::GraphId tile_id(tile.header->tile_id); auto DA = gurka::findEdge(reader, closure_map.nodes, "DA", "A", tile_id); @@ -934,7 +928,7 @@ TEST_F(WaypointsOnClosuresTest, ArrivePointAtClosure) { // end at edge that is closed in a one direction { LiveTrafficCustomize close_edge = [](baldr::GraphReader& reader, baldr::TrafficTile& tile, - int index, baldr::TrafficSpeed* current) -> void { + uint32_t index, baldr::TrafficSpeed* current) -> void { baldr::GraphId tile_id(tile.header->tile_id); auto AD = gurka::findEdge(reader, closure_map.nodes, "DA", "D", tile_id); @@ -955,7 +949,7 @@ TEST_F(WaypointsOnClosuresTest, ArrivePointAtClosure) { // - arrive point should be matched to the nearest node; { LiveTrafficCustomize close_edge = [](baldr::GraphReader& reader, baldr::TrafficTile& tile, - int index, baldr::TrafficSpeed* current) -> void { + uint32_t index, baldr::TrafficSpeed* current) -> void { baldr::GraphId tile_id(tile.header->tile_id); auto DA = gurka::findEdge(reader, closure_map.nodes, "DA", "A", tile_id); @@ -986,7 +980,7 @@ TEST_F(WaypointsOnClosuresTest, IgnoreDepartPointAtClosure) { // the edge is closed in both directions { LiveTrafficCustomize close_edge = [](baldr::GraphReader& reader, baldr::TrafficTile& tile, - int index, baldr::TrafficSpeed* current) -> void { + uint32_t index, baldr::TrafficSpeed* current) -> void { baldr::GraphId tile_id(tile.header->tile_id); auto BC = std::get<0>(gurka::findEdge(reader, closure_map.nodes, "BC", "C")); auto CB = std::get<0>(gurka::findEdge(reader, closure_map.nodes, "BC", "B")); @@ -1004,7 +998,7 @@ TEST_F(WaypointsOnClosuresTest, IgnoreDepartPointAtClosure) { // the edge is closed in both directions but you say you dont care { LiveTrafficCustomize close_edge = [](baldr::GraphReader& reader, baldr::TrafficTile& tile, - int index, baldr::TrafficSpeed* current) -> void { + uint32_t index, baldr::TrafficSpeed* current) -> void { baldr::GraphId tile_id(tile.header->tile_id); auto BC = std::get<0>(gurka::findEdge(reader, closure_map.nodes, "BC", "C")); auto CB = std::get<0>(gurka::findEdge(reader, closure_map.nodes, "BC", "B")); diff --git a/test/gurka/test_traffic_signals.cc b/test/gurka/test_traffic_signals.cc index a45a6810d3..46611e3224 100644 --- a/test/gurka/test_traffic_signals.cc +++ b/test/gurka/test_traffic_signals.cc @@ -1,3 +1,5 @@ +#include + #include "gurka.h" #include "test/test.h" #include @@ -73,8 +75,8 @@ TEST(Standalone, TrafficSignals) { const std::string workdir = "test/data/gurka_traffic_signals"; - if (!filesystem::exists(workdir)) { - bool created = filesystem::create_directories(workdir); + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); EXPECT_TRUE(created); } diff --git a/test/gurka/test_traffic_smoothing.cc b/test/gurka/test_traffic_smoothing.cc index 820f42de76..a4fc2664ee 100644 --- a/test/gurka/test_traffic_smoothing.cc +++ b/test/gurka/test_traffic_smoothing.cc @@ -1,5 +1,6 @@ #include "gurka.h" #include "test.h" +#include using namespace valhalla; @@ -381,7 +382,7 @@ TEST_F(RouteWithTraffic, LiveOnButNotUsed) { etas.emplace_back(calculate_eta(result)); } } - for (int i = 0; i < etas.size() - 1; ++i) { + for (size_t i = 0; i < etas.size() - 1; ++i) { // Live traffic is not used, algos should return the same ETA. EXPECT_EQ(etas[i], etas[i + 1]); } @@ -558,7 +559,7 @@ class MapMatchWithTraffic : public ::testing::Test { traffic_speed->breakpoint1 = 255; }); - test::customize_historical_traffic(map.config, [](DirectedEdge& e) { + test::customize_historical_traffic(map.config, [](DirectedEdge&) { // speeds for every 5 min bucket of the week std::array historical; historical.fill(6); diff --git a/test/gurka/test_truck.cc b/test/gurka/test_truck.cc new file mode 100644 index 0000000000..132dce2238 --- /dev/null +++ b/test/gurka/test_truck.cc @@ -0,0 +1,349 @@ +#include "gurka.h" +#include "test.h" +#include + +using namespace valhalla; + +inline float getDuration(const valhalla::Api& route) { + return route.directions().routes(0).legs(0).summary().time(); +} + +class TruckRestrictionTest : public ::testing::TestWithParam> { +protected: + static gurka::map map; + + static void SetUpTestSuite() { + constexpr double gridsize = 10; + + const std::string ascii_map = R"( + A---B---C---D + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "residential"}}}, + // all restrictions should be higher than our defaults, so we can actually see the impact of + // any single one + {"BC", + {{"highway", "residential"}, + {"maxheight", "5"}, + {"maxlength", "25"}, + {"maxwidth", "3"}, + {"hazmat", "destination"}, + {"maxaxles", "8"}, + {"maxaxleload", "10"}}}, + {"CD", {{"highway", "residential"}}}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/truck_restrictions"); + } +}; + +gurka::map TruckRestrictionTest::map = {}; + +TEST_P(TruckRestrictionTest, NotAllowed) { + std::string option, v; + std::tie(option, v) = GetParam(); + + // "no path could be found for input" should be raised if we exceed this costing option + try { + gurka::do_action(Options::route, map, {"A", "D"}, "truck", + {{"/costing_options/truck/" + option, v}}); + FAIL() << "Expected no path to be found"; + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + }; +} + +TEST_F(TruckRestrictionTest, Allowed) { + // without setting a costing option, we should get a path + auto res = gurka::do_action(Options::route, map, {"A", "D"}, "truck"); + gurka::assert::raw::expect_path(res, {"AB", "BC", "CD"}); +} + +INSTANTIATE_TEST_SUITE_P(TruckRestrictions, + TruckRestrictionTest, + ::testing::Values(std::pair{"height", "6"}, + std::pair{"width", "4"}, + std::pair{"length", "30"}, + std::pair{"hazmat", "1"}, + std::pair{"axle_load", "11"}, + std::pair{"axle_count", "10"})); + +TEST(TruckSpeed, MaxTruckSpeed) { + constexpr double gridsize = 500; + + const std::string ascii_map = R"( + A----------B + )"; + + const gurka::ways ways = {{"AB", {{"highway", "motorway"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + gurka::map map = gurka::buildtiles(layout, ways, {}, {}, "test/data/truckspeed"); + + map.config.put("mjolnir.traffic_extract", "test/data/truckspeed/traffic.tar"); + + test::build_live_traffic_data(map.config); + + valhalla::Api default_route = + gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", {}); + + // should be clamped to edge speed + valhalla::Api clamped_top_speed_route = + gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", + {{"/costing_options/truck/top_speed", "115"}, + {"/date_time/type", "0"}, + {"/date_time/value", "current"}}); + + valhalla::Api low_top_speed_route = + gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", + {{"/costing_options/truck/top_speed", "70"}, + {"/date_time/type", "0"}, + {"/date_time/value", "current"}}); + + test::customize_live_traffic_data(map.config, [&](baldr::GraphReader& reader, + baldr::TrafficTile& tile, u_int32_t index, + valhalla::baldr::TrafficSpeed* traffic_speed) { + baldr::GraphId tile_id(tile.header->tile_id); + auto AB = gurka::findEdge(reader, map.nodes, "AB", "B", tile_id); + + if (std::get<1>(AB) != nullptr && std::get<0>(AB).id() == index) { + traffic_speed->overall_encoded_speed = 140 >> 1; + traffic_speed->breakpoint1 = 255; + traffic_speed->encoded_speed1 = 140 >> 1; + } else { + traffic_speed->overall_encoded_speed = UNKNOWN_TRAFFIC_SPEED_RAW - 1; + } + }); + + valhalla::Api modified_traffic_route = + gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", + {{"/date_time/type", "0"}, + {"/date_time/value", "current"}, + {"/costing_options/truck/speed_types/0", "current"}}); + + test::customize_live_traffic_data(map.config, [&](baldr::GraphReader& reader, + baldr::TrafficTile& tile, u_int32_t index, + valhalla::baldr::TrafficSpeed* traffic_speed) { + baldr::GraphId tile_id(tile.header->tile_id); + auto AB = gurka::findEdge(reader, map.nodes, "AB", "B", tile_id); + + if (std::get<1>(AB) != nullptr && std::get<0>(AB).id() == index) { + traffic_speed->overall_encoded_speed = 50 >> 1; + traffic_speed->breakpoint1 = 255; + traffic_speed->encoded_speed1 = 50 >> 1; + } else { + traffic_speed->overall_encoded_speed = UNKNOWN_TRAFFIC_SPEED_RAW - 1; + } + }); + + valhalla::Api modified_traffic_low_speed_route = + gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", + {{"/date_time/type", "0"}, + {"/date_time/value", "current"}, + {"/costing_options/truck/speed_types/0", "current"}}); + + gurka::assert::raw::expect_path(default_route, {"AB"}); + + auto default_time = getDuration(default_route); + auto clamped_top_speed_time = getDuration(clamped_top_speed_route); + auto low_top_speed_time = getDuration(low_top_speed_route); + auto traffic_time = getDuration(modified_traffic_route); + auto traffic_low_speed_time = getDuration(modified_traffic_low_speed_route); + + // both default and set top_speeds exceeds edge speed, so use edge speed in both cases + ASSERT_EQ(default_time, clamped_top_speed_time); + + // expect a trip to take longer when a low top speed is set + ASSERT_LT(default_time, low_top_speed_time); + + // was clamped to 120 KPH, traffic speed was set to 140 + ASSERT_EQ(5500 / (120 / 3.6), traffic_time); + + // expect lower traffic speeds to lead to a lower duration + ASSERT_LT(traffic_time, traffic_low_speed_time); +} + +TEST(TruckSpeed, TopSpeed) { + constexpr double gridsize = 500; + + const std::string ascii_map = R"( + A----B + )"; + + const gurka::ways ways = {{"AB", {{"highway", "motorway"}, {"maxspeed", "120"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + gurka::map map = gurka::buildtiles(layout, ways, {}, {}, "test/data/truckspeed"); + + valhalla::Api default_route = gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck"); + + valhalla::Api top_speed_route = gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", + {{"/costing_options/truck/top_speed", "110"}}); + + valhalla::Api default_top_speed_route = + gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", + {{"/costing_options/truck/top_speed", "120"}}); + + auto default_dur = getDuration(default_route); + auto top_speed_dur = getDuration(top_speed_route); + auto default_top_speed_dur = getDuration(default_top_speed_route); + + ASSERT_LT(default_dur, top_speed_dur); + ASSERT_EQ(default_dur, default_top_speed_dur); +} + +// tag name, tag value, costing opt name, costing opt value, forward +using asymmetric_restriction_params_t = + std::tuple; + +class TruckAsymmetricAccessRestrictions + : public ::testing::TestWithParam { + +protected: + static std::string ascii_map; + + static void SetUpTestSuite() { + + ascii_map = R"( + A---B---C---D + )"; + } +}; +std::string TruckAsymmetricAccessRestrictions::ascii_map = {}; + +// test forward/backward only access restrictions +TEST_P(TruckAsymmetricAccessRestrictions, ForwardBackward) { + std::string tag_name; + std::string tag_value; + std::string co_opt; + std::string co_value; + bool forward; + std::tie(tag_name, tag_value, co_opt, co_value, forward) = GetParam(); + + std::string tag_name_full = tag_name + (forward ? ":forward" : ":backward"); + + // middle edge has a forward/backward only access restriction + const gurka::ways ways = { + {"AB", {{"highway", "residential"}}}, + {"BC", {{"highway", "residential"}, {tag_name_full, tag_value}}}, + {"CD", {{"highway", "residential"}}}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 10); + gurka::map map = + gurka::buildtiles(layout, ways, {}, {}, "test/data/truck_directed_access_restrictions"); + + // we test a route in the direction of the access restriction, should fail... + std::vector locations_fail = {"A", "D"}; + // ...but should work in the other direction... + std::vector locations_success = {"D", "A"}; + // ...and this should be the succeeding route's path + std::vector expected_path_success = {"CD", "BC", "AB"}; + + // reverse case if backward + if (!forward) { + locations_fail = {"D", "A"}; + locations_success = {"A", "D"}; + expected_path_success = {"AB", "BC", "CD"}; + } + + const std::unordered_map costing_opts = {{co_opt, co_value}}; + try { + std::vector> options; + gurka::do_action(Options::route, map, locations_fail, "truck", costing_opts); + FAIL() << "Expected no path to be found"; + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + }; + + // other direction should work + const auto success_route = + gurka::do_action(Options::route, map, locations_success, "truck", costing_opts); + gurka::assert::raw::expect_path(success_route, expected_path_success); +} + +std::vector buildParams() { + std::vector params; + + params.push_back(std::make_tuple("maxheight", "4", "/costing_options/truck/height", "7", true)); + params.push_back(std::make_tuple("maxheight", "4", "/costing_options/truck/height", "7", false)); + params.push_back(std::make_tuple("maxlength", "4", "/costing_options/truck/length", "5", true)); + params.push_back(std::make_tuple("maxlength", "4", "/costing_options/truck/length", "5", false)); + params.push_back(std::make_tuple("maxwidth", "2", "/costing_options/truck/length", "3", true)); + params.push_back(std::make_tuple("maxwidth", "2", "/costing_options/truck/length", "3", false)); + params.push_back(std::make_tuple("hazmat", "no", "/costing_options/truck/hazmat", "1", true)); + + return params; +}; + +INSTANTIATE_TEST_SUITE_P(TruckAsymmetricAccessRestrictionsTest, + TruckAsymmetricAccessRestrictions, + ::testing::ValuesIn(buildParams())); + +TEST(Standalone, TruckSpeeds) { + constexpr double gridsize = 500; + + const std::string ascii_map = R"( + A---B---C---D---E + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "residential"}, {"maxspeed:hgv:forward", "15"}}}, + {"BC", {{"highway", "residential"}, {"maxspeed:hgv:backward", "15"}}}, + // truck_speed > truck_speed_forward + {"CD", {{"highway", "residential"}, {"maxspeed:hgv", "20"}, {"maxspeed:hgv:forward", "15"}}}, + // truck_speed < truck_speed_forward + {"DE", {{"highway", "residential"}, {"maxspeed:hgv", "15"}, {"maxspeed:hgv:forward", "20"}}}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + gurka::map map = gurka::buildtiles(layout, ways, {}, {}, "test/data/truck_speed"); + + baldr::GraphReader reader(map.config.get_child("mjolnir")); + const DirectedEdge *de_fwd, *de_rev, *de_both_1, *de_both_2; + std::tie(std::ignore, de_fwd) = gurka::findEdgeByNodes(reader, layout, "A", "B"); + std::tie(std::ignore, de_rev) = gurka::findEdgeByNodes(reader, layout, "C", "B"); + std::tie(std::ignore, de_both_1) = gurka::findEdgeByNodes(reader, layout, "C", "D"); + std::tie(std::ignore, de_both_2) = gurka::findEdgeByNodes(reader, layout, "D", "E"); + EXPECT_EQ(de_fwd->truck_speed(), 15); + EXPECT_EQ(de_rev->truck_speed(), 15); + EXPECT_EQ(de_both_1->truck_speed(), 15); + EXPECT_EQ(de_both_2->truck_speed(), 15); +} + +TEST(Standalone, UseTruckRoute) { + constexpr double gridsize = 500; + + const std::string ascii_map = R"( + A------B + | | + | | + | | + | | + | C + | | + E------D + )"; + + const gurka::ways ways = {{"AB", {{"highway", "residential"}}}, + {"AE", {{"highway", "residential"}, {"hgv", "designated"}}}, + {"BC", {{"highway", "residential"}}}, + {"CD", {{"highway", "residential"}}}, + {"ED", {{"highway", "residential"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + gurka::map map = gurka::buildtiles(layout, ways, {}, {}, "test/data/use_truck_route"); + + std::unordered_map options = { + {"/costing_options/truck/use_truck_route", "1"}}; + + valhalla::Api default_route = gurka::do_action(valhalla::Options::route, map, {"A", "C"}, "truck"); + + valhalla::Api use_truck_route = + gurka::do_action(valhalla::Options::route, map, {"A", "C"}, "truck", options); + + gurka::assert::raw::expect_path(default_route, {"AB", "BC"}); + gurka::assert::raw::expect_path(use_truck_route, {"AE", "ED", "CD"}); +} diff --git a/test/gurka/test_truck_restrictions.cc b/test/gurka/test_truck_restrictions.cc deleted file mode 100644 index 6ff2a1fd67..0000000000 --- a/test/gurka/test_truck_restrictions.cc +++ /dev/null @@ -1,65 +0,0 @@ -#include "gurka.h" -#include - -using namespace valhalla; - -class TruckRestrictionTest : public ::testing::TestWithParam> { -protected: - static gurka::map map; - - static void SetUpTestSuite() { - constexpr double gridsize = 10; - - const std::string ascii_map = R"( - A---B---C---D - )"; - - const gurka::ways ways = { - {"AB", {{"highway", "residential"}}}, - // all restrictions should be higher than our defaults, so we can actually see the impact of - // any single one - {"BC", - {{"highway", "residential"}, - {"maxheight", "5"}, - {"maxlength", "25"}, - {"maxwidth", "3"}, - {"hazmat", "destination"}, - {"maxaxles", "8"}, - {"maxaxleload", "10"}}}, - {"CD", {{"highway", "residential"}}}, - }; - - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); - map = gurka::buildtiles(layout, ways, {}, {}, "test/data/truck_restrictions"); - } -}; - -gurka::map TruckRestrictionTest::map = {}; - -TEST_P(TruckRestrictionTest, NotAllowed) { - std::string option, v; - std::tie(option, v) = GetParam(); - - // "no path could be found for input" should be raised if we exceed this costing option - try { - gurka::do_action(Options::route, map, {"A", "D"}, "truck", - {{"/costing_options/truck/" + option, v}}); - } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { - FAIL() << "Expected valhalla_exception_t."; - }; -} - -TEST_F(TruckRestrictionTest, Allowed) { - // without setting a costing option, we should get a path - auto res = gurka::do_action(Options::route, map, {"A", "D"}, "truck"); - gurka::assert::raw::expect_path(res, {"AB", "BC", "CD"}); -} - -INSTANTIATE_TEST_SUITE_P(TruckRestrictions, - TruckRestrictionTest, - ::testing::Values(std::pair{"height", "6"}, - std::pair{"width", "4"}, - std::pair{"length", "30"}, - std::pair{"hazmat", "true"}, - std::pair{"axle_load", "11"}, - std::pair{"axle_count", "10"})); diff --git a/test/gurka/test_turn_lanes.cc b/test/gurka/test_turn_lanes.cc index 785a8d0f9d..3331cefa22 100644 --- a/test/gurka/test_turn_lanes.cc +++ b/test/gurka/test_turn_lanes.cc @@ -11,7 +11,7 @@ void validate_turn_lanes(valhalla::Api& result, ASSERT_EQ(expected_lanes.size(), etl.node_size() - 1); - for (size_t i = 1; i < etl.node_size(); ++i) { + for (int i = 1; i < etl.node_size(); ++i) { auto prev_edge = etl.GetPrevEdge(i); ASSERT_TRUE(prev_edge) << "Expected prev_edge on node index " << i; ASSERT_EQ(prev_edge->turn_lanes_size(), expected_lanes[i - 1].first) diff --git a/test/gurka/test_use.cc b/test/gurka/test_use.cc index 4370d067ac..48a92cc2bc 100644 --- a/test/gurka/test_use.cc +++ b/test/gurka/test_use.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_use_direction_on_ways.cc b/test/gurka/test_use_direction_on_ways.cc index 7fb750a37c..a2a149403f 100644 --- a/test/gurka/test_use_direction_on_ways.cc +++ b/test/gurka/test_use_direction_on_ways.cc @@ -12,7 +12,6 @@ class UseDirectionOnWays : public ::testing::Test { static gurka::map map; static void SetUpTestSuite() { - constexpr double gridsize = 100; const std::string ascii_map = R"( A--B diff --git a/test/gurka/test_use_lit.cc b/test/gurka/test_use_lit.cc index 8f10c28754..064b310dd3 100644 --- a/test/gurka/test_use_lit.cc +++ b/test/gurka/test_use_lit.cc @@ -1,7 +1,6 @@ #include "baldr/graphconstants.h" #include "gurka.h" #include "test.h" -#include #include using namespace valhalla; @@ -48,7 +47,6 @@ TEST_F(UseLitTest, LitHighway) { valhalla::Api default_route = gurka::do_action(valhalla::Options::route, speed_map, {"A", "C"}, "pedestrian"); - float default_time = getDuration(default_route); valhalla::Api lit_route = gurka::do_action(valhalla::Options::route, speed_map, {"A", "C"}, "pedestrian", options); diff --git a/test/gurka/test_via_waypoints.cc b/test/gurka/test_via_waypoints.cc index 78f591ae33..9f2f478270 100644 --- a/test/gurka/test_via_waypoints.cc +++ b/test/gurka/test_via_waypoints.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_warnings.cc b/test/gurka/test_warnings.cc index 4abde53612..f2674b45ed 100644 --- a/test/gurka/test_warnings.cc +++ b/test/gurka/test_warnings.cc @@ -1,6 +1,5 @@ #include "gurka.h" #include -#include using namespace valhalla; diff --git a/test/gurka/test_yield_signs.cc b/test/gurka/test_yield_signs.cc index de1fe8f803..771eaa53a3 100644 --- a/test/gurka/test_yield_signs.cc +++ b/test/gurka/test_yield_signs.cc @@ -1,3 +1,5 @@ +#include + #include "gurka.h" #include "test/test.h" #include @@ -72,8 +74,8 @@ TEST(Standalone, yieldSigns) { const std::string workdir = "test/data/gurka_yeild_signs"; - if (!filesystem::exists(workdir)) { - bool created = filesystem::create_directories(workdir); + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); EXPECT_TRUE(created); } diff --git a/test/http_tiles.cc b/test/http_tiles.cc index e06438c4af..eb924226f1 100644 --- a/test/http_tiles.cc +++ b/test/http_tiles.cc @@ -3,12 +3,11 @@ #include "baldr/curl_tilegetter.h" #include "baldr/graphtile.h" #include "tyr/actor.h" -#include "valhalla/filesystem.h" #include "valhalla/tile_server.h" #include -#include +#include #include #include #include @@ -71,10 +70,10 @@ TEST(HttpTiles, test_no_cache_gz) { class HttpTilesWithCache : public ::testing::Test { protected: void SetUp() override { - filesystem::remove_all("url_tile_cache"); + std::filesystem::remove_all("url_tile_cache"); } void TearDown() override { - filesystem::remove_all("url_tile_cache"); + std::filesystem::remove_all("url_tile_cache"); } }; @@ -126,9 +125,9 @@ void test_tile_download(size_t tile_count, size_t curler_count, size_t thread_co std::vector threads; threads.reserve(thread_count); - for (int thread_i = 0; thread_i < thread_count; ++thread_i) { + for (size_t thread_i = 0; thread_i < thread_count; ++thread_i) { threads.emplace_back([&, thread_i]() { - for (int tile_i = 0; tile_i < tile_count; ++tile_i) { + for (size_t tile_i = 0; tile_i < tile_count; ++tile_i) { bool is_for_this_thread = ((tile_i % thread_count) == thread_i); if (!is_for_this_thread) { continue; @@ -173,9 +172,9 @@ void test_graphreader_tile_download(size_t tile_count, size_t curler_count, size std::vector threads; threads.reserve(thread_count); - for (int thread_i = 0; thread_i < thread_count; ++thread_i) { + for (size_t thread_i = 0; thread_i < thread_count; ++thread_i) { threads.emplace_back([&, thread_i]() { - for (int tile_i = 0; tile_i < tile_count; ++tile_i) { + for (size_t tile_i = 0; tile_i < tile_count; ++tile_i) { bool is_for_this_thread = ((tile_i % thread_count) == thread_i); if (!is_for_this_thread) { continue; diff --git a/test/incident_loading.cc b/test/incident_loading.cc index 8fc72a076e..6d53d39ee9 100644 --- a/test/incident_loading.cc +++ b/test/incident_loading.cc @@ -23,14 +23,14 @@ class incident_loading : public testing::Test { struct testable_singleton : public incident_singleton_t { // make an incident singleton and inject a watch function that either passes or fails initialization - testable_singleton(const boost::property_tree::ptree& config, bool intialize) + testable_singleton(const boost::property_tree::ptree& config, bool initialize) : incident_singleton_t(config, {}, - [intialize](boost::property_tree::ptree, - std::unordered_set, - std::shared_ptr state, - std::function) { - state->initialized.store(intialize); + [initialize](boost::property_tree::ptree, + std::unordered_set, + std::shared_ptr state, + std::function) { + state->initialized.store(initialize); state->signal.notify_one(); }) { } @@ -80,7 +80,7 @@ TEST_F(incident_loading, update_tile) { // no slot exists std::shared_ptr state{new testable_singleton::state_t{}}; ASSERT_TRUE(testable_singleton::update_tile(state, baldr::GraphId(0), {})) - << " unable to update nonexistant tile"; + << " unable to update nonexistent tile"; ASSERT_TRUE(state->cache.count(baldr::GraphId(0))) << " cannot find new tile in cache"; ASSERT_TRUE(state->cache.find(baldr::GraphId(0))->second == nullptr) << " tile should be null"; diff --git a/test/instructions.cc b/test/instructions.cc index b2a23f7e79..74d386c8b5 100644 --- a/test/instructions.cc +++ b/test/instructions.cc @@ -1,6 +1,4 @@ #include -#include -#include #include #include "baldr/rapidjson_utils.h" diff --git a/test/isochrone.cc b/test/isochrone.cc index 4c8d8c0441..667402fb6b 100644 --- a/test/isochrone.cc +++ b/test/isochrone.cc @@ -14,6 +14,10 @@ #include #include +#ifdef ENABLE_GDAL +#include +#endif + using point_type = boost::geometry::model::d2::point_xy; using polygon_type = boost::geometry::model::polygon; using boost::geometry::within; @@ -30,13 +34,33 @@ using rp = rapidjson::Pointer; namespace { -const auto config = test::make_config("test/data/utrecht_tiles"); +const auto cfg = test::make_config(VALHALLA_BUILD_DIR "test/data/utrecht_tiles", + {{"service_limits.isochrone.max_locations", "2"}}); void check_coords(const rapidjson::Value& a, const rapidjson::Value& b) { EXPECT_NEAR(a.GetArray()[0].GetDouble(), b.GetArray()[0].GetDouble(), 0.00002); EXPECT_NEAR(a.GetArray()[1].GetDouble(), b.GetArray()[1].GetDouble(), 0.00002); } +void test_iso_shape_equality(const rapidjson::GenericArray& actual_geom, + const rapidjson::GenericArray& expected_geom) { + // different platforms can end up having some slightly different floating point wobble + // to avoid failing tests we measure shape similarity and fail if its too far out of whack + std::vector actual, expected; + for (size_t j = 0; j < std::max(expected_geom.Size(), actual_geom.Size()); ++j) { + if (j < actual_geom.Size()) { + auto c = actual_geom[j].GetArray(); + actual.emplace_back(c[0].GetDouble(), c[1].GetDouble()); + } + if (j < expected_geom.Size()) { + auto c = expected_geom[j].GetArray(); + expected.emplace_back(c[0].GetDouble(), c[1].GetDouble()); + } + } + // TODO: maybe use macro here to have higher tolerance only apply on ARM arch + ASSERT_TRUE(test::shape_equality(actual, expected, 41)); +} + void try_isochrone(loki_worker_t& loki_worker, thor_worker_t& thor_worker, const std::string& test_request, @@ -77,27 +101,54 @@ void try_isochrone(loki_worker_t& loki_worker, rp("/features/" + std::to_string(i) + "/geometry/type").Get(expected_response)->GetString(); ASSERT_EQ(actual_type, expected_type); + std::string coord_selector = "/features/" + std::to_string(i) + "/geometry/coordinates"; // point is special if (expected_type == "Point") { check_coords(*rp("/features/" + std::to_string(i) + "/geometry/coordinates").Get(response), *rp("/features/" + std::to_string(i) + "/geometry/coordinates") .Get(expected_response)); } // iteration required - else { + else if (expected_type == "LineString" || expected_type == "Polygon" || + expected_type == "MultiPoint") { // same geom appx - auto actual_geom = rp("/features/" + std::to_string(i) + "/geometry/coordinates" + - (actual_type == "Polygon" ? "/0" : "")) - .Get(response) - ->GetArray(); - auto expected_geom = rp("/features/" + std::to_string(i) + "/geometry/coordinates" + - (expected_type == "Polygon" ? "/0" : "")) - .Get(expected_response) - ->GetArray(); - ASSERT_EQ(actual_geom.Size(), expected_geom.Size()); - for (size_t j = 0; j < expected_geom.Size(); ++j) { - auto actual_coord = actual_geom[j].GetArray(); - auto expected_coord = expected_geom[j].GetArray(); - check_coords(actual_coord, expected_coord); + uint32_t size = (actual_type == "LineString" || actual_type == "MultiPoint") + ? 1 + : rp(coord_selector).Get(response)->GetArray().Size(); + for (uint32_t j = 0; j < size; ++j) { + auto actual_geom = + rp(coord_selector + (actual_type == "Polygon" ? "/" + std::to_string(j) : "")) + .Get(response) + ->GetArray(); + auto expected_geom = + rp(coord_selector + (expected_type == "Polygon" ? "/" + std::to_string(j) : "")) + .Get(expected_response) + ->GetArray(); + test_iso_shape_equality(actual_geom, expected_geom); + } + } else { // MultiPolygon + uint32_t actual_poly_size = rp(coord_selector).Get(response)->GetArray().Size(); + uint32_t expected_poly_size = rp(coord_selector).Get(expected_response)->GetArray().Size(); + + ASSERT_EQ(actual_poly_size, expected_poly_size); + + // for each polygon + for (uint32_t j = 0; j < actual_poly_size; ++j) { + + uint32_t actual_ring_count = rp(coord_selector).Get(response)->GetArray()[j].Size(); + uint32_t expected_ring_count = rp(coord_selector).Get(response)->GetArray()[j].Size(); + ASSERT_EQ(actual_ring_count, expected_ring_count); + // test equality of each ring + for (uint32_t k = 0; k < actual_ring_count; ++k) { + auto actual_geom = rp(coord_selector + "/" + std::to_string(j) + "/" + std::to_string(k)) + .Get(response) + ->GetArray(); + auto expected_geom = rp(coord_selector + "/" + std::to_string(j) + "/" + std::to_string(k)) + .Get(expected_response) + ->GetArray(); + + // tests exterior ring equality + test_iso_shape_equality(actual_geom, expected_geom); + } } } } @@ -131,15 +182,15 @@ std::vector polygon_from_geojson(const std::string& geojson) { TEST(Isochrones, Basic) { // Test setup - loki_worker_t loki_worker(config); - thor_worker_t thor_worker(config); - GraphReader reader(config.get_child("mjolnir")); + loki_worker_t loki_worker(cfg); + thor_worker_t thor_worker(cfg); + GraphReader reader(cfg.get_child("mjolnir")); { const auto request = R"({"locations":[{"lat":52.078937,"lon":5.115321}],"costing":"auto","contours":[{"time":9.1}],"polygons":false,"generalize":55})"; const auto expected = - R"({"features":[{"properties":{"fill":"#bf4040","fillOpacity":0.33,"fill-opacity":0.33,"fillColor":"#bf4040","color":"#bf4040","contour":9.1,"opacity":0.33,"metric":"time"},"geometry":{"coordinates":[[5.042321,52.127328],[5.041287,52.126971],[5.041162,52.126096],[5.040250,52.126008],[5.040123,52.125135],[5.038321,52.124230],[5.038155,52.123104],[5.035757,52.121937],[5.034321,52.119336],[5.029719,52.120335],[5.027321,52.122071],[5.025528,52.122144],[5.025321,52.123106],[5.023541,52.123157],[5.022162,52.124937],[5.022291,52.123907],[5.023244,52.122860],[5.024983,52.122599],[5.025321,52.121673],[5.026949,52.121565],[5.028321,52.119350],[5.029770,52.119386],[5.030321,52.118047],[5.031738,52.118354],[5.031768,52.117491],[5.030068,52.117190],[5.029028,52.112644],[5.032109,52.116149],[5.037321,52.115662],[5.044771,52.112387],[5.047321,52.112088],[5.047520,52.111136],[5.049379,52.110995],[5.047712,52.109937],[5.053321,52.103702],[5.057006,52.103622],[5.058321,52.102515],[5.061389,52.102937],[5.060882,52.100376],[5.059321,52.099179],[5.056855,52.098937],[5.057948,52.098564],[5.057822,52.096937],[5.059321,52.096398],[5.059890,52.094937],[5.062669,52.096285],[5.063657,52.092601],[5.059321,52.093638],[5.057934,52.090324],[5.054848,52.088410],[5.051513,52.088129],[5.051321,52.089053],[5.051073,52.088185],[5.048321,52.087370],[5.047321,52.088623],[5.046687,52.086571],[5.044576,52.084937],[5.048713,52.085329],[5.049321,52.082083],[5.050409,52.083849],[5.053317,52.081933],[5.055321,52.082660],[5.071321,52.081546],[5.072536,52.078937],[5.070321,52.075645],[5.068321,52.076598],[5.064321,52.076524],[5.063321,52.077492],[5.060985,52.077273],[5.059232,52.073848],[5.060461,52.073797],[5.062321,52.076406],[5.063321,52.075371],[5.066556,52.075172],[5.067628,52.072937],[5.067514,52.070744],[5.065321,52.069631],[5.061321,52.069611],[5.060321,52.070600],[5.054934,52.070937],[5.056828,52.072937],[5.057903,52.077355],[5.060418,52.078937],[5.059321,52.081790],[5.057766,52.078492],[5.054557,52.077937],[5.055847,52.077463],[5.055644,52.074614],[5.053321,52.074166],[5.053916,52.077937],[5.053321,52.078215],[5.052921,52.076337],[5.051321,52.076004],[5.049321,52.079349],[5.049113,52.078145],[5.047811,52.078427],[5.047736,52.079522],[5.048997,52.079937],[5.048420,52.082036],[5.046798,52.078460],[5.044152,52.077106],[5.044305,52.074921],[5.043205,52.075937],[5.043272,52.074888],[5.043588,52.074204],[5.043516,52.072937],[5.042321,52.072093],[5.040321,52.073230],[5.034962,52.073578],[5.034321,52.078500],[5.028415,52.075937],[5.022321,52.075362],[5.021967,52.073583],[5.026171,52.072937],[5.027090,52.069706],[5.030321,52.071554],[5.037321,52.071095],[5.043321,52.069471],[5.047584,52.069200],[5.049321,52.068151],[5.054548,52.068164],[5.056321,52.064931],[5.059321,52.067213],[5.063500,52.066116],[5.063749,52.064365],[5.066245,52.061937],[5.066622,52.058937],[5.065629,52.057937],[5.067742,52.057358],[5.067482,52.054937],[5.068412,52.054846],[5.069986,52.058272],[5.073724,52.057937],[5.070994,52.057264],[5.069724,52.051534],[5.066166,52.051937],[5.066182,52.050798],[5.067807,52.050423],[5.070321,52.046926],[5.070489,52.047769],[5.072941,52.046557],[5.074321,52.047386],[5.075321,52.046360],[5.077321,52.046293],[5.078321,52.047273],[5.079321,52.046325],[5.080321,52.047249],[5.082321,52.046259],[5.084321,52.046217],[5.085321,52.047210],[5.089321,52.046295],[5.090321,52.047254],[5.091321,52.046299],[5.093321,52.046308],[5.094022,52.048236],[5.098321,52.046241],[5.099150,52.049108],[5.101179,52.051079],[5.102321,52.050145],[5.104104,52.050154],[5.106321,52.050195],[5.107564,52.051180],[5.107863,52.048479],[5.111321,52.048205],[5.112321,52.046211],[5.113156,52.049103],[5.114082,52.049176],[5.114884,52.046500],[5.118321,52.046159],[5.120321,52.049158],[5.123321,52.049170],[5.124321,52.048185],[5.125321,52.049179],[5.137321,52.049242],[5.138321,52.050249],[5.141321,52.050274],[5.142321,52.048247],[5.145321,52.052234],[5.146321,52.051283],[5.147321,52.052271],[5.153321,52.052233],[5.155531,52.051147],[5.155730,52.048937],[5.156715,52.048543],[5.156054,52.053937],[5.159038,52.054937],[5.158321,52.055654],[5.155321,52.055673],[5.154089,52.057169],[5.157321,52.057302],[5.159942,52.058937],[5.157321,52.059570],[5.155921,52.058337],[5.152166,52.058782],[5.151747,52.063937],[5.149185,52.063801],[5.149130,52.065937],[5.151202,52.068056],[5.152321,52.068113],[5.157733,52.064349],[5.159115,52.064937],[5.158940,52.066556],[5.157196,52.066812],[5.153964,52.069580],[5.152204,52.069820],[5.151829,52.070937],[5.161805,52.079453],[5.163076,52.087937],[5.161705,52.090937],[5.163080,52.091937],[5.160754,52.092937],[5.160613,52.094229],[5.156863,52.097479],[5.157778,52.099394],[5.154859,52.100475],[5.153664,52.102937],[5.154321,52.103593],[5.156321,52.103359],[5.156893,52.104365],[5.161843,52.104937],[5.157321,52.105503],[5.156749,52.104509],[5.154046,52.104662],[5.152666,52.108282],[5.148745,52.113361],[5.148560,52.117176],[5.146688,52.117304],[5.147321,52.119762],[5.149593,52.117937],[5.149479,52.119095],[5.146387,52.121003],[5.146353,52.122905],[5.147322,52.122936],[5.147321,52.124034],[5.147316,52.122942],[5.146303,52.122937],[5.145994,52.120264],[5.142638,52.119937],[5.146082,52.119698],[5.146024,52.116937],[5.147915,52.113343],[5.145043,52.113215],[5.142321,52.111476],[5.142256,52.110872],[5.144917,52.109937],[5.142059,52.108199],[5.141321,52.106359],[5.140321,52.109126],[5.134001,52.107257],[5.131323,52.107935],[5.129321,52.104613],[5.127464,52.105937],[5.127001,52.105257],[5.124321,52.105052],[5.121321,52.105478],[5.120705,52.106321],[5.114321,52.107046],[5.111466,52.105937],[5.111321,52.104540],[5.110128,52.107937],[5.107801,52.107937],[5.108097,52.109937],[5.107286,52.109972],[5.107042,52.109216],[5.105050,52.109208],[5.103321,52.106732],[5.101321,52.106806],[5.099613,52.107937],[5.099321,52.109550],[5.094522,52.108736],[5.094321,52.107870],[5.092536,52.110152],[5.090344,52.110960],[5.079114,52.113144],[5.078884,52.111937],[5.080159,52.110937],[5.079285,52.109937],[5.081850,52.108466],[5.083025,52.106641],[5.087321,52.107423],[5.089635,52.105623],[5.085321,52.103302],[5.084321,52.104616],[5.082879,52.104495],[5.082601,52.106217],[5.079350,52.105966],[5.078321,52.107218],[5.077321,52.107047],[5.077321,52.104012],[5.078321,52.104623],[5.078929,52.103937],[5.078321,52.103167],[5.077321,52.103618],[5.077321,52.102764],[5.080311,52.100927],[5.080237,52.099937],[5.079321,52.099443],[5.078321,52.100714],[5.077321,52.098449],[5.076321,52.099214],[5.071321,52.098889],[5.071321,52.095681],[5.070165,52.098781],[5.061847,52.106463],[5.060234,52.106850],[5.055330,52.110937],[5.059321,52.112547],[5.062321,52.109518],[5.063768,52.109490],[5.063820,52.111937],[5.061808,52.112424],[5.057767,52.116937],[5.058695,52.118311],[5.056869,52.118485],[5.056387,52.119871],[5.058321,52.119782],[5.058970,52.118586],[5.061321,52.117612],[5.064005,52.117937],[5.062427,52.118043],[5.062321,52.119101],[5.060321,52.120161],[5.056282,52.119976],[5.054321,52.118230],[5.050361,52.118977],[5.050022,52.117236],[5.047501,52.117117],[5.047321,52.117767],[5.047321,52.116171],[5.049321,52.114042],[5.049475,52.114783],[5.052321,52.114542],[5.053321,52.115561],[5.055665,52.115281],[5.055612,52.113646],[5.053321,52.111551],[5.049195,52.112811],[5.049213,52.113829],[5.046321,52.113771],[5.043321,52.115487],[5.041321,52.115474],[5.035870,52.118486],[5.037321,52.121213],[5.038561,52.121177],[5.038623,52.119937],[5.040321,52.120732],[5.041756,52.119937],[5.042051,52.120937],[5.038603,52.122219],[5.038452,52.122937],[5.038557,52.123701],[5.039657,52.123937],[5.041388,52.125870],[5.041477,52.126781],[5.042321,52.126913],[5.042321,52.127328]],"type":"LineString"},"type":"Feature"}],"type":"FeatureCollection"})"; + R"({"features":[{"properties":{"fill-opacity":0.33,"fillColor":"#bf4040","opacity":0.33,"fill":"#bf4040","fillOpacity":0.33,"color":"#bf4040","contour":9.1,"metric":"time"},"geometry":{"coordinates":[[5.042321,52.127328],[5.041288,52.126971],[5.041162,52.126096],[5.040250,52.126008],[5.040123,52.125135],[5.038321,52.124230],[5.038155,52.123104],[5.035757,52.121937],[5.034321,52.119336],[5.029719,52.120335],[5.027321,52.122071],[5.025528,52.122144],[5.025321,52.123106],[5.023541,52.123157],[5.022162,52.124937],[5.022291,52.123907],[5.023244,52.122860],[5.024983,52.122599],[5.025321,52.121673],[5.026949,52.121565],[5.028321,52.119350],[5.029770,52.119386],[5.030321,52.118047],[5.031738,52.118354],[5.031768,52.117491],[5.030068,52.117190],[5.029028,52.112644],[5.032109,52.116149],[5.037321,52.115662],[5.044771,52.112387],[5.047321,52.112089],[5.047520,52.111136],[5.049379,52.110995],[5.047712,52.109937],[5.053321,52.103702],[5.057006,52.103622],[5.058321,52.102515],[5.061389,52.102937],[5.060882,52.100376],[5.059321,52.099179],[5.056855,52.098937],[5.057948,52.098564],[5.057822,52.096937],[5.059321,52.096398],[5.059890,52.094937],[5.062669,52.096285],[5.063657,52.092601],[5.059321,52.093638],[5.057934,52.090324],[5.054848,52.088410],[5.051513,52.088129],[5.051321,52.089053],[5.051073,52.088185],[5.048321,52.087370],[5.047321,52.088623],[5.046687,52.086571],[5.044576,52.084937],[5.045321,52.084802],[5.047321,52.085491],[5.048713,52.085329],[5.049321,52.082083],[5.050409,52.083850],[5.053317,52.081933],[5.055321,52.082660],[5.071321,52.081546],[5.072536,52.078937],[5.070321,52.075645],[5.068321,52.076598],[5.064321,52.076524],[5.063321,52.077492],[5.060985,52.077273],[5.060321,52.074253],[5.062321,52.076406],[5.063321,52.075371],[5.066556,52.075173],[5.067628,52.072937],[5.067514,52.070744],[5.065321,52.069631],[5.061321,52.069611],[5.060321,52.070600],[5.054934,52.070937],[5.056828,52.072937],[5.057903,52.077355],[5.060418,52.078937],[5.059321,52.081790],[5.057766,52.078492],[5.054557,52.077937],[5.055847,52.077463],[5.055644,52.074614],[5.053321,52.074166],[5.053916,52.077937],[5.053321,52.078215],[5.052921,52.076337],[5.051321,52.076004],[5.049321,52.079349],[5.049113,52.078145],[5.047811,52.078427],[5.047736,52.079522],[5.048998,52.079937],[5.048420,52.082036],[5.046798,52.078460],[5.044152,52.077106],[5.044305,52.074921],[5.043205,52.075937],[5.043272,52.074888],[5.043588,52.074204],[5.043516,52.072937],[5.042321,52.072093],[5.040321,52.073230],[5.034963,52.073579],[5.034321,52.078500],[5.028415,52.075937],[5.022321,52.075363],[5.021967,52.073583],[5.026171,52.072937],[5.027090,52.069706],[5.030321,52.071554],[5.037321,52.071095],[5.043321,52.069471],[5.047584,52.069200],[5.049321,52.068151],[5.054321,52.068229],[5.056321,52.066108],[5.059321,52.067213],[5.063501,52.066117],[5.063749,52.064365],[5.066245,52.061937],[5.066622,52.058937],[5.065629,52.057937],[5.067742,52.057358],[5.067482,52.054937],[5.068412,52.054846],[5.069986,52.058272],[5.073724,52.057937],[5.070994,52.057264],[5.069724,52.051534],[5.066166,52.051937],[5.066182,52.050798],[5.067807,52.050423],[5.070321,52.046926],[5.070489,52.047769],[5.072941,52.046557],[5.074321,52.047386],[5.075321,52.046360],[5.077321,52.046293],[5.078321,52.047273],[5.079321,52.046325],[5.080321,52.047249],[5.082321,52.046259],[5.084321,52.046217],[5.085321,52.047210],[5.089321,52.046295],[5.090321,52.047254],[5.091321,52.046299],[5.093321,52.046308],[5.094023,52.048236],[5.098321,52.046241],[5.099150,52.049108],[5.101179,52.051079],[5.102321,52.050145],[5.104104,52.050154],[5.106321,52.050234],[5.107564,52.051180],[5.107902,52.048518],[5.111321,52.048247],[5.112321,52.046211],[5.113156,52.049103],[5.114082,52.049176],[5.114885,52.046501],[5.118321,52.046159],[5.120321,52.049158],[5.123321,52.049170],[5.124321,52.048185],[5.125321,52.049180],[5.137321,52.049242],[5.138321,52.050250],[5.141321,52.050274],[5.142321,52.048247],[5.145321,52.052234],[5.146321,52.051283],[5.147321,52.052271],[5.153321,52.052233],[5.155531,52.051147],[5.155730,52.048937],[5.156715,52.048543],[5.156054,52.053937],[5.159038,52.054937],[5.158321,52.055654],[5.155321,52.055673],[5.154089,52.057169],[5.157321,52.057302],[5.159942,52.058937],[5.157321,52.059570],[5.155921,52.058337],[5.152166,52.058782],[5.151716,52.063332],[5.151269,52.063989],[5.149185,52.063801],[5.149130,52.065937],[5.151202,52.068057],[5.152321,52.068114],[5.157733,52.064349],[5.159115,52.064937],[5.158940,52.066556],[5.157196,52.066812],[5.153964,52.069580],[5.152204,52.069820],[5.151829,52.070937],[5.161805,52.079453],[5.163076,52.087937],[5.161705,52.090937],[5.163080,52.091937],[5.160754,52.092937],[5.160613,52.094229],[5.156863,52.097479],[5.157778,52.099394],[5.154859,52.100475],[5.153664,52.102937],[5.154321,52.103593],[5.156321,52.103359],[5.156893,52.104365],[5.161843,52.104937],[5.157321,52.105503],[5.156749,52.104509],[5.154046,52.104662],[5.152666,52.108282],[5.148745,52.113361],[5.148560,52.117176],[5.146688,52.117304],[5.147321,52.119762],[5.149593,52.117937],[5.149479,52.119095],[5.146387,52.121003],[5.146353,52.122905],[5.147322,52.122936],[5.147321,52.124034],[5.147316,52.122942],[5.146303,52.122937],[5.145994,52.120264],[5.142638,52.119937],[5.146082,52.119698],[5.146024,52.116937],[5.147915,52.113343],[5.145043,52.113215],[5.142321,52.111476],[5.142256,52.110872],[5.144917,52.109937],[5.142059,52.108199],[5.141321,52.106359],[5.140321,52.109126],[5.134001,52.107257],[5.131323,52.107935],[5.129321,52.104613],[5.127464,52.105937],[5.127001,52.105257],[5.124321,52.105052],[5.121321,52.105478],[5.120705,52.106321],[5.114321,52.106650],[5.112106,52.105937],[5.111321,52.104541],[5.110128,52.107937],[5.109321,52.108434],[5.108009,52.107625],[5.107321,52.109550],[5.105050,52.109208],[5.103321,52.106732],[5.101321,52.106806],[5.099613,52.107937],[5.099321,52.109550],[5.094522,52.108736],[5.094321,52.107870],[5.092536,52.110152],[5.090344,52.110960],[5.079114,52.113144],[5.078884,52.111937],[5.080159,52.110937],[5.079285,52.109937],[5.081850,52.108466],[5.083025,52.106641],[5.087321,52.107423],[5.089635,52.105623],[5.085321,52.103302],[5.084321,52.104616],[5.082879,52.104495],[5.082601,52.106217],[5.079350,52.105966],[5.078321,52.107218],[5.077321,52.107047],[5.077321,52.104012],[5.078321,52.104623],[5.078929,52.103937],[5.078321,52.103167],[5.077321,52.103618],[5.077321,52.102764],[5.078312,52.101928],[5.080321,52.102151],[5.081321,52.100617],[5.082321,52.101396],[5.082798,52.099937],[5.079321,52.099443],[5.078321,52.100514],[5.077321,52.098449],[5.076321,52.099214],[5.071321,52.098889],[5.071321,52.095681],[5.070165,52.098781],[5.061847,52.106463],[5.060235,52.106851],[5.055330,52.110937],[5.059321,52.112547],[5.062321,52.109518],[5.063768,52.109490],[5.063820,52.111937],[5.061808,52.112424],[5.057767,52.116937],[5.058695,52.118311],[5.056869,52.118485],[5.056387,52.119871],[5.058321,52.119783],[5.058970,52.118586],[5.061220,52.117836],[5.064005,52.117937],[5.062427,52.118043],[5.062321,52.119101],[5.060321,52.120161],[5.056282,52.119976],[5.054321,52.118230],[5.050361,52.118977],[5.050023,52.117236],[5.047501,52.117117],[5.047321,52.117767],[5.047321,52.116171],[5.049321,52.114042],[5.049475,52.114783],[5.052321,52.114542],[5.053321,52.115561],[5.055665,52.115281],[5.055612,52.113646],[5.053321,52.111551],[5.049195,52.112811],[5.049213,52.113829],[5.046321,52.113771],[5.043321,52.115487],[5.041321,52.115474],[5.035871,52.118487],[5.038321,52.121583],[5.038623,52.119937],[5.039353,52.119905],[5.040321,52.120732],[5.041756,52.119937],[5.042051,52.120937],[5.038526,52.122142],[5.038415,52.122937],[5.038489,52.123769],[5.039657,52.123937],[5.041389,52.125870],[5.041477,52.126781],[5.042321,52.126913],[5.042321,52.127328]],"type":"LineString"},"type":"Feature"}],"type":"FeatureCollection"})"; try_isochrone(loki_worker, thor_worker, request, expected); } @@ -147,7 +198,7 @@ TEST(Isochrones, Basic) { const auto request = R"({"locations":[{"lat":52.078937,"lon":5.115321}],"costing":"bicycle","costing_options":{"bicycle":{"service_penalty":0}},"contours":[{"time":15}],"polygons":true,"denoise":0.2})"; const auto expected = - R"({"features":[{"properties":{"fill":"#bf4040","fillOpacity":0.33,"fill-opacity":0.33,"fillColor":"#bf4040","color":"#bf4040","contour":15,"opacity":0.33,"metric":"time"},"geometry":{"coordinates":[[[5.108321,52.106133],[5.105321,52.106242],[5.103227,52.105031],[5.101321,52.105126],[5.100321,52.104612],[5.098321,52.104950],[5.094681,52.103937],[5.094321,52.101694],[5.095785,52.101401],[5.098321,52.098534],[5.100321,52.098287],[5.101321,52.097284],[5.103321,52.097293],[5.103321,52.095801],[5.103029,52.096645],[5.102321,52.096791],[5.095321,52.096753],[5.094321,52.097731],[5.088081,52.097697],[5.087983,52.098275],[5.089502,52.098756],[5.089643,52.099615],[5.090729,52.099937],[5.089321,52.100487],[5.088321,52.100213],[5.087321,52.101688],[5.085751,52.100937],[5.085321,52.099826],[5.082580,52.098937],[5.085321,52.098574],[5.085732,52.097937],[5.085321,52.097544],[5.081321,52.097320],[5.080051,52.097937],[5.080203,52.096819],[5.081875,52.095937],[5.079321,52.095542],[5.078765,52.094937],[5.079911,52.093527],[5.081638,52.092937],[5.078916,52.092342],[5.078764,52.090937],[5.079917,52.088533],[5.081712,52.087937],[5.081321,52.087147],[5.080254,52.087004],[5.079886,52.083937],[5.081540,52.082156],[5.081661,52.079937],[5.080818,52.079440],[5.078731,52.079347],[5.077896,52.079512],[5.077321,52.080425],[5.076321,52.080581],[5.076165,52.079937],[5.077321,52.078611],[5.082321,52.078291],[5.082568,52.077184],[5.084321,52.075779],[5.084576,52.074682],[5.082885,52.073937],[5.084614,52.071644],[5.082187,52.071803],[5.081899,52.072515],[5.079135,52.074751],[5.078965,52.075293],[5.079751,52.075937],[5.078542,52.077158],[5.077321,52.077268],[5.075321,52.075447],[5.070758,52.074937],[5.071321,52.074578],[5.074321,52.074450],[5.074954,52.073570],[5.076549,52.073165],[5.076575,52.071683],[5.075321,52.071450],[5.074808,52.070937],[5.074994,52.069610],[5.075984,52.068937],[5.075914,52.065530],[5.081321,52.065241],[5.084070,52.062686],[5.086663,52.062279],[5.087321,52.061429],[5.089321,52.061093],[5.089444,52.062060],[5.088578,52.063194],[5.088887,52.064371],[5.090321,52.064512],[5.091321,52.063497],[5.092724,52.063340],[5.093321,52.062497],[5.094321,52.064097],[5.094649,52.063265],[5.095460,52.063076],[5.095733,52.062349],[5.100727,52.058343],[5.101298,52.056914],[5.102338,52.056920],[5.102894,52.057364],[5.104321,52.056186],[5.106380,52.055996],[5.105321,52.054503],[5.104321,52.054945],[5.103599,52.054659],[5.103433,52.053825],[5.102665,52.053593],[5.102435,52.052823],[5.100981,52.052277],[5.100321,52.051343],[5.096519,52.051135],[5.096321,52.051627],[5.096115,52.051143],[5.097017,52.050633],[5.102321,52.050477],[5.104321,52.048435],[5.104894,52.050364],[5.106321,52.050394],[5.107706,52.051322],[5.108321,52.050495],[5.111913,52.049529],[5.112321,52.049087],[5.114310,52.050937],[5.113321,52.051958],[5.112934,52.051324],[5.111029,52.051645],[5.112002,52.052937],[5.112126,52.055132],[5.114321,52.055681],[5.115482,52.056776],[5.121321,52.057218],[5.123321,52.059146],[5.125321,52.059215],[5.126321,52.058232],[5.127519,52.059135],[5.128185,52.056801],[5.131811,52.056427],[5.132321,52.056061],[5.136323,52.056935],[5.137321,52.057547],[5.139321,52.057085],[5.139895,52.058363],[5.141624,52.059634],[5.141735,52.060937],[5.142808,52.061450],[5.144321,52.063187],[5.150021,52.057637],[5.151782,52.057398],[5.152321,52.056021],[5.152321,52.058197],[5.150894,52.058510],[5.148036,52.061937],[5.148321,52.062530],[5.149574,52.062684],[5.148835,52.063937],[5.148886,52.066372],[5.151091,52.068167],[5.152321,52.068296],[5.153321,52.067337],[5.154655,52.067271],[5.156321,52.065622],[5.158078,52.065937],[5.156855,52.066471],[5.155643,52.068259],[5.154321,52.068486],[5.152914,52.069937],[5.153321,52.070437],[5.157335,52.070937],[5.152321,52.071514],[5.151321,52.070638],[5.149107,52.072937],[5.151746,52.073512],[5.152321,52.075389],[5.153321,52.073864],[5.153943,52.075315],[5.155525,52.075733],[5.155380,52.077996],[5.153321,52.076444],[5.151876,52.077937],[5.153321,52.079469],[5.154548,52.079710],[5.155321,52.080761],[5.156130,52.080937],[5.156124,52.082134],[5.156835,52.082937],[5.156094,52.083164],[5.155657,52.082601],[5.154034,52.082224],[5.153669,52.081589],[5.152321,52.081506],[5.151583,52.080675],[5.149878,52.080380],[5.149526,52.079732],[5.148963,52.079937],[5.149164,52.081094],[5.151126,52.083132],[5.153768,52.083490],[5.153716,52.085937],[5.154647,52.087611],[5.155630,52.087937],[5.154321,52.089447],[5.151321,52.089430],[5.150858,52.089937],[5.151321,52.090504],[5.154088,52.090937],[5.153601,52.091217],[5.153321,52.093413],[5.152232,52.094026],[5.150321,52.093097],[5.149321,52.093808],[5.148321,52.093005],[5.147924,52.093540],[5.148321,52.094324],[5.149321,52.094187],[5.149858,52.095400],[5.151321,52.095321],[5.151894,52.097364],[5.154121,52.097937],[5.153491,52.098107],[5.153321,52.098841],[5.152974,52.098284],[5.152321,52.098260],[5.150908,52.098524],[5.150321,52.099513],[5.147207,52.099051],[5.146821,52.098937],[5.148732,52.098348],[5.148625,52.097633],[5.145321,52.097622],[5.144793,52.097937],[5.146181,52.098937],[5.145730,52.099937],[5.146519,52.100937],[5.146321,52.101629],[5.145019,52.101239],[5.144912,52.099937],[5.144321,52.099476],[5.142828,52.100937],[5.143321,52.101667],[5.144485,52.101937],[5.142656,52.102937],[5.140321,52.102566],[5.140038,52.102220],[5.137321,52.102151],[5.136321,52.103072],[5.135895,52.102363],[5.134155,52.102103],[5.133783,52.099937],[5.134321,52.099378],[5.135598,52.099214],[5.135580,52.096937],[5.134666,52.096592],[5.134321,52.095878],[5.132462,52.096078],[5.132120,52.096736],[5.130321,52.097066],[5.129321,52.098041],[5.128321,52.098234],[5.126988,52.099604],[5.124891,52.099367],[5.124313,52.098929],[5.124334,52.102937],[5.120321,52.103242],[5.119321,52.102682],[5.116321,52.105625],[5.115321,52.104350],[5.114321,52.104814],[5.112321,52.104898],[5.111321,52.104483],[5.110652,52.105268],[5.108321,52.106133]]],"type":"Polygon"},"type":"Feature"}],"type":"FeatureCollection"})"; + R"({"features":[{"properties":{"fill-opacity":0.33,"fillColor":"#bf4040","opacity":0.33,"fill":"#bf4040","fillOpacity":0.33,"color":"#bf4040","contour":15,"metric":"time"},"geometry":{"coordinates":[[[5.108321,52.106133],[5.105321,52.106242],[5.103227,52.105031],[5.101321,52.105126],[5.100321,52.104612],[5.098321,52.104950],[5.094681,52.103937],[5.094321,52.101694],[5.095785,52.101401],[5.098321,52.098534],[5.100321,52.098287],[5.101321,52.097284],[5.103321,52.097293],[5.103321,52.095801],[5.103029,52.096645],[5.102321,52.096791],[5.095321,52.096753],[5.094321,52.097731],[5.088081,52.097697],[5.087983,52.098275],[5.089502,52.098756],[5.089643,52.099615],[5.090729,52.099937],[5.089321,52.100487],[5.088321,52.100213],[5.087321,52.101688],[5.085751,52.100937],[5.085321,52.099826],[5.082580,52.098937],[5.085321,52.098574],[5.085732,52.097937],[5.085321,52.097544],[5.081321,52.097320],[5.080051,52.097937],[5.080203,52.096819],[5.081875,52.095937],[5.079321,52.095542],[5.078765,52.094937],[5.079911,52.093527],[5.081638,52.092937],[5.078917,52.092342],[5.078764,52.090937],[5.079917,52.088533],[5.081712,52.087937],[5.081321,52.087147],[5.080254,52.087004],[5.079886,52.083937],[5.081540,52.082156],[5.081661,52.079937],[5.080818,52.079440],[5.078731,52.079347],[5.077896,52.079512],[5.077321,52.080426],[5.076321,52.080581],[5.076165,52.079937],[5.077321,52.078611],[5.082321,52.078291],[5.082568,52.077184],[5.084321,52.075779],[5.084576,52.074682],[5.082885,52.073937],[5.084614,52.071644],[5.082187,52.071803],[5.081899,52.072515],[5.079135,52.074751],[5.078965,52.075293],[5.079752,52.075937],[5.078542,52.077158],[5.077321,52.077268],[5.075321,52.075447],[5.070758,52.074937],[5.071321,52.074578],[5.074321,52.074450],[5.074954,52.073570],[5.076549,52.073165],[5.076575,52.071683],[5.075321,52.071450],[5.074808,52.070937],[5.074994,52.069610],[5.075984,52.068937],[5.075914,52.065530],[5.081321,52.065241],[5.084070,52.062686],[5.086663,52.062279],[5.087321,52.061429],[5.089321,52.061093],[5.089444,52.062060],[5.088578,52.063194],[5.088887,52.064371],[5.090321,52.064512],[5.091321,52.063497],[5.092724,52.063340],[5.093321,52.062497],[5.094321,52.064097],[5.094649,52.063265],[5.095460,52.063076],[5.095733,52.062349],[5.100727,52.058343],[5.101298,52.056914],[5.102338,52.056920],[5.102894,52.057364],[5.104321,52.056186],[5.106380,52.055996],[5.105321,52.054503],[5.104321,52.054945],[5.103599,52.054659],[5.103433,52.053825],[5.102665,52.053593],[5.102435,52.052823],[5.100981,52.052277],[5.100321,52.051343],[5.096519,52.051135],[5.096321,52.051628],[5.096115,52.051143],[5.097017,52.050633],[5.102321,52.050477],[5.104321,52.048435],[5.104894,52.050364],[5.106321,52.050394],[5.107706,52.051322],[5.108321,52.050495],[5.111913,52.049529],[5.112321,52.049087],[5.114310,52.050937],[5.113321,52.051958],[5.112934,52.051324],[5.111029,52.051645],[5.112002,52.052937],[5.112126,52.055132],[5.114321,52.055681],[5.115482,52.056776],[5.121321,52.057218],[5.123321,52.059146],[5.125321,52.059215],[5.126321,52.058232],[5.127519,52.059135],[5.128186,52.056802],[5.131812,52.056428],[5.132321,52.056061],[5.136323,52.056935],[5.137321,52.057547],[5.139321,52.057085],[5.139895,52.058363],[5.141624,52.059634],[5.141735,52.060937],[5.142808,52.061450],[5.144321,52.063187],[5.150021,52.057637],[5.151782,52.057398],[5.152321,52.056021],[5.152321,52.058197],[5.150823,52.058439],[5.147866,52.062482],[5.146158,52.063774],[5.145868,52.065484],[5.144321,52.065722],[5.144200,52.066058],[5.145905,52.066353],[5.146321,52.067168],[5.147530,52.067146],[5.148321,52.063899],[5.148740,52.066518],[5.151091,52.068167],[5.152321,52.068296],[5.153321,52.067337],[5.154655,52.067271],[5.156321,52.065622],[5.158078,52.065937],[5.156855,52.066471],[5.155643,52.068259],[5.154321,52.068486],[5.152914,52.069937],[5.153321,52.070437],[5.157335,52.070937],[5.152321,52.071514],[5.151321,52.070638],[5.149107,52.072937],[5.151746,52.073512],[5.152321,52.075389],[5.153321,52.073865],[5.153943,52.075315],[5.155525,52.075734],[5.155380,52.077996],[5.153321,52.076444],[5.151876,52.077937],[5.153321,52.079469],[5.154548,52.079710],[5.155321,52.080761],[5.156130,52.080937],[5.156124,52.082134],[5.156835,52.082937],[5.156094,52.083164],[5.155658,52.082601],[5.154034,52.082224],[5.153670,52.081589],[5.152321,52.081506],[5.151583,52.080675],[5.149878,52.080380],[5.149526,52.079732],[5.148963,52.079937],[5.149164,52.081094],[5.151126,52.083132],[5.153768,52.083490],[5.153716,52.085937],[5.154647,52.087611],[5.155630,52.087937],[5.154321,52.089447],[5.151321,52.089430],[5.150858,52.089937],[5.151321,52.090504],[5.154088,52.090937],[5.153601,52.091217],[5.153321,52.093413],[5.152232,52.094026],[5.150321,52.093097],[5.149321,52.093808],[5.148321,52.093005],[5.147924,52.093540],[5.148321,52.094324],[5.149321,52.094187],[5.149858,52.095400],[5.151321,52.095321],[5.151894,52.097364],[5.154121,52.097937],[5.153491,52.098107],[5.153321,52.098841],[5.152974,52.098284],[5.152321,52.098260],[5.150908,52.098524],[5.150321,52.099513],[5.147207,52.099051],[5.146821,52.098937],[5.148732,52.098348],[5.148625,52.097634],[5.145321,52.097622],[5.144793,52.097937],[5.146181,52.098937],[5.145730,52.099937],[5.146519,52.100937],[5.146321,52.101629],[5.145019,52.101240],[5.144912,52.099937],[5.144321,52.099476],[5.142828,52.100937],[5.143321,52.101667],[5.144485,52.101937],[5.142656,52.102937],[5.140321,52.102566],[5.140038,52.102220],[5.137321,52.102151],[5.136321,52.103072],[5.135895,52.102363],[5.134155,52.102103],[5.133783,52.099937],[5.134321,52.099378],[5.135598,52.099214],[5.135580,52.096937],[5.134666,52.096592],[5.134321,52.095878],[5.132462,52.096078],[5.132120,52.096736],[5.130321,52.097066],[5.129321,52.098041],[5.128321,52.098234],[5.126988,52.099604],[5.124891,52.099367],[5.124313,52.098929],[5.124334,52.102937],[5.120321,52.103242],[5.119321,52.102682],[5.116321,52.105625],[5.115321,52.104350],[5.114321,52.104814],[5.112321,52.104898],[5.111321,52.104483],[5.110652,52.105268],[5.108321,52.106133]]],"type":"Polygon"},"type":"Feature"}],"type":"FeatureCollection"})"; try_isochrone(loki_worker, thor_worker, request, expected); } @@ -155,7 +206,25 @@ TEST(Isochrones, Basic) { const auto request = R"({"locations":[{"lat":52.078937,"lon":5.115321}],"costing":"bicycle","costing_options":{"bicycle":{"service_penalty":0}},"contours":[{"time":15}],"show_locations":true})"; const auto expected = - R"({"features":[{"properties":{"fill":"#bf4040","fillOpacity":0.33,"fill-opacity":0.33,"fillColor":"#bf4040","color":"#bf4040","contour":15,"opacity":0.33,"metric":"time"},"geometry":{"coordinates":[[5.108321,52.106133],[5.105321,52.106242],[5.103227,52.105031],[5.101321,52.105126],[5.100321,52.104612],[5.098321,52.104950],[5.094681,52.103937],[5.094321,52.101694],[5.095785,52.101401],[5.098321,52.098534],[5.100321,52.098287],[5.101321,52.097284],[5.103321,52.097293],[5.103321,52.095801],[5.103029,52.096645],[5.102321,52.096791],[5.095321,52.096753],[5.094321,52.097731],[5.088081,52.097697],[5.087983,52.098275],[5.089502,52.098756],[5.089643,52.099615],[5.090729,52.099937],[5.089321,52.100487],[5.088321,52.100213],[5.087321,52.101688],[5.085751,52.100937],[5.085321,52.099826],[5.082580,52.098937],[5.085321,52.098574],[5.085732,52.097937],[5.085321,52.097544],[5.081321,52.097320],[5.080051,52.097937],[5.080203,52.096819],[5.081875,52.095937],[5.079321,52.095542],[5.078765,52.094937],[5.079911,52.093527],[5.081638,52.092937],[5.078916,52.092342],[5.078764,52.090937],[5.079917,52.088533],[5.081712,52.087937],[5.081321,52.087147],[5.080254,52.087004],[5.079886,52.083937],[5.081540,52.082156],[5.081661,52.079937],[5.080818,52.079440],[5.078731,52.079347],[5.077896,52.079512],[5.077321,52.080425],[5.076321,52.080581],[5.076165,52.079937],[5.077321,52.078611],[5.082321,52.078291],[5.082568,52.077184],[5.084321,52.075779],[5.084576,52.074682],[5.082885,52.073937],[5.084614,52.071644],[5.082187,52.071803],[5.081899,52.072515],[5.079135,52.074751],[5.078965,52.075293],[5.079751,52.075937],[5.078542,52.077158],[5.077321,52.077268],[5.075321,52.075447],[5.070758,52.074937],[5.071321,52.074578],[5.074321,52.074450],[5.074954,52.073570],[5.076549,52.073165],[5.076575,52.071683],[5.075321,52.071450],[5.074808,52.070937],[5.074994,52.069610],[5.075984,52.068937],[5.075914,52.065530],[5.081321,52.065241],[5.084070,52.062686],[5.086663,52.062279],[5.087321,52.061429],[5.089321,52.061093],[5.089444,52.062060],[5.088578,52.063194],[5.088887,52.064371],[5.090321,52.064512],[5.091321,52.063497],[5.092724,52.063340],[5.093321,52.062497],[5.094321,52.064097],[5.094649,52.063265],[5.095460,52.063076],[5.095733,52.062349],[5.100727,52.058343],[5.101298,52.056914],[5.102338,52.056920],[5.102894,52.057364],[5.104321,52.056186],[5.106380,52.055996],[5.105321,52.054503],[5.104321,52.054945],[5.103599,52.054659],[5.103433,52.053825],[5.102665,52.053593],[5.102435,52.052823],[5.100981,52.052277],[5.100321,52.051343],[5.096519,52.051135],[5.096321,52.051627],[5.096115,52.051143],[5.097017,52.050633],[5.102321,52.050477],[5.104321,52.048435],[5.104894,52.050364],[5.106321,52.050394],[5.107706,52.051322],[5.108321,52.050495],[5.111913,52.049529],[5.112321,52.049087],[5.114310,52.050937],[5.113321,52.051958],[5.112934,52.051324],[5.111029,52.051645],[5.112002,52.052937],[5.112126,52.055132],[5.114321,52.055681],[5.115482,52.056776],[5.121321,52.057218],[5.123321,52.059146],[5.125321,52.059215],[5.126321,52.058232],[5.127519,52.059135],[5.128185,52.056801],[5.131811,52.056427],[5.132321,52.056061],[5.136323,52.056935],[5.137321,52.057547],[5.139321,52.057085],[5.139895,52.058363],[5.141624,52.059634],[5.141735,52.060937],[5.142808,52.061450],[5.144321,52.063187],[5.150021,52.057637],[5.151782,52.057398],[5.152321,52.056021],[5.152321,52.058197],[5.150894,52.058510],[5.148036,52.061937],[5.148321,52.062530],[5.149574,52.062684],[5.148835,52.063937],[5.148886,52.066372],[5.151091,52.068167],[5.152321,52.068296],[5.153321,52.067337],[5.154655,52.067271],[5.156321,52.065622],[5.158078,52.065937],[5.156855,52.066471],[5.155643,52.068259],[5.154321,52.068486],[5.152914,52.069937],[5.153321,52.070437],[5.157335,52.070937],[5.152321,52.071514],[5.151321,52.070638],[5.149107,52.072937],[5.151746,52.073512],[5.152321,52.075389],[5.153321,52.073864],[5.153943,52.075315],[5.155525,52.075733],[5.155380,52.077996],[5.153321,52.076444],[5.151876,52.077937],[5.153321,52.079469],[5.154548,52.079710],[5.155321,52.080761],[5.156130,52.080937],[5.156124,52.082134],[5.156835,52.082937],[5.156094,52.083164],[5.155657,52.082601],[5.154034,52.082224],[5.153669,52.081589],[5.152321,52.081506],[5.151583,52.080675],[5.149878,52.080380],[5.149526,52.079732],[5.148963,52.079937],[5.149164,52.081094],[5.151126,52.083132],[5.153768,52.083490],[5.153716,52.085937],[5.154647,52.087611],[5.155630,52.087937],[5.154321,52.089447],[5.151321,52.089430],[5.150858,52.089937],[5.151321,52.090504],[5.154088,52.090937],[5.153601,52.091217],[5.153321,52.093413],[5.152232,52.094026],[5.150321,52.093097],[5.149321,52.093808],[5.148321,52.093005],[5.147924,52.093540],[5.148321,52.094324],[5.149321,52.094187],[5.149858,52.095400],[5.151321,52.095321],[5.151894,52.097364],[5.154121,52.097937],[5.153491,52.098107],[5.153321,52.098841],[5.152974,52.098284],[5.152321,52.098260],[5.150908,52.098524],[5.150321,52.099513],[5.147207,52.099051],[5.146821,52.098937],[5.148732,52.098348],[5.148625,52.097633],[5.145321,52.097622],[5.144793,52.097937],[5.146181,52.098937],[5.145730,52.099937],[5.146519,52.100937],[5.146321,52.101629],[5.145019,52.101239],[5.144912,52.099937],[5.144321,52.099476],[5.142828,52.100937],[5.143321,52.101667],[5.144485,52.101937],[5.142656,52.102937],[5.140321,52.102566],[5.140038,52.102220],[5.137321,52.102151],[5.136321,52.103072],[5.135895,52.102363],[5.134155,52.102103],[5.133783,52.099937],[5.134321,52.099378],[5.135598,52.099214],[5.135580,52.096937],[5.134666,52.096592],[5.134321,52.095878],[5.132462,52.096078],[5.132120,52.096736],[5.130321,52.097066],[5.129321,52.098041],[5.128321,52.098234],[5.126988,52.099604],[5.124891,52.099367],[5.124313,52.098929],[5.124334,52.102937],[5.120321,52.103242],[5.119321,52.102682],[5.116321,52.105625],[5.115321,52.104350],[5.114321,52.104814],[5.112321,52.104898],[5.111321,52.104483],[5.110652,52.105268],[5.108321,52.106133]],"type":"LineString"},"type":"Feature"},{"geometry":{"coordinates":[[5.115328,52.078940]],"type":"MultiPoint"},"properties":{"location_index":0,"type":"snapped"},"type":"Feature"},{"geometry":{"coordinates":[5.115321,52.078937],"type":"Point"},"properties":{"location_index":0,"type":"input"},"type":"Feature"}],"type":"FeatureCollection"})"; + R"({"features":[{"properties":{"fill-opacity":0.33,"fillColor":"#bf4040","opacity":0.33,"fill":"#bf4040","fillOpacity":0.33,"color":"#bf4040","contour":15,"metric":"time"},"geometry":{"coordinates":[[5.108321,52.106133],[5.105321,52.106242],[5.103227,52.105031],[5.101321,52.105126],[5.100321,52.104612],[5.098321,52.104950],[5.094681,52.103937],[5.094321,52.101694],[5.095785,52.101401],[5.098321,52.098534],[5.100321,52.098287],[5.101321,52.097284],[5.103321,52.097293],[5.103321,52.095801],[5.103029,52.096645],[5.102321,52.096791],[5.095321,52.096753],[5.094321,52.097731],[5.088081,52.097697],[5.087983,52.098275],[5.089502,52.098756],[5.089643,52.099615],[5.090729,52.099937],[5.089321,52.100487],[5.088321,52.100213],[5.087321,52.101688],[5.085751,52.100937],[5.085321,52.099826],[5.082580,52.098937],[5.085321,52.098574],[5.085732,52.097937],[5.085321,52.097544],[5.081321,52.097320],[5.080051,52.097937],[5.080203,52.096819],[5.081875,52.095937],[5.079321,52.095542],[5.078765,52.094937],[5.079911,52.093527],[5.081638,52.092937],[5.078917,52.092342],[5.078764,52.090937],[5.079917,52.088533],[5.081712,52.087937],[5.081321,52.087147],[5.080254,52.087004],[5.079886,52.083937],[5.081540,52.082156],[5.081661,52.079937],[5.080818,52.079440],[5.078731,52.079347],[5.077896,52.079512],[5.077321,52.080426],[5.076321,52.080581],[5.076165,52.079937],[5.077321,52.078611],[5.082321,52.078291],[5.082568,52.077184],[5.084321,52.075779],[5.084576,52.074682],[5.082885,52.073937],[5.084614,52.071644],[5.082187,52.071803],[5.081899,52.072515],[5.079135,52.074751],[5.078965,52.075293],[5.079752,52.075937],[5.078542,52.077158],[5.077321,52.077268],[5.075321,52.075447],[5.070758,52.074937],[5.071321,52.074578],[5.074321,52.074450],[5.074954,52.073570],[5.076549,52.073165],[5.076575,52.071683],[5.075321,52.071450],[5.074808,52.070937],[5.074994,52.069610],[5.075984,52.068937],[5.075914,52.065530],[5.081321,52.065241],[5.084070,52.062686],[5.086663,52.062279],[5.087321,52.061429],[5.089321,52.061093],[5.089444,52.062060],[5.088578,52.063194],[5.088887,52.064371],[5.090321,52.064512],[5.091321,52.063497],[5.092724,52.063340],[5.093321,52.062497],[5.094321,52.064097],[5.094649,52.063265],[5.095460,52.063076],[5.095733,52.062349],[5.100727,52.058343],[5.101298,52.056914],[5.102338,52.056920],[5.102894,52.057364],[5.104321,52.056186],[5.106380,52.055996],[5.105321,52.054503],[5.104321,52.054945],[5.103599,52.054659],[5.103433,52.053825],[5.102665,52.053593],[5.102435,52.052823],[5.100981,52.052277],[5.100321,52.051343],[5.096519,52.051135],[5.096321,52.051628],[5.096115,52.051143],[5.097017,52.050633],[5.102321,52.050477],[5.104321,52.048435],[5.104894,52.050364],[5.106321,52.050394],[5.107706,52.051322],[5.108321,52.050495],[5.111913,52.049529],[5.112321,52.049087],[5.114310,52.050937],[5.113321,52.051958],[5.112934,52.051324],[5.111029,52.051645],[5.112002,52.052937],[5.112126,52.055132],[5.114321,52.055681],[5.115482,52.056776],[5.121321,52.057218],[5.123321,52.059146],[5.125321,52.059215],[5.126321,52.058232],[5.127519,52.059135],[5.128186,52.056802],[5.131812,52.056428],[5.132321,52.056061],[5.136323,52.056935],[5.137321,52.057547],[5.139321,52.057085],[5.139895,52.058363],[5.141624,52.059634],[5.141735,52.060937],[5.142808,52.061450],[5.144321,52.063187],[5.150021,52.057637],[5.151782,52.057398],[5.152321,52.056021],[5.152321,52.058197],[5.150823,52.058439],[5.147866,52.062482],[5.146158,52.063774],[5.145868,52.065484],[5.144321,52.065722],[5.144200,52.066058],[5.145905,52.066353],[5.146321,52.067168],[5.147530,52.067146],[5.148321,52.063899],[5.148740,52.066518],[5.151091,52.068167],[5.152321,52.068296],[5.153321,52.067337],[5.154655,52.067271],[5.156321,52.065622],[5.158078,52.065937],[5.156855,52.066471],[5.155643,52.068259],[5.154321,52.068486],[5.152914,52.069937],[5.153321,52.070437],[5.157335,52.070937],[5.152321,52.071514],[5.151321,52.070638],[5.149107,52.072937],[5.151746,52.073512],[5.152321,52.075389],[5.153321,52.073865],[5.153943,52.075315],[5.155525,52.075734],[5.155380,52.077996],[5.153321,52.076444],[5.151876,52.077937],[5.153321,52.079469],[5.154548,52.079710],[5.155321,52.080761],[5.156130,52.080937],[5.156124,52.082134],[5.156835,52.082937],[5.156094,52.083164],[5.155658,52.082601],[5.154034,52.082224],[5.153670,52.081589],[5.152321,52.081506],[5.151583,52.080675],[5.149878,52.080380],[5.149526,52.079732],[5.148963,52.079937],[5.149164,52.081094],[5.151126,52.083132],[5.153768,52.083490],[5.153716,52.085937],[5.154647,52.087611],[5.155630,52.087937],[5.154321,52.089447],[5.151321,52.089430],[5.150858,52.089937],[5.151321,52.090504],[5.154088,52.090937],[5.153601,52.091217],[5.153321,52.093413],[5.152232,52.094026],[5.150321,52.093097],[5.149321,52.093808],[5.148321,52.093005],[5.147924,52.093540],[5.148321,52.094324],[5.149321,52.094187],[5.149858,52.095400],[5.151321,52.095321],[5.151894,52.097364],[5.154121,52.097937],[5.153491,52.098107],[5.153321,52.098841],[5.152974,52.098284],[5.152321,52.098260],[5.150908,52.098524],[5.150321,52.099513],[5.147207,52.099051],[5.146821,52.098937],[5.148732,52.098348],[5.148625,52.097634],[5.145321,52.097622],[5.144793,52.097937],[5.146181,52.098937],[5.145730,52.099937],[5.146519,52.100937],[5.146321,52.101629],[5.145019,52.101240],[5.144912,52.099937],[5.144321,52.099476],[5.142828,52.100937],[5.143321,52.101667],[5.144485,52.101937],[5.142656,52.102937],[5.140321,52.102566],[5.140038,52.102220],[5.137321,52.102151],[5.136321,52.103072],[5.135895,52.102363],[5.134155,52.102103],[5.133783,52.099937],[5.134321,52.099378],[5.135598,52.099214],[5.135580,52.096937],[5.134666,52.096592],[5.134321,52.095878],[5.132462,52.096078],[5.132120,52.096736],[5.130321,52.097066],[5.129321,52.098041],[5.128321,52.098234],[5.126988,52.099604],[5.124891,52.099367],[5.124313,52.098929],[5.124334,52.102937],[5.120321,52.103242],[5.119321,52.102682],[5.116321,52.105625],[5.115321,52.104350],[5.114321,52.104814],[5.112321,52.104898],[5.111321,52.104483],[5.110652,52.105268],[5.108321,52.106133]],"type":"LineString"},"type":"Feature"},{"geometry":{"coordinates":[[5.115328,52.078940]],"type":"MultiPoint"},"properties":{"location_index":0,"type":"snapped"},"type":"Feature"},{"geometry":{"coordinates":[5.115321,52.078937],"type":"Point"},"properties":{"location_index":0,"type":"input"},"type":"Feature"}],"type":"FeatureCollection"})"; + try_isochrone(loki_worker, thor_worker, request, expected); + } + + // multi-location + { + const auto request = + R"({"costing":"auto","locations":[{"lon":5.086633,"lat":52.075911},{"lon":5.128852,"lat":52.109455}],"contours":[{"time":2}],"denoise":0,"generalize":100,"polygons":true})"; + const auto expected = + R"({"features":[{"properties":{"fill-opacity":0.33,"fillColor":"#bf4040","opacity":0.33,"fill":"#bf4040","fillOpacity":0.33,"color":"#bf4040","contour":2,"metric":"time"},"geometry":{"coordinates":[[[[5.097852,52.083605],[5.092852,52.081801],[5.088852,52.083234],[5.084989,52.079318],[5.081579,52.079728],[5.091017,52.069291],[5.092793,52.070455],[5.090706,52.071601],[5.093852,52.072047],[5.094362,52.074945],[5.096972,52.074335],[5.095137,52.077455],[5.097140,52.078455],[5.097852,52.083605]]],[[[5.135852,52.111906],[5.122630,52.109677],[5.125079,52.104682],[5.131567,52.103455],[5.133852,52.107889],[5.138735,52.109455],[5.136394,52.109997],[5.135852,52.111906]]]],"type":"MultiPolygon"},"type":"Feature"}],"type":"FeatureCollection"})"; + try_isochrone(loki_worker, thor_worker, request, expected); + } + + // holes + { + const auto request = + R"({"costing":"auto","locations":[{"lon":5.042799,"lat":52.093199}],"contours":[{"time":1}],"denoise":0,"generalize":0,"polygons":true})"; + const auto expected = + R"({"features":[{"properties":{"fill-opacity":0.33,"fillColor":"#bf4040","opacity":0.33,"fill":"#bf4040","fillOpacity":0.33,"color":"#bf4040","contour":1,"metric":"time"},"geometry":{"coordinates":[[[5.045799,52.097113],[5.045609,52.096389],[5.045676,52.096199],[5.045666,52.096066],[5.045581,52.095418],[5.045567,52.095199],[5.045254,52.094744],[5.044799,52.094582],[5.044403,52.094803],[5.044163,52.095199],[5.044032,52.095432],[5.043799,52.095523],[5.043400,52.095598],[5.043190,52.095590],[5.042799,52.095720],[5.042367,52.095631],[5.042161,52.095561],[5.041799,52.095469],[5.041527,52.095471],[5.041071,52.095471],[5.040799,52.095474],[5.040602,52.095396],[5.040450,52.095199],[5.040290,52.094708],[5.040280,52.094680],[5.040116,52.094199],[5.040004,52.093994],[5.039799,52.093869],[5.039495,52.093895],[5.039043,52.094199],[5.038919,52.094319],[5.038799,52.094404],[5.038532,52.094466],[5.038066,52.094466],[5.037799,52.094582],[5.037714,52.094284],[5.037742,52.094199],[5.037736,52.094136],[5.037799,52.093590],[5.037966,52.093366],[5.037944,52.093199],[5.038467,52.092867],[5.038453,52.092545],[5.038578,52.092199],[5.038637,52.092037],[5.038799,52.091994],[5.039295,52.091695],[5.039569,52.091199],[5.039495,52.090895],[5.039617,52.090381],[5.039419,52.090199],[5.039721,52.090121],[5.039799,52.090100],[5.039974,52.090024],[5.040658,52.090058],[5.040799,52.089352],[5.040865,52.089265],[5.041009,52.089199],[5.041619,52.089019],[5.041799,52.089031],[5.041953,52.089045],[5.042508,52.088908],[5.042799,52.088922],[5.043037,52.088961],[5.043467,52.089199],[5.043594,52.089404],[5.043799,52.089847],[5.043856,52.090142],[5.043843,52.090199],[5.043923,52.090323],[5.044099,52.090899],[5.044517,52.090917],[5.044799,52.091046],[5.044977,52.091021],[5.045318,52.091199],[5.045073,52.091473],[5.045140,52.091858],[5.045055,52.092199],[5.045329,52.092669],[5.045799,52.092930],[5.046160,52.092838],[5.046485,52.092885],[5.046799,52.092622],[5.047059,52.092939],[5.047427,52.093199],[5.047577,52.093421],[5.047799,52.093565],[5.048184,52.093584],[5.048744,52.093254],[5.048799,52.093269],[5.048965,52.094034],[5.049327,52.094199],[5.048887,52.094287],[5.048799,52.094420],[5.048690,52.094308],[5.048046,52.094446],[5.047799,52.094385],[5.047419,52.094579],[5.047212,52.094612],[5.047127,52.094871],[5.046920,52.095199],[5.046927,52.095327],[5.046799,52.095577],[5.046439,52.095839],[5.046181,52.096199],[5.046142,52.096542],[5.045799,52.097113]],[[5.044201,52.093601],[5.044464,52.093199],[5.044483,52.092883],[5.044539,52.092460],[5.044557,52.092199],[5.044391,52.091791],[5.044441,52.091557],[5.044172,52.091572],[5.043799,52.091373],[5.043601,52.091397],[5.043105,52.091505],[5.042799,52.091535],[5.042392,52.091792],[5.042242,52.092199],[5.042445,52.092553],[5.042799,52.092699],[5.043132,52.092866],[5.043299,52.093199],[5.043449,52.093549],[5.043799,52.093738],[5.044201,52.093601]]],"type":"Polygon"},"type":"Feature"}],"type":"FeatureCollection"})"; try_isochrone(loki_worker, thor_worker, request, expected); } } @@ -264,14 +333,178 @@ TEST(Isochrones, test_max_reserved_labels_count) { isochrone.Clear(); } +#ifdef ENABLE_GDAL + +void check_raster_edges(size_t x, size_t y, uint16_t* data) { + + // make sure the outer "edges" are not 0 + for (size_t i = 0; i < y; ++i) { + // if not in first or last row + if (i != 0 || i != y - 1) { + // just check first and last element in the row + ASSERT_NE(data[i * y], 0); + ASSERT_NE(data[i * y + x], 0); + continue; + } + + // else check the whole row + for (size_t j = 0; j < x; ++j) { + ASSERT_NE(data[i * y + j], 0); + } + } +} + +TEST(Isochrones, test_geotiff_output_distance) { + loki_worker_t loki_worker(cfg); + thor_worker_t thor_worker(cfg); + + const auto request = + R"({"costing":"auto","locations":[{"lon":5.042799,"lat":52.093199}],"contours":[{"distance":1}], "format": "geotiff"})"; + Api request_pbf; + ParseApi(request, Options::isochrone, request_pbf); + loki_worker.isochrones(request_pbf); + std::string geotiff = thor_worker.isochrones(request_pbf); + + std::string name = "/vsimem/test_isogrid_geotiff_d.tif"; + unsigned char buffer[geotiff.length()]; + std::copy(geotiff.cbegin(), geotiff.cend(), buffer); + auto handle = VSIFileFromMemBuffer(name.c_str(), buffer, static_cast(geotiff.size()), 0); + auto geotiff_dataset = GDALDataset::FromHandle(GDALOpen(name.c_str(), GA_ReadOnly)); + int x = geotiff_dataset->GetRasterXSize(); + int y = geotiff_dataset->GetRasterYSize(); + GDALRasterBand* band = geotiff_dataset->GetRasterBand(1); + uint16_t data_array[x * y]; + CPLErr err = band->RasterIO(GF_Read, 0, 0, x, y, data_array, x, y, GDT_UInt16, 0, 0); + double min_max[2]; + + band->ComputeRasterMinMax(0, min_max); + + ASSERT_EQ(err, CE_None); + ASSERT_NE(x, 0); + ASSERT_NE(y, 0); + ASSERT_EQ(static_cast(min_max[0]), 0); + ASSERT_EQ(static_cast(min_max[1]), 1100); + ASSERT_EQ(band->GetNoDataValue(), std::numeric_limits::max()); + size_t array_size = x * y; + + check_raster_edges(x, y, data_array); + + // make sure there are some grid cells whose metric value is neither 0 nor the max + bool no_intermediate_values = true; + for (size_t i = 0; i < array_size; ++i) { + if (data_array[i] > 0 && data_array[i] < min_max[1]) + no_intermediate_values = false; + } + ASSERT_EQ(no_intermediate_values, false); + VSIFCloseL(handle); +} + +TEST(Isochrones, test_geotiff_output_time) { + loki_worker_t loki_worker(cfg); + thor_worker_t thor_worker(cfg); + + const auto request = + R"({"costing":"auto","locations":[{"lon":5.042799,"lat":52.093199}],"contours":[{"time":1}], "format": "geotiff"})"; + Api request_pbf; + ParseApi(request, Options::isochrone, request_pbf); + loki_worker.isochrones(request_pbf); + std::string geotiff = thor_worker.isochrones(request_pbf); + + std::string name = "/vsimem/test_isogrid_geotiff_t.tif"; + unsigned char buffer[geotiff.length()]; + std::copy(geotiff.cbegin(), geotiff.cend(), buffer); + auto handle = VSIFileFromMemBuffer(name.c_str(), buffer, static_cast(geotiff.size()), 0); + auto geotiff_dataset = GDALDataset::FromHandle(GDALOpen(name.c_str(), GA_ReadOnly)); + int x = geotiff_dataset->GetRasterXSize(); + int y = geotiff_dataset->GetRasterYSize(); + GDALRasterBand* band = geotiff_dataset->GetRasterBand(1); + uint16_t data_array[x * y]; + CPLErr err = band->RasterIO(GF_Read, 0, 0, x, y, data_array, x, y, GDT_UInt16, 0, 0); + double min_max[2]; + + band->ComputeRasterMinMax(0, min_max); + + ASSERT_EQ(err, CE_None); + ASSERT_GT(x, 0); + ASSERT_GT(y, 0); + ASSERT_EQ(static_cast(min_max[0]), 0); + ASSERT_EQ(static_cast(min_max[1]), 660); + ASSERT_EQ(band->GetNoDataValue(), std::numeric_limits::max()); + size_t array_size = x * y; + + check_raster_edges(x, y, data_array); + + // make sure there are some grid cells whose metric value is neither 0 nor the max + bool no_intermediate_values = true; + for (size_t i = 0; i < array_size; ++i) { + if (data_array[i] > 0 && data_array[i] < min_max[1]) + no_intermediate_values = false; + } + ASSERT_EQ(no_intermediate_values, false); + VSIFCloseL(handle); +} + +// test request with two metrics +TEST(Isochrones, test_geotiff_output_time_distance) { + loki_worker_t loki_worker(cfg); + thor_worker_t thor_worker(cfg); + + const auto request = + R"({"costing":"auto","locations":[{"lon":5.042799,"lat":52.093199}],"contours":[{"time":1},{"distance":2}], "format": "geotiff"})"; + Api request_pbf; + ParseApi(request, Options::isochrone, request_pbf); + loki_worker.isochrones(request_pbf); + std::string geotiff = thor_worker.isochrones(request_pbf); + + std::string name = "/vsimem/test_isogrid_geotiff_td.tif"; + unsigned char buffer[geotiff.length()]; + std::copy(geotiff.cbegin(), geotiff.cend(), buffer); + auto handle = VSIFileFromMemBuffer(name.c_str(), buffer, static_cast(geotiff.size()), 0); + auto geotiff_dataset = GDALDataset::FromHandle(GDALOpen(name.c_str(), GA_ReadOnly)); + int x = geotiff_dataset->GetRasterXSize(); + int y = geotiff_dataset->GetRasterYSize(); + + // time, distance + std::array expected_max{660, 1200}; + + for (int b = 1; b <= 2; ++b) { + GDALRasterBand* band = geotiff_dataset->GetRasterBand(b); + uint16_t data_array[x * y]; + CPLErr err = band->RasterIO(GF_Read, 0, 0, x, y, data_array, x, y, GDT_UInt16, 0, 0); + double min_max[2]; + + band->ComputeRasterMinMax(0, min_max); + + ASSERT_EQ(err, CE_None); + ASSERT_NE(x, 0); + ASSERT_NE(y, 0); + ASSERT_EQ(static_cast(min_max[0]), 0); + ASSERT_EQ(static_cast(min_max[1]), expected_max[b - 1]); + ASSERT_EQ(band->GetNoDataValue(), std::numeric_limits::max()); + size_t array_size = x * y; + + check_raster_edges(x, y, data_array); + + // make sure there are some grid cells whose metric value is neither 0 nor the max + bool no_intermediate_values = true; + for (size_t j = 0; j < array_size; ++j) { + if (data_array[j] > 0 && data_array[j] < min_max[1]) + no_intermediate_values = false; + } + ASSERT_EQ(no_intermediate_values, false); + } + VSIFCloseL(handle); +} +#endif + } // namespace int main(int argc, char* argv[]) { // user wants to try it if (argc > 1) { - loki_worker_t loki_worker(config); - thor_worker_t thor_worker(config); - GraphReader reader(config.get_child("mjolnir")); + loki_worker_t loki_worker(cfg); + thor_worker_t thor_worker(cfg); + GraphReader reader(cfg.get_child("mjolnir")); Api request; ParseApi(argv[1], Options::isochrone, request); loki_worker.isochrones(request); diff --git a/test/logging.cc b/test/logging.cc index 91d0972fed..cdf32bd834 100644 --- a/test/logging.cc +++ b/test/logging.cc @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -57,12 +56,8 @@ TEST(Logging, FileLoggerTest) { results.emplace_back(std::async(std::launch::async, work)); } - // dont really care about the results but we can pretend - int exit_code = 0; for (auto& result : results) { - try { - size_t count = result.get(); - } catch (std::exception& e) { exit_code++; } + ASSERT_NO_THROW(result.get()); } // wait for logger to close and reopen the file diff --git a/test/loki_service.cc b/test/loki_service.cc index 0f18044725..dbb3f1072d 100644 --- a/test/loki_service.cc +++ b/test/loki_service.cc @@ -1,5 +1,7 @@ #include "test.h" + #include +#include #include "baldr/rapidjson_utils.h" #include "midgard/logging.h" @@ -12,7 +14,6 @@ #include #include -#include "filesystem.h" #include "loki/worker.h" #include "odin/worker.h" #include "thor/worker.h" @@ -379,9 +380,9 @@ boost::property_tree::ptree make_config(const std::vector& whitelis "centroid", "status", }) { - auto run_dir = VALHALLA_BUILD_DIR "test" + std::string(1, filesystem::path::preferred_separator) + - "loki_service_tmp"; - if (!filesystem::is_directory(run_dir) && !filesystem::create_directories(run_dir)) + auto run_dir = VALHALLA_BUILD_DIR "test" + + std::string(1, std::filesystem::path::preferred_separator) + "loki_service_tmp"; + if (!std::filesystem::is_directory(run_dir) && !std::filesystem::create_directories(run_dir)) throw std::runtime_error("Couldnt make directory to run from"); auto config = test::make_config(run_dir, @@ -581,7 +582,7 @@ TEST(LokiService, test_actions_whitelist) { } TEST(LokiService, test_hierarchy_warning) { - // all actions involving disble_hierarchy_pruning + // all actions involving disable_hierarchy_pruning const std::vector actions{Options_Action_route, Options_Action_sources_to_targets}; // all relevant costing types, with or without warning const std::vector costing_types{Costing_Type_bicycle, Costing_Type_bus, @@ -606,9 +607,6 @@ class LokiServiceEnv : public ::testing::Environment { // Elevation service int main(int argc, char* argv[]) { - // make this whole thing bail if it doesnt finish fast - alarm(180); - testing::AddGlobalTestEnvironment(new LokiServiceEnv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/test/lua.cc b/test/lua.cc index 669e76cee3..5110a54185 100644 --- a/test/lua.cc +++ b/test/lua.cc @@ -103,6 +103,34 @@ TEST(Lua, NumberDoublePeriod) { // ... but that the results aren't completely empty ASSERT_TRUE(results.size() > 0); } + +TEST(Lua, TestForwardBackward) { + // Way 25494427 version 14 has a "maxheight" tag value of 3..35 with the two + // dots. This probably shouldn't be parsed? + mjolnir::LuaTagTransform lua(std::string(lua_graph_lua, lua_graph_lua + lua_graph_lua_len)); + + mjolnir::Tags tags; + tags.insert({"highway", "tertiary"}); + tags.insert({"maxheight:forward", "1"}); + tags.insert({"maxheight:backward", "1"}); + tags.insert({"maxlength:forward", "1"}); + tags.insert({"maxlength:backward", "1"}); + tags.insert({"maxwidth:forward", "1"}); + tags.insert({"maxwidth:backward", "1"}); + tags.insert({"maxweight:forward", "1"}); + tags.insert({"maxweight:backward", "1"}); + auto results = lua.Transform(mjolnir::OSMType::kWay, 1, tags); + + // check that the maxheight is present... + ASSERT_TRUE(results.count("maxheight_forward") == 1); + ASSERT_TRUE(results.count("maxheight_backward") == 1); + ASSERT_TRUE(results.count("maxlength_forward") == 1); + ASSERT_TRUE(results.count("maxlength_backward") == 1); + ASSERT_TRUE(results.count("maxwidth_forward") == 1); + ASSERT_TRUE(results.count("maxwidth_backward") == 1); + ASSERT_TRUE(results.count("maxweight_forward") == 1); + ASSERT_TRUE(results.count("maxweight_backward") == 1); +} } // namespace // TODO: sweet jesus add more tests of this class! diff --git a/test/maneuversbuilder.cc b/test/maneuversbuilder.cc index 1f3612794e..dd6ecacbe2 100644 --- a/test/maneuversbuilder.cc +++ b/test/maneuversbuilder.cc @@ -1148,7 +1148,7 @@ TEST(Maneuversbuilder, TestLeftInternalUturnCombine) { valhalla::RoadClass::kPrimary, 36, 32, 0, 2, TripLeg_Traversability_kBoth, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}); - // node:1 TURN_CHANNNEL + // node:1 TURN_CHANNEL node = path.add_node(); edge = node->mutable_edge(); PopulateEdge(edge, {{"Devonshire Road", 0}}, 0.013000, 50.000000, valhalla::RoadClass::kTertiary, @@ -1242,7 +1242,7 @@ TEST(Maneuversbuilder, TestLeftInternalUturnProperDirectionCombine) { valhalla::RoadClass::kPrimary, 48, 52, 0, 3, TripLeg_Traversability_kBoth, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}); - // node:1 TURN_CHANNNEL + // node:1 TURN_CHANNEL node = path.add_node(); edge = node->mutable_edge(); PopulateEdge(edge, {{"Moravia Park Drive", 0}}, 0.019000, 60.000000, @@ -1343,7 +1343,7 @@ TEST(Maneuversbuilder, TestStraightInternalLeftInternalStraightInternalUturnComb valhalla::RoadClass::kTrunk, 335, 334, 0, 2, TripLeg_Traversability_kBoth, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}); - // node:1 TURN_CHANNNEL + // node:1 TURN_CHANNEL node = path.add_node(); edge = node->mutable_edge(); PopulateEdge(edge, {{"MD 24", 1}, {"Vietnam Veterans Memorial Highway", 0}}, 0.012000, 89.000000, @@ -1452,7 +1452,7 @@ TEST(Maneuversbuilder, TestInternalPencilPointUturnProperDirectionCombine) { valhalla::RoadClass::kUnclassified, 352, 343, 0, 2, TripLeg_Traversability_kBoth, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}); - // node:1 TURN_CHANNNEL + // node:1 TURN_CHANNEL node = path.add_node(); edge = node->mutable_edge(); PopulateEdge(edge, {{"Old Carolina Road", 0}}, 0.019000, 50.000000, valhalla::RoadClass::kTertiary, @@ -1552,7 +1552,7 @@ TEST(Maneuversbuilder, TestSimpleRightTurnChannelCombine) { valhalla::RoadClass::kTrunk, 59, 94, 0, 4, TripLeg_Traversability_kBoth, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, {}, {}); - // node:1 TURN_CHANNNEL + // node:1 TURN_CHANNEL node = path.add_node(); edge = node->mutable_edge(); PopulateEdge(edge, {}, 0.142000, 113.000000, valhalla::RoadClass::kSecondary, 105, 179, 4, 11, diff --git a/test/map_matcher_factory.cc b/test/map_matcher_factory.cc index 05bed1325a..e16b509130 100644 --- a/test/map_matcher_factory.cc +++ b/test/map_matcher_factory.cc @@ -23,8 +23,8 @@ using ptree = boost::property_tree::ptree; void create_costing_options(Costing::Type costing, Options& options) { const rapidjson::Document doc; - sif::ParseCosting(doc, "/costing_options", options); options.set_costing_type(costing); + sif::ParseCosting(doc, "/costing_options", options); } TEST(MapMatcherFactory, TestMapMatcherFactory) { @@ -124,7 +124,7 @@ TEST(MapMatcherFactory, TestMapMatcherFactory) { delete matcher; - options.set_costing_type(Costing::bicycle); + create_costing_options(Costing::bicycle, options); matcher = factory.Create(options); EXPECT_EQ(matcher->travelmode(), sif::TravelMode::kBicycle) << "should read costing in options correctly again"; @@ -139,7 +139,7 @@ TEST(MapMatcherFactory, TestMapMatcherFactory) { meili::MapMatcherFactory factory(root); options.set_costing_type(Costing::pedestrian); auto matcher = factory.Create(options); - EXPECT_NE(matcher->costing()->travel_type(), (int)sif::PedestrianType::kSegway) + EXPECT_NE(matcher->costing()->travel_type(), (int)sif::PedestrianType::kWheelchair) << "should not have custom costing options when not set in preferences"; delete matcher; @@ -147,10 +147,10 @@ TEST(MapMatcherFactory, TestMapMatcherFactory) { options.mutable_costings() ->find(Costing::pedestrian) ->second.mutable_options() - ->set_transport_type("segway"); + ->set_transport_type("wheelchair"); matcher = factory.Create(options); - EXPECT_EQ(matcher->costing()->travel_type(), (int)sif::PedestrianType::kSegway) + EXPECT_EQ(matcher->costing()->travel_type(), (int)sif::PedestrianType::kWheelchair) << "should read custom costing options in preferences correctly"; delete matcher; @@ -167,7 +167,7 @@ TEST(MapMatcherFactory, TestMapMatcher) { Options options; create_costing_options(Costing::auto_, options); auto auto_matcher = factory.Create(options); - options.set_costing_type(Costing::pedestrian); + create_costing_options(Costing::pedestrian, options); auto pedestrian_matcher = factory.Create(options); // Share the same pool diff --git a/test/mapmatch.cc b/test/mapmatch.cc index 0d9fe0d357..ea61dfae03 100644 --- a/test/mapmatch.cc +++ b/test/mapmatch.cc @@ -115,18 +115,6 @@ std::string json_escape(const std::string& unescaped) { return escaped; } -std::string output_shape(const valhalla::Api& api) { - std::stringstream shape; - for (const auto& r : api.directions().routes()) { - shape << "new route" << std::endl; - for (const auto& l : r.legs()) { - shape << std::fixed << std::setprecision(3) << "Time : " << l.summary().time() - << ", length : " << l.summary().length() << ", shape : " << l.shape() << std::endl; - } - } - return shape.str(); -} - void compare_results(const valhalla::Api& expected, const valhalla::Api& result) { // check the number of routes match ASSERT_EQ(result.trip().routes_size(), expected.trip().routes_size()) @@ -1036,12 +1024,12 @@ TEST(Mapmatch, test_leg_duration_trimming) { EXPECT_EQ(route_api.trip().routes_size(), match_api.trip().routes_size()) << "Number of routes differs"; - for (size_t i = 0; i < route_api.trip().routes_size(); ++i) { + for (int i = 0; i < route_api.trip().routes_size(); ++i) { const auto& rlegs = route_api.trip().routes(i).legs(); const auto& mlegs = match_api.trip().routes(i).legs(); EXPECT_EQ(rlegs.size(), mlegs.size()) << "Number of legs differs"; - printf("Route %zu\n", i); - for (size_t j = 0; j < rlegs.size(); ++j) { + printf("Route %d\n", i); + for (int j = 0; j < rlegs.size(); ++j) { auto rtime = rlegs.Get(j).node().rbegin()->cost().elapsed_cost().seconds(); auto mtime = mlegs.Get(j).node().rbegin()->cost().elapsed_cost().seconds(); printf("r: %.2f %s\n", rtime, rlegs.Get(j).shape().c_str()); @@ -1090,7 +1078,7 @@ TEST(Mapmatch, test_discontinuity_on_same_edge) { for (size_t i = 0; i < test_cases.size(); ++i) { auto result = tester.match(test_cases[i]); EXPECT_EQ(result.trip().routes_size(), test_ans_num_routes[i]); - int j = 0, k = 0; + int j = 0; for (const auto& route : result.trip().routes()) { ASSERT_EQ(route.legs_size(), test_ans_num_legs[i][j++]) << "Expected " + std::to_string(test_ans_num_legs[i][j - 1]) + " legs but got " + diff --git a/test/mapmatch_config.cc b/test/mapmatch_config.cc index f331b288f4..c750a42c9e 100644 --- a/test/mapmatch_config.cc +++ b/test/mapmatch_config.cc @@ -33,12 +33,12 @@ TEST(MapmatchConfig, check_read_all_params) { config.Read(fake_config); // check candidate search params - const auto& candiate_search = config.candidate_search; - EXPECT_EQ(candiate_search.search_radius_meters, 10.f); - EXPECT_TRUE(candiate_search.is_search_radius_customizable); - EXPECT_EQ(candiate_search.max_search_radius_meters, 500.f); - EXPECT_EQ(candiate_search.grid_size, 100); - EXPECT_EQ(candiate_search.cache_size, 100500); + const auto& candidate_search = config.candidate_search; + EXPECT_EQ(candidate_search.search_radius_meters, 10.f); + EXPECT_TRUE(candidate_search.is_search_radius_customizable); + EXPECT_EQ(candidate_search.max_search_radius_meters, 500.f); + EXPECT_EQ(candidate_search.grid_size, 100); + EXPECT_EQ(candidate_search.cache_size, 100500); // check transition params const auto& transition = config.transition_cost; diff --git a/test/matrix.cc b/test/matrix.cc index 9aad003776..5428949151 100644 --- a/test/matrix.cc +++ b/test/matrix.cc @@ -1,6 +1,5 @@ #include "test.h" -#include #include #include @@ -136,9 +135,7 @@ const std::unordered_map kMaxDistances = { {"taxi", 43200.0f}, }; // a scale factor to apply to the score so that we bias towards closer results more -constexpr float kDistanceScale = 10.f; - -const auto config = test::make_config("test/data/utrecht_tiles"); +const auto cfg = test::make_config("test/data/utrecht_tiles"); const auto test_request = R"({ "sources":[ @@ -169,13 +166,33 @@ const auto test_request_osrm = R"({ {"lat":52.103105,"lon":5.081005}, {"lat":52.094273,"lon":5.075254} ], - "costing":"auto" + "costing":"auto", + "format": "osrm" })"; -std::vector matrix_answers = {{28, 28}, {2027, 1837}, {2403, 2213}, {4163, 3838}, - {1519, 1398}, {1808, 1638}, {2061, 1951}, {3944, 3639}, - {2311, 2111}, {701, 641}, {0, 0}, {2821, 2626}, - {5562, 5177}, {3952, 3707}, {4367, 4107}, {1825, 1680}}; +// clang-format off +std::vector> matrix_answers = {{28, 28}, {2027, 1837}, {2403, 2213}, {4163, 3838}, + {1519, 1398}, {1808, 1638}, {2061, 1951}, {3944, 3639}, + {2311, 2111}, {701, 641}, {0, 0}, {2821, 2626}, + {5562, 5177}, {3952, 3707}, {4367, 4107}, {1825, 1680}}; +// clang-format on + +void check_osrm_response(std::string& res, std::string& algo) { + rapidjson::Document res_doc; + res_doc.Parse(res); + + ASSERT_FALSE(res_doc.HasParseError()); + + std::string status = "Ok"; + EXPECT_EQ(res_doc["code"].GetString(), status) << "Didn't work for " + algo; + EXPECT_EQ(res_doc["algorithm"].GetString(), algo) << "Didn't work for " + algo; + + EXPECT_NEAR(res_doc["distances"].GetArray()[0][0].GetDouble(), 28, 1) << "Didn't work for " + algo; + EXPECT_EQ(res_doc["durations"].GetArray()[0][0].GetInt64(), 28) << "Didn't work for " + algo; + EXPECT_NEAR(res_doc["distances"].GetArray()[3][3].GetDouble(), 1680, 1) + << "Didn't work for " + algo; + EXPECT_EQ(res_doc["durations"].GetArray()[3][3].GetInt64(), 1825) << "Didn't work for " + algo; +} } // namespace const uint32_t kThreshold = 1; @@ -184,78 +201,127 @@ bool within_tolerance(const uint32_t v1, const uint32_t v2) { } TEST(Matrix, test_matrix) { - loki_worker_t loki_worker(config); + loki_worker_t loki_worker(cfg); Api request; ParseApi(test_request, Options::sources_to_targets, request); loki_worker.matrix(request); thor_worker_t::adjust_scores(*request.mutable_options()); - GraphReader reader(config.get_child("mjolnir")); + GraphReader reader(cfg.get_child("mjolnir")); sif::mode_costing_t mode_costing; mode_costing[0] = CreateSimpleCost(request.options().costings().find(request.options().costing_type())->second); CostMatrix cost_matrix; - std::vector results = - cost_matrix.SourceToTarget(*request.mutable_options()->mutable_sources(), - *request.mutable_options()->mutable_targets(), reader, mode_costing, - sif::TravelMode::kDrive, 400000.0); - for (uint32_t i = 0; i < results.size(); ++i) { - EXPECT_NEAR(results[i].dist, matrix_answers[i].dist, kThreshold) + cost_matrix.SourceToTarget(request, reader, mode_costing, sif::TravelMode::kDrive, 400000.0); + auto matrix = request.matrix(); + for (int i = 0; i < matrix.times().size(); ++i) { + EXPECT_NEAR(matrix.distances()[i], matrix_answers[i][1], kThreshold) << "result " + std::to_string(i) + "'s distance is not close enough" + " to expected value for CostMatrix"; - EXPECT_NEAR(results[i].time, matrix_answers[i].time, kThreshold) + EXPECT_NEAR(matrix.times()[i], matrix_answers[i][0], kThreshold) << "result " + std::to_string(i) + "'s time is not close enough" + " to expected value for CostMatrix"; } + request.clear_matrix(); CostMatrix cost_matrix_abort_source; - results = - cost_matrix_abort_source.SourceToTarget(*request.mutable_options()->mutable_sources(), - *request.mutable_options()->mutable_targets(), reader, - mode_costing, sif::TravelMode::kDrive, 100000.0); + cost_matrix_abort_source.SourceToTarget(request, reader, mode_costing, sif::TravelMode::kDrive, + 7000.0); + matrix = request.matrix(); uint32_t found = 0; - for (uint32_t i = 0; i < results.size(); ++i) { - if (results[i].dist < kMaxCost) { + for (int i = 0; i < matrix.times().size(); ++i) { + if (matrix.distances()[i] < kMaxCost) { ++found; } } EXPECT_EQ(found, 15) << " not the number of results as expected"; + request.clear_matrix(); CostMatrix cost_matrix_abort_target; - results = - cost_matrix_abort_target.SourceToTarget(*request.mutable_options()->mutable_sources(), - *request.mutable_options()->mutable_targets(), reader, - mode_costing, sif::TravelMode::kDrive, 50000.0); + cost_matrix_abort_target.SourceToTarget(request, reader, mode_costing, sif::TravelMode::kDrive, + 5000.0); + matrix = request.matrix(); found = 0; - for (uint32_t i = 0; i < results.size(); ++i) { - if (results[i].dist < kMaxCost) { + for (int i = 0; i < matrix.times().size(); ++i) { + if (matrix.distances()[i] < kMaxCost) { ++found; } } - EXPECT_EQ(found, 10) << " not the number of results as expected"; + EXPECT_EQ(found, 13) << " not the number of results as expected"; + request.clear_matrix(); TimeDistanceMatrix timedist_matrix; - results = timedist_matrix.SourceToTarget(*request.mutable_options()->mutable_sources(), - *request.mutable_options()->mutable_targets(), reader, - mode_costing, sif::TravelMode::kDrive, 400000.0); - for (uint32_t i = 0; i < results.size(); ++i) { - EXPECT_NEAR(results[i].dist, matrix_answers[i].dist, kThreshold) - << "result " + std::to_string(i) + "'s distance is not equal to" + - " the expected value for TimeDistMatrix"; - - EXPECT_NEAR(results[i].time, matrix_answers[i].time, kThreshold) - << "result " + std::to_string(i) + - "'s time is not equal to the expected value for TimeDistMatrix"; + timedist_matrix.SourceToTarget(request, reader, mode_costing, sif::TravelMode::kDrive, 400000.0); + + matrix = request.matrix(); + for (int i = 0; i < matrix.times().size(); ++i) { + EXPECT_NEAR(matrix.distances()[i], matrix_answers[i][1], kThreshold) + << "result " + std::to_string(i) + "'s distance is not equal" + + " to expected value for TDMatrix"; + + EXPECT_NEAR(matrix.times()[i], matrix_answers[i][0], kThreshold) + << "result " + std::to_string(i) + "'s time is not equal" + " to expected value for TDMatrix"; } } -TEST(Matrix, test_timedistancematrix_results_sequence) { +TEST(Matrix, test_timedistancematrix_forward) { + // Input request is the same as `test_request`, but without the last target + const auto test_request_more_sources = R"({ + "sources":[ + {"lat":52.106337,"lon":5.101728}, + {"lat":52.111276,"lon":5.089717}, + {"lat":52.103105,"lon":5.081005} + ], + "targets":[ + {"lat":52.106126,"lon":5.101497}, + {"lat":52.100469,"lon":5.087099}, + {"lat":52.103105,"lon":5.081005}, + {"lat":52.094273,"lon":5.075254} + ], + "costing":"auto" + })"; + + loki_worker_t loki_worker(cfg); + + Api request; + ParseApi(test_request_more_sources, Options::sources_to_targets, request); + loki_worker.matrix(request); + thor_worker_t::adjust_scores(*request.mutable_options()); + + GraphReader reader(cfg.get_child("mjolnir")); + + sif::mode_costing_t mode_costing; + mode_costing[0] = + CreateSimpleCost(request.options().costings().find(request.options().costing_type())->second); + + TimeDistanceMatrix timedist_matrix; + timedist_matrix.SourceToTarget(request, reader, mode_costing, sif::TravelMode::kDrive, 400000.0); + auto& matrix = request.matrix(); + + // expected results are the same as `matrix_answers`, but without the last origin + // clang-format off + std::vector> expected_results = {{28, 28}, {2027, 1837}, {2403, 2213}, {4163, 3838}, + {1519, 1398}, {1808, 1638}, {2061, 1951}, {3944, 3639}, + {2311, 2111}, {701, 641}, {0, 0}, {2821, 2626}}; + // clang-format on + + for (int i = 0; i < matrix.times().size(); ++i) { + EXPECT_NEAR(matrix.distances()[i], expected_results[i][1], kThreshold) + << "result " + std::to_string(i) + "'s distance is not equal" + + " to expected value for TDMatrix"; + + EXPECT_NEAR(matrix.times()[i], expected_results[i][0], kThreshold) + << "result " + std::to_string(i) + "'s time is not equal" + " to expected value for TDMatrix"; + } +} + +TEST(Matrix, test_timedistancematrix_reverse) { // Input request is the same as `test_request`, but without the last target const auto test_request_more_sources = R"({ "sources":[ @@ -272,45 +338,43 @@ TEST(Matrix, test_timedistancematrix_results_sequence) { "costing":"auto" })"; - loki_worker_t loki_worker(config); + loki_worker_t loki_worker(cfg); Api request; ParseApi(test_request_more_sources, Options::sources_to_targets, request); loki_worker.matrix(request); thor_worker_t::adjust_scores(*request.mutable_options()); - GraphReader reader(config.get_child("mjolnir")); + GraphReader reader(cfg.get_child("mjolnir")); sif::mode_costing_t mode_costing; mode_costing[0] = CreateSimpleCost(request.options().costings().find(request.options().costing_type())->second); TimeDistanceMatrix timedist_matrix; - std::vector results = - timedist_matrix.SourceToTarget(*request.mutable_options()->mutable_sources(), - *request.mutable_options()->mutable_targets(), reader, - mode_costing, sif::TravelMode::kDrive, 400000.0); - - // expected results are the same as `matrix_answers`, but without the last column - std::vector expected_results = { - {28, 28}, {2027, 1837}, {2403, 2213}, {1519, 1398}, {1808, 1638}, {2061, 1951}, - {2311, 2111}, {701, 641}, {0, 0}, {5562, 5177}, {3952, 3707}, {4367, 4107}, - }; - - for (uint32_t i = 0; i < results.size(); ++i) { - EXPECT_NEAR(results[i].dist, expected_results[i].dist, kThreshold) - << "result " + std::to_string(i) + "'s distance is not equal to" + - " the expected value for TimeDistMatrix"; - - EXPECT_NEAR(results[i].time, expected_results[i].time, kThreshold) - << "result " + std::to_string(i) + - "'s time is not equal to the expected value for TimeDistMatrix"; + timedist_matrix.SourceToTarget(request, reader, mode_costing, sif::TravelMode::kDrive, 400000.0); + auto& matrix = request.matrix(); + + // expected results are the same as `matrix_answers`, but without the last target + // clang-format off + std::vector> expected_results = {{28, 28}, {2027, 1837}, {2403, 2213}, + {1519, 1398}, {1808, 1638}, {2061, 1951}, + {2311, 2111}, {701, 641}, {0, 0}, + {5562, 5177}, {3952, 3707}, {4367, 4107}}; + // clang-format on + + for (int i = 0; i < matrix.times().size(); ++i) { + EXPECT_NEAR(matrix.distances()[i], expected_results[i][1], kThreshold) + << "result " + std::to_string(i) + "'s distance is not equal" + + " to expected value for TDMatrix"; + + EXPECT_NEAR(matrix.times()[i], expected_results[i][0], kThreshold) + << "result " + std::to_string(i) + "'s time is not equal" + " to expected value for TDMatrix"; } } -// TODO: it was commented before. Why? -TEST(Matrix, DISABLED_test_matrix_osrm) { - loki_worker_t loki_worker(config); +TEST(Matrix, test_matrix_osrm) { + loki_worker_t loki_worker(cfg); Api request; ParseApi(test_request_osrm, Options::sources_to_targets, request); @@ -318,40 +382,23 @@ TEST(Matrix, DISABLED_test_matrix_osrm) { loki_worker.matrix(request); thor_worker_t::adjust_scores(*request.mutable_options()); - GraphReader reader(config.get_child("mjolnir")); + GraphReader reader(cfg.get_child("mjolnir")); sif::mode_costing_t mode_costing; mode_costing[0] = CreateSimpleCost(request.options().costings().find(request.options().costing_type())->second); CostMatrix cost_matrix; - std::vector results; - results = cost_matrix.SourceToTarget(*request.mutable_options()->mutable_sources(), - *request.mutable_options()->mutable_targets(), reader, - mode_costing, sif::TravelMode::kDrive, 400000.0); - for (uint32_t i = 0; i < results.size(); ++i) { - EXPECT_EQ(results[i].dist, matrix_answers[i].dist) - << "result " + std::to_string(i) + - "'s distance is not close enough to expected value for CostMatrix."; - - EXPECT_EQ(results[i].time, matrix_answers[i].time) - << "result " + std::to_string(i) + - "'s time is not close enough to expected value for CostMatrix."; - } + cost_matrix.SourceToTarget(request, reader, mode_costing, sif::TravelMode::kDrive, 400000.0); + auto json_res = tyr::serializeMatrix(request); + std::string algo = "costmatrix"; + check_osrm_response(json_res, algo); TimeDistanceMatrix timedist_matrix; - results = timedist_matrix.SourceToTarget(*request.mutable_options()->mutable_sources(), - *request.mutable_options()->mutable_sources(), reader, - mode_costing, sif::TravelMode::kDrive, 400000.0); - for (uint32_t i = 0; i < results.size(); ++i) { - EXPECT_EQ(results[i].dist, matrix_answers[i].dist) - << "result " + std::to_string(i) + - "'s distance is not equal to the expected value for TimeDistMatrix."; - - EXPECT_EQ(results[i].time, matrix_answers[i].time) - << "result " + std::to_string(i) + - "'s time is not equal to the expected value for TimeDistMatrix"; - } + timedist_matrix.SourceToTarget(request, reader, mode_costing, sif::TravelMode::kDrive, 400000.0); + json_res = tyr::serializeMatrix(request); + algo = "timedistancematrix"; + check_osrm_response(json_res, algo); } const auto test_request_partial = R"({ @@ -369,28 +416,25 @@ const auto test_request_partial = R"({ })"; TEST(Matrix, partial_matrix) { - loki_worker_t loki_worker(config); + loki_worker_t loki_worker(cfg); Api request; ParseApi(test_request_partial, Options::sources_to_targets, request); loki_worker.matrix(request); thor_worker_t::adjust_scores(*request.mutable_options()); - GraphReader reader(config.get_child("mjolnir")); + GraphReader reader(cfg.get_child("mjolnir")); sif::mode_costing_t mode_costing; mode_costing[0] = CreateSimpleCost(request.options().costings().find(request.options().costing_type())->second); TimeDistanceMatrix timedist_matrix; - std::vector results = - timedist_matrix.SourceToTarget(*request.mutable_options()->mutable_sources(), - *request.mutable_options()->mutable_targets(), reader, - mode_costing, sif::TravelMode::kDrive, 400000.0, - request.options().matrix_locations()); + timedist_matrix.SourceToTarget(request, reader, mode_costing, sif::TravelMode::kDrive, 400000.0); + auto& matrix = request.matrix(); uint32_t found = 0; - for (uint32_t i = 0; i < results.size(); ++i) { - if (results[i].dist > 0) { + for (int i = 0; i < matrix.times().size(); ++i) { + if (matrix.distances()[i] > 0) { ++found; } } @@ -412,7 +456,7 @@ const auto test_matrix_default = R"({ })"; TEST(Matrix, default_matrix) { - tyr::actor_t actor(config, true); + tyr::actor_t actor(cfg, true); auto response = actor.matrix(test_matrix_default); @@ -440,7 +484,7 @@ TEST(Matrix, default_matrix) { // first values in the object EXPECT_DOUBLE_EQ(json["sources_to_targets"].GetArray()[0][0].GetObject()["distance"].GetDouble(), 5.88); - EXPECT_EQ(json["sources_to_targets"].GetArray()[0][0].GetObject()["time"].GetInt64(), 474); + EXPECT_EQ(json["sources_to_targets"].GetArray()[0][0].GetObject()["time"].GetInt64(), 473); EXPECT_EQ(json["sources_to_targets"].GetArray()[0][0].GetObject()["to_index"].GetInt64(), 0); EXPECT_EQ(json["sources_to_targets"].GetArray()[0][0].GetObject()["from_index"].GetInt64(), 0); @@ -464,7 +508,7 @@ const auto test_matrix_verbose_false = R"({ })"; TEST(Matrix, slim_matrix) { - tyr::actor_t actor(config, true); + tyr::actor_t actor(cfg, true); auto response = actor.matrix(test_matrix_verbose_false); @@ -492,7 +536,7 @@ TEST(Matrix, slim_matrix) { EXPECT_DOUBLE_EQ(json["sources_to_targets"].GetObject()["distances"][0][0].GetDouble(), 5.88); // first value of "durations" array - EXPECT_EQ(json["sources_to_targets"].GetObject()["durations"][0][0].GetInt64(), 474); + EXPECT_EQ(json["sources_to_targets"].GetObject()["durations"][0][0].GetInt64(), 473); EXPECT_FALSE(json.HasMember("sources")); EXPECT_FALSE(json.HasMember("targets")); diff --git a/test/matrix_bss.cc b/test/matrix_bss.cc index 579cc9cffb..fdb974eb80 100644 --- a/test/matrix_bss.cc +++ b/test/matrix_bss.cc @@ -14,7 +14,7 @@ #include "sif/costfactory.h" #include "sif/dynamiccost.h" -#include "thor/matrix_common.h" +#include "thor/matrixalgorithm.h" #include "thor/timedistancebssmatrix.h" using namespace valhalla; @@ -34,7 +34,7 @@ namespace { // of the existing way on which the bike share sation is projected. It would be advisable to not set // radius to 0 so that the algorithm will choose the best projection. Otherwise, the location may be // projected uniquely on the bss_connection. -const auto config = +const auto cfg = test::make_config("test/data/paris_bss_tiles", {{"loki.service_defaults.radius", "10"}}); } // namespace @@ -114,10 +114,8 @@ class MatrixBssTest : public ::testing::Test { ParseApi(make_matrix_request(sources, targets), Options::sources_to_targets, matrix_request); loki_worker.matrix(matrix_request); - auto matrix_results = - timedist_matrix_bss.SourceToTarget(matrix_request.options().sources(), - matrix_request.options().targets(), reader, mode_costing, - sif::TravelMode::kPedestrian, 400000.0); + timedist_matrix_bss.SourceToTarget(matrix_request, reader, mode_costing, + sif::TravelMode::kPedestrian, 400000.0); auto s_size = sources.size(); auto t_size = targets.size(); @@ -140,8 +138,8 @@ class MatrixBssTest : public ::testing::Test { int route_length = legs.begin()->summary().length() * 1000; size_t m_result_idx = i * t_size + j; - int matrix_time = matrix_results[m_result_idx].time; - int matrix_length = matrix_results[m_result_idx].dist; + int matrix_time = matrix_request.matrix().times()[m_result_idx]; + int matrix_length = matrix_request.matrix().distances()[m_result_idx]; EXPECT_NEAR(matrix_time, route_time, kTimeThreshold); EXPECT_NEAR(matrix_length, route_length, route_length * kDistancePercentThreshold); @@ -150,11 +148,11 @@ class MatrixBssTest : public ::testing::Test { } private: - loki_worker_t loki_worker{config}; - thor_worker_t thor_worker{config}; - odin_worker_t odin_worker{config}; + loki_worker_t loki_worker{cfg}; + thor_worker_t thor_worker{cfg}; + odin_worker_t odin_worker{cfg}; - GraphReader reader{config.get_child("mjolnir")}; + GraphReader reader{cfg.get_child("mjolnir")}; mode_costing_t mode_costing; TimeDistanceBSSMatrix timedist_matrix_bss; }; diff --git a/test/minbb.cc b/test/minbb.cc index 10e9d2a41e..37721246a4 100644 --- a/test/minbb.cc +++ b/test/minbb.cc @@ -1,4 +1,3 @@ -#include #include #include diff --git a/test/multipoint_routes.cc b/test/multipoint_routes.cc index 3fc92dc24e..26771c8e22 100644 --- a/test/multipoint_routes.cc +++ b/test/multipoint_routes.cc @@ -1,6 +1,5 @@ #include "test.h" -#include #include #include @@ -10,7 +9,6 @@ #include "odin/worker.h" #include "sif/autocost.h" #include "thor/worker.h" -#include using namespace valhalla; using namespace valhalla::thor; diff --git a/test/names.cc b/test/names.cc index d74f622152..078512297c 100644 --- a/test/names.cc +++ b/test/names.cc @@ -2,7 +2,6 @@ #include "midgard/util.h" #include "mjolnir/osmway.h" #include "mjolnir/uniquenames.h" -#include #include "test.h" @@ -13,17 +12,17 @@ using namespace valhalla::baldr; namespace { -void TestKeyTypeValue(const std::string& pronunciation, - uint32_t expected_key, - PronunciationAlphabet expected_type, - const std::string& expected_value) { +void TestPronunciationKeyTypeValue(const std::string& pronunciation, + uint32_t expected_key, + PronunciationAlphabet expected_type, + const std::string& expected_value) { auto* p = const_cast(pronunciation.c_str()); size_t pos = 0; while (pos < strlen(p)) { const auto header = unaligned_read(p + pos); - pos += 3; + pos += kLinguisticHeaderSize; EXPECT_EQ(header.name_index_, expected_key); EXPECT_EQ(static_cast(header.phonetic_alphabet_), expected_type); EXPECT_EQ(std::string((p + pos), header.length_), expected_value); @@ -38,8 +37,7 @@ TEST(Names, NamesTest) { OSMWay w2{1234}; OSMWay w3{1234}; - OSMPronunciation pronunciation{}; - std::vector pronunciations; + std::vector linguistics; UniqueNames name_offset_map; std::string ref = "I 79 North"; @@ -50,14 +48,25 @@ TEST(Names, NamesTest) { w2.set_ref_index(name_offset_map.index("PA 43")); w3.set_ref_index(name_offset_map.index("PA 272")); - pronunciation.set_name_pronunciation_ipa_index(name_offset_map.index("test name ipa")); - pronunciation.set_name_pronunciation_nt_sampa_index(name_offset_map.index("test name sampa")); - pronunciation.set_name_pronunciation_katakana_index(name_offset_map.index("test name katakana")); - pronunciation.set_name_pronunciation_jeita_index(name_offset_map.index("test name jeita")); - pronunciation.set_ref_pronunciation_ipa_index(name_offset_map.index("test ref ipa")); - pronunciation.set_ref_pronunciation_nt_sampa_index(name_offset_map.index("test ref sampa")); - pronunciation.set_ref_pronunciation_katakana_index(name_offset_map.index("test ref katakana")); - pronunciation.set_ref_pronunciation_jeita_index(name_offset_map.index("test ref jeita")); + std::map, uint32_t> pronunciationMap; + const std::map, uint32_t> langMap; + + const uint8_t ipa = static_cast(PronunciationAlphabet::kIpa); + const uint8_t nt_sampa = static_cast(PronunciationAlphabet::kNtSampa); + const uint8_t katakana = static_cast(PronunciationAlphabet::kKatakana); + const uint8_t jeita = static_cast(PronunciationAlphabet::kJeita); + + uint8_t t = static_cast(OSMLinguistic::Type::kName); + pronunciationMap[std::make_pair(t, ipa)] = name_offset_map.index("test name ipa"); + pronunciationMap[std::make_pair(t, nt_sampa)] = name_offset_map.index("test name sampa"); + pronunciationMap[std::make_pair(t, katakana)] = name_offset_map.index("test name katakana"); + pronunciationMap[std::make_pair(t, jeita)] = name_offset_map.index("test name jeita"); + + t = static_cast(OSMLinguistic::Type::kRef); + pronunciationMap[std::make_pair(t, ipa)] = name_offset_map.index("test ref ipa"); + pronunciationMap[std::make_pair(t, nt_sampa)] = name_offset_map.index("test ref sampa"); + pronunciationMap[std::make_pair(t, katakana)] = name_offset_map.index("test ref katakana"); + pronunciationMap[std::make_pair(t, jeita)] = name_offset_map.index("test ref jeita"); w1.set_road_class(RoadClass::kMotorway); w2.set_road_class(RoadClass::kTrunk); @@ -65,142 +74,170 @@ TEST(Names, NamesTest) { uint16_t types; std::vector w1_names; - w1.GetNames(ref, name_offset_map, pronunciation, types, w1_names, pronunciations); + std::vector> default_languages; + + w1.GetNames(ref, name_offset_map, pronunciationMap, langMap, default_languages, w1.ref_index(), 0, + w1.name_index(), 0, w1.official_name_index(), 0, w1.alt_name_index(), 0, types, + w1_names, linguistics); // if road class = kTrunk or kMotorway, then ref comes first. ref from relation overrides // ref from name_offset_map EXPECT_EQ(w1_names.at(0), "I 79 North"); EXPECT_EQ(w1_names.at(1), "William Flynn Highway"); - TestKeyTypeValue(pronunciations.at(0), 0, PronunciationAlphabet::kIpa, "test ref ipa"); - TestKeyTypeValue(pronunciations.at(1), 0, PronunciationAlphabet::kNtSampa, "test ref sampa"); - TestKeyTypeValue(pronunciations.at(2), 0, PronunciationAlphabet::kXKatakana, "test ref katakana"); - TestKeyTypeValue(pronunciations.at(3), 0, PronunciationAlphabet::kXJeita, "test ref jeita"); - TestKeyTypeValue(pronunciations.at(4), 1, PronunciationAlphabet::kIpa, "test name ipa"); - TestKeyTypeValue(pronunciations.at(5), 1, PronunciationAlphabet::kNtSampa, "test name sampa"); - TestKeyTypeValue(pronunciations.at(6), 1, PronunciationAlphabet::kXKatakana, "test name katakana"); - TestKeyTypeValue(pronunciations.at(7), 1, PronunciationAlphabet::kXJeita, "test name jeita"); - - EXPECT_EQ(types, 1) << "relation ref failed. ref not in correct position."; + TestPronunciationKeyTypeValue(linguistics.at(0), 0, PronunciationAlphabet::kIpa, "test ref ipa"); + TestPronunciationKeyTypeValue(linguistics.at(1), 0, PronunciationAlphabet::kNtSampa, + "test ref sampa"); + TestPronunciationKeyTypeValue(linguistics.at(2), 0, PronunciationAlphabet::kKatakana, + "test ref katakana"); + TestPronunciationKeyTypeValue(linguistics.at(3), 0, PronunciationAlphabet::kJeita, + "test ref jeita"); + TestPronunciationKeyTypeValue(linguistics.at(4), 1, PronunciationAlphabet::kIpa, "test name ipa"); + TestPronunciationKeyTypeValue(linguistics.at(5), 1, PronunciationAlphabet::kNtSampa, + "test name sampa"); + TestPronunciationKeyTypeValue(linguistics.at(6), 1, PronunciationAlphabet::kKatakana, + "test name katakana"); + TestPronunciationKeyTypeValue(linguistics.at(7), 1, PronunciationAlphabet::kJeita, + "test name jeita"); std::vector w2_names; - pronunciations.clear(); - w2.GetNames("", name_offset_map, pronunciation, types, w2_names, pronunciations); + linguistics.clear(); + w2.GetNames("", name_offset_map, pronunciationMap, langMap, default_languages, w2.ref_index(), 0, + w2.name_index(), 0, w2.official_name_index(), 0, w2.alt_name_index(), 0, types, + w2_names, linguistics); // if road class = kTrunk or kMotorway, then ref comes first. use ref from name_offset_map EXPECT_EQ(w2_names.at(0), "PA 43"); EXPECT_EQ(w2_names.at(1), "Mon/Fayette Expressway"); - TestKeyTypeValue(pronunciations.at(0), 0, PronunciationAlphabet::kIpa, "test ref ipa"); - TestKeyTypeValue(pronunciations.at(1), 0, PronunciationAlphabet::kNtSampa, "test ref sampa"); - TestKeyTypeValue(pronunciations.at(2), 0, PronunciationAlphabet::kXKatakana, "test ref katakana"); - TestKeyTypeValue(pronunciations.at(3), 0, PronunciationAlphabet::kXJeita, "test ref jeita"); - TestKeyTypeValue(pronunciations.at(4), 1, PronunciationAlphabet::kIpa, "test name ipa"); - TestKeyTypeValue(pronunciations.at(5), 1, PronunciationAlphabet::kNtSampa, "test name sampa"); - TestKeyTypeValue(pronunciations.at(6), 1, PronunciationAlphabet::kXKatakana, "test name katakana"); - TestKeyTypeValue(pronunciations.at(7), 1, PronunciationAlphabet::kXJeita, "test name jeita"); + TestPronunciationKeyTypeValue(linguistics.at(0), 0, PronunciationAlphabet::kIpa, "test ref ipa"); + TestPronunciationKeyTypeValue(linguistics.at(1), 0, PronunciationAlphabet::kNtSampa, + "test ref sampa"); + TestPronunciationKeyTypeValue(linguistics.at(2), 0, PronunciationAlphabet::kKatakana, + "test ref katakana"); + TestPronunciationKeyTypeValue(linguistics.at(3), 0, PronunciationAlphabet::kJeita, + "test ref jeita"); + TestPronunciationKeyTypeValue(linguistics.at(4), 1, PronunciationAlphabet::kIpa, "test name ipa"); + TestPronunciationKeyTypeValue(linguistics.at(5), 1, PronunciationAlphabet::kNtSampa, + "test name sampa"); + TestPronunciationKeyTypeValue(linguistics.at(6), 1, PronunciationAlphabet::kKatakana, + "test name katakana"); + TestPronunciationKeyTypeValue(linguistics.at(7), 1, PronunciationAlphabet::kJeita, + "test name jeita"); EXPECT_EQ(types, 1) << "ref_map failed. ref not in correct position."; std::vector w3_names; - pronunciations.clear(); - w3.GetNames("", name_offset_map, pronunciation, types, w3_names, pronunciations); + linguistics.clear(); + w3.GetNames("", name_offset_map, pronunciationMap, langMap, default_languages, w3.ref_index(), 0, + w3.name_index(), 0, w3.official_name_index(), 0, w3.alt_name_index(), 0, types, + w3_names, linguistics); // if Road class < kTrunk, then name first then ref using ref from name_offset_map EXPECT_EQ(w3_names.at(0), "Lancaster Pike") << "Road class < kTrunk test failed."; EXPECT_EQ(w3_names.at(1), "PA 272") << "Road class < kTrunk test failed."; - TestKeyTypeValue(pronunciations.at(0), 0, PronunciationAlphabet::kIpa, "test name ipa"); - TestKeyTypeValue(pronunciations.at(1), 0, PronunciationAlphabet::kNtSampa, "test name sampa"); - TestKeyTypeValue(pronunciations.at(2), 0, PronunciationAlphabet::kXKatakana, "test name katakana"); - TestKeyTypeValue(pronunciations.at(3), 0, PronunciationAlphabet::kXJeita, "test name jeita"); - TestKeyTypeValue(pronunciations.at(4), 1, PronunciationAlphabet::kIpa, "test ref ipa"); - TestKeyTypeValue(pronunciations.at(5), 1, PronunciationAlphabet::kNtSampa, "test ref sampa"); - TestKeyTypeValue(pronunciations.at(6), 1, PronunciationAlphabet::kXKatakana, "test ref katakana"); - TestKeyTypeValue(pronunciations.at(7), 1, PronunciationAlphabet::kXJeita, "test ref jeita"); - + TestPronunciationKeyTypeValue(linguistics.at(0), 0, PronunciationAlphabet::kIpa, "test name ipa"); + TestPronunciationKeyTypeValue(linguistics.at(1), 0, PronunciationAlphabet::kNtSampa, + "test name sampa"); + TestPronunciationKeyTypeValue(linguistics.at(2), 0, PronunciationAlphabet::kKatakana, + "test name katakana"); + TestPronunciationKeyTypeValue(linguistics.at(3), 0, PronunciationAlphabet::kJeita, + "test name jeita"); + TestPronunciationKeyTypeValue(linguistics.at(4), 1, PronunciationAlphabet::kIpa, "test ref ipa"); + TestPronunciationKeyTypeValue(linguistics.at(5), 1, PronunciationAlphabet::kNtSampa, + "test ref sampa"); + TestPronunciationKeyTypeValue(linguistics.at(6), 1, PronunciationAlphabet::kKatakana, + "test ref katakana"); + TestPronunciationKeyTypeValue(linguistics.at(7), 1, PronunciationAlphabet::kJeita, + "test ref jeita"); EXPECT_EQ(types, 2) << "Road class < kTrunk test failed. ref not in correct position."; w3_names.clear(); - pronunciations.clear(); - w3.GetNames("PA 555", name_offset_map, pronunciation, types, w3_names, pronunciations); + linguistics.clear(); + w3.GetNames("PA 555", name_offset_map, pronunciationMap, langMap, default_languages, w3.ref_index(), + 0, w3.name_index(), 0, w3.official_name_index(), 0, w3.alt_name_index(), 0, types, + w3_names, linguistics); // if Road class < kTrunk, then name first then ref using ref from relations EXPECT_EQ(w3_names.at(0), "Lancaster Pike") << "ref from relations"; EXPECT_EQ(w3_names.at(1), "PA 555") << "ref from relations"; - TestKeyTypeValue(pronunciations.at(0), 0, PronunciationAlphabet::kIpa, "test name ipa"); - TestKeyTypeValue(pronunciations.at(1), 0, PronunciationAlphabet::kNtSampa, "test name sampa"); - TestKeyTypeValue(pronunciations.at(2), 0, PronunciationAlphabet::kXKatakana, "test name katakana"); - TestKeyTypeValue(pronunciations.at(3), 0, PronunciationAlphabet::kXJeita, "test name jeita"); - TestKeyTypeValue(pronunciations.at(4), 1, PronunciationAlphabet::kIpa, "test ref ipa"); - TestKeyTypeValue(pronunciations.at(5), 1, PronunciationAlphabet::kNtSampa, "test ref sampa"); - TestKeyTypeValue(pronunciations.at(6), 1, PronunciationAlphabet::kXKatakana, "test ref katakana"); - TestKeyTypeValue(pronunciations.at(7), 1, PronunciationAlphabet::kXJeita, "test ref jeita"); - + TestPronunciationKeyTypeValue(linguistics.at(0), 0, PronunciationAlphabet::kIpa, "test name ipa"); + TestPronunciationKeyTypeValue(linguistics.at(1), 0, PronunciationAlphabet::kNtSampa, + "test name sampa"); + TestPronunciationKeyTypeValue(linguistics.at(2), 0, PronunciationAlphabet::kKatakana, + "test name katakana"); + TestPronunciationKeyTypeValue(linguistics.at(3), 0, PronunciationAlphabet::kJeita, + "test name jeita"); + TestPronunciationKeyTypeValue(linguistics.at(4), 1, PronunciationAlphabet::kIpa, "test ref ipa"); + TestPronunciationKeyTypeValue(linguistics.at(5), 1, PronunciationAlphabet::kNtSampa, + "test ref sampa"); + TestPronunciationKeyTypeValue(linguistics.at(6), 1, PronunciationAlphabet::kKatakana, + "test ref katakana"); + TestPronunciationKeyTypeValue(linguistics.at(7), 1, PronunciationAlphabet::kJeita, + "test ref jeita"); EXPECT_EQ(types, 2) << "Road class < kTrunk test failed(ref from relations). ref not in correct position."; w3.set_alt_name_index(name_offset_map.index("Lanc Pike")); w3.set_official_name_index(name_offset_map.index("LP")); - w3.set_name_en_index(name_offset_map.index("LancP")); - - pronunciation.set_alt_name_pronunciation_ipa_index(name_offset_map.index("test alt name ipa")); - pronunciation.set_alt_name_pronunciation_nt_sampa_index( - name_offset_map.index("test alt name sampa")); - pronunciation.set_alt_name_pronunciation_katakana_index( - name_offset_map.index("test alt name katakana")); - pronunciation.set_alt_name_pronunciation_jeita_index(name_offset_map.index("test alt name jeita")); - pronunciation.set_official_name_pronunciation_ipa_index( - name_offset_map.index("test official name ipa")); - pronunciation.set_official_name_pronunciation_nt_sampa_index( - name_offset_map.index("test official name sampa")); - pronunciation.set_official_name_pronunciation_katakana_index( - name_offset_map.index("test official name katakana")); - pronunciation.set_official_name_pronunciation_jeita_index( - name_offset_map.index("test official name jeita")); - pronunciation.set_name_en_pronunciation_ipa_index(name_offset_map.index("test name en ipa")); - pronunciation.set_name_en_pronunciation_nt_sampa_index(name_offset_map.index("test name en sampa")); - pronunciation.set_name_en_pronunciation_katakana_index( - name_offset_map.index("test name en katakana")); - pronunciation.set_name_en_pronunciation_jeita_index(name_offset_map.index("test name en jeita")); + + t = static_cast(OSMLinguistic::Type::kAltName); + pronunciationMap[std::make_pair(t, ipa)] = name_offset_map.index("test alt name ipa"); + pronunciationMap[std::make_pair(t, nt_sampa)] = name_offset_map.index("test alt name sampa"); + pronunciationMap[std::make_pair(t, katakana)] = name_offset_map.index("test alt name katakana"); + pronunciationMap[std::make_pair(t, jeita)] = name_offset_map.index("test alt name jeita"); + + t = static_cast(OSMLinguistic::Type::kOfficialName); + pronunciationMap[std::make_pair(t, ipa)] = name_offset_map.index("test official name ipa"); + pronunciationMap[std::make_pair(t, nt_sampa)] = name_offset_map.index("test official name sampa"); + pronunciationMap[std::make_pair(t, katakana)] = + name_offset_map.index("test official name katakana"); + pronunciationMap[std::make_pair(t, jeita)] = name_offset_map.index("test official name jeita"); w3_names.clear(); - pronunciations.clear(); - w3.GetNames("", name_offset_map, pronunciation, types, w3_names, pronunciations); + linguistics.clear(); + w3.GetNames("", name_offset_map, pronunciationMap, langMap, default_languages, w3.ref_index(), 0, + w3.name_index(), 0, w3.official_name_index(), 0, w3.alt_name_index(), 0, types, + w3_names, linguistics); EXPECT_EQ(types, 2) << "all other names test failed. ref not in correct position."; // all other names should be last. EXPECT_EQ(w3_names.at(2), "Lanc Pike") << "Alt name failed."; EXPECT_EQ(w3_names.at(3), "LP") << "official name failed."; - EXPECT_EQ(w3_names.at(4), "LancP") << "name en failed."; - - TestKeyTypeValue(pronunciations.at(0), 0, PronunciationAlphabet::kIpa, "test name ipa"); - TestKeyTypeValue(pronunciations.at(1), 0, PronunciationAlphabet::kNtSampa, "test name sampa"); - TestKeyTypeValue(pronunciations.at(2), 0, PronunciationAlphabet::kXKatakana, "test name katakana"); - TestKeyTypeValue(pronunciations.at(3), 0, PronunciationAlphabet::kXJeita, "test name jeita"); - TestKeyTypeValue(pronunciations.at(4), 1, PronunciationAlphabet::kIpa, "test ref ipa"); - TestKeyTypeValue(pronunciations.at(5), 1, PronunciationAlphabet::kNtSampa, "test ref sampa"); - TestKeyTypeValue(pronunciations.at(6), 1, PronunciationAlphabet::kXKatakana, "test ref katakana"); - TestKeyTypeValue(pronunciations.at(7), 1, PronunciationAlphabet::kXJeita, "test ref jeita"); - TestKeyTypeValue(pronunciations.at(8), 2, PronunciationAlphabet::kIpa, "test alt name ipa"); - TestKeyTypeValue(pronunciations.at(9), 2, PronunciationAlphabet::kNtSampa, "test alt name sampa"); - TestKeyTypeValue(pronunciations.at(10), 2, PronunciationAlphabet::kXKatakana, - "test alt name katakana"); - TestKeyTypeValue(pronunciations.at(11), 2, PronunciationAlphabet::kXJeita, "test alt name jeita"); - TestKeyTypeValue(pronunciations.at(12), 3, PronunciationAlphabet::kIpa, "test official name ipa"); - TestKeyTypeValue(pronunciations.at(13), 3, PronunciationAlphabet::kNtSampa, - "test official name sampa"); - TestKeyTypeValue(pronunciations.at(14), 3, PronunciationAlphabet::kXKatakana, - "test official name katakana"); - TestKeyTypeValue(pronunciations.at(15), 3, PronunciationAlphabet::kXJeita, - "test official name jeita"); - TestKeyTypeValue(pronunciations.at(16), 4, PronunciationAlphabet::kIpa, "test name en ipa"); - TestKeyTypeValue(pronunciations.at(17), 4, PronunciationAlphabet::kNtSampa, "test name en sampa"); - TestKeyTypeValue(pronunciations.at(18), 4, PronunciationAlphabet::kXKatakana, - "test name en katakana"); - TestKeyTypeValue(pronunciations.at(19), 4, PronunciationAlphabet::kXJeita, "test name en jeita"); + + TestPronunciationKeyTypeValue(linguistics.at(0), 0, PronunciationAlphabet::kIpa, "test name ipa"); + TestPronunciationKeyTypeValue(linguistics.at(1), 0, PronunciationAlphabet::kNtSampa, + "test name sampa"); + TestPronunciationKeyTypeValue(linguistics.at(2), 0, PronunciationAlphabet::kKatakana, + "test name katakana"); + TestPronunciationKeyTypeValue(linguistics.at(3), 0, PronunciationAlphabet::kJeita, + "test name jeita"); + TestPronunciationKeyTypeValue(linguistics.at(4), 1, PronunciationAlphabet::kIpa, "test ref ipa"); + TestPronunciationKeyTypeValue(linguistics.at(5), 1, PronunciationAlphabet::kNtSampa, + "test ref sampa"); + TestPronunciationKeyTypeValue(linguistics.at(6), 1, PronunciationAlphabet::kKatakana, + "test ref katakana"); + TestPronunciationKeyTypeValue(linguistics.at(7), 1, PronunciationAlphabet::kJeita, + "test ref jeita"); + TestPronunciationKeyTypeValue(linguistics.at(8), 2, PronunciationAlphabet::kIpa, + "test alt name ipa"); + TestPronunciationKeyTypeValue(linguistics.at(9), 2, PronunciationAlphabet::kNtSampa, + "test alt name sampa"); + TestPronunciationKeyTypeValue(linguistics.at(10), 2, PronunciationAlphabet::kKatakana, + "test alt name katakana"); + TestPronunciationKeyTypeValue(linguistics.at(11), 2, PronunciationAlphabet::kJeita, + "test alt name jeita"); + TestPronunciationKeyTypeValue(linguistics.at(12), 3, PronunciationAlphabet::kIpa, + "test official name ipa"); + TestPronunciationKeyTypeValue(linguistics.at(13), 3, PronunciationAlphabet::kNtSampa, + "test official name sampa"); + TestPronunciationKeyTypeValue(linguistics.at(14), 3, PronunciationAlphabet::kKatakana, + "test official name katakana"); + TestPronunciationKeyTypeValue(linguistics.at(15), 3, PronunciationAlphabet::kJeita, + "test official name jeita"); } TEST(Names, TaggedNamesTest) { @@ -208,8 +245,10 @@ TEST(Names, TaggedNamesTest) { OSMWay w1{1234}; OSMWay w2{1234}; - OSMPronunciation pronunciation1{}, pronunciation2{}; - std::vector pronunciations; + std::map, uint32_t> pronunciationMap; + const std::map, uint32_t> langMap; + std::vector linguistics; + std::vector> default_languages; UniqueNames name_offset_map; @@ -218,44 +257,54 @@ TEST(Names, TaggedNamesTest) { w1.set_road_class(RoadClass::kMotorway); w2.set_road_class(RoadClass::kMotorway); - pronunciation1.set_tunnel_name_pronunciation_ipa_index(name_offset_map.index("tɛd ˈwɪljəmz ˈtʌnl")); - pronunciation1.set_tunnel_name_pronunciation_nt_sampa_index( - name_offset_map.index("tEd wIly@mz t@n@l")); - pronunciation1.set_tunnel_name_pronunciation_katakana_index( - name_offset_map.index("テッド ウィリャムズ タネル")); - pronunciation1.set_tunnel_name_pronunciation_jeita_index( - name_offset_map.index("チバダ'イガ&ク% セーモンマ'エ.")); - - pronunciation2.set_tunnel_name_pronunciation_ipa_index(name_offset_map.index("fɔːt McHenry ˈtʌnl")); - pronunciation2.set_tunnel_name_pronunciation_nt_sampa_index( - name_offset_map.index("fOrt m@kEnri t@n@l")); - pronunciation2.set_tunnel_name_pronunciation_katakana_index( - name_offset_map.index("フォート ムケンリー タネル")); - pronunciation2.set_tunnel_name_pronunciation_jeita_index( - name_offset_map.index("チバダ'イガ&ク% セーモンマ'エ.")); + const uint8_t ipa = static_cast(PronunciationAlphabet::kIpa); + const uint8_t nt_sampa = static_cast(PronunciationAlphabet::kNtSampa); + const uint8_t katakana = static_cast(PronunciationAlphabet::kKatakana); + const uint8_t jeita = static_cast(PronunciationAlphabet::kJeita); + + uint8_t t = static_cast(OSMLinguistic::Type::kTunnelName); + pronunciationMap[std::make_pair(t, ipa)] = name_offset_map.index("tɛd ˈwɪljəmz ˈtʌnl"); + pronunciationMap[std::make_pair(t, nt_sampa)] = name_offset_map.index("tEd wIly@mz t@n@l"); + pronunciationMap[std::make_pair(t, katakana)] = name_offset_map.index("テッド ウィリャムズ タネル"); + pronunciationMap[std::make_pair(t, jeita)] = + name_offset_map.index("チバダ'イガ&ク% セーモンマ'エ."); std::vector w1_tagged_names; - w1.GetTaggedValues(name_offset_map, pronunciation1, 0, w1_tagged_names, pronunciations); + w1.GetTaggedValues(name_offset_map, pronunciationMap, langMap, default_languages, + w1.tunnel_name_index(), 0, 0, w1_tagged_names, linguistics); EXPECT_EQ(w1_tagged_names.at(0), "1Ted Williams Tunnel"); - TestKeyTypeValue(pronunciations.at(0), 0, PronunciationAlphabet::kIpa, "tɛd ˈwɪljəmz ˈtʌnl"); - TestKeyTypeValue(pronunciations.at(1), 0, PronunciationAlphabet::kNtSampa, "tEd wIly@mz t@n@l"); - TestKeyTypeValue(pronunciations.at(2), 0, PronunciationAlphabet::kXKatakana, - "テッド ウィリャムズ タネル"); - TestKeyTypeValue(pronunciations.at(3), 0, PronunciationAlphabet::kXJeita, - "チバダ'イガ&ク% セーモンマ'エ."); - - pronunciations.clear(); + TestPronunciationKeyTypeValue(linguistics.at(0), 0, PronunciationAlphabet::kIpa, + "tɛd ˈwɪljəmz ˈtʌnl"); + TestPronunciationKeyTypeValue(linguistics.at(1), 0, PronunciationAlphabet::kNtSampa, + "tEd wIly@mz t@n@l"); + TestPronunciationKeyTypeValue(linguistics.at(2), 0, PronunciationAlphabet::kKatakana, + "テッド ウィリャムズ タネル"); + TestPronunciationKeyTypeValue(linguistics.at(3), 0, PronunciationAlphabet::kJeita, + "チバダ'イガ&ク% セーモンマ'エ."); + + linguistics.clear(); + pronunciationMap.clear(); + pronunciationMap[std::make_pair(t, ipa)] = name_offset_map.index("fɔːt McHenry ˈtʌnl"); + pronunciationMap[std::make_pair(t, nt_sampa)] = name_offset_map.index("fOrt m@kEnri t@n@l"); + pronunciationMap[std::make_pair(t, katakana)] = name_offset_map.index("フォート ムケンリー タネル"); + pronunciationMap[std::make_pair(t, jeita)] = + name_offset_map.index("チバダ'イガ&ク% セーモンマ'エ."); + std::vector w2_tagged_names; - w2_tagged_names.emplace_back("test name"); // testing pronunciation index + w2_tagged_names.emplace_back("test name"); + // testing pronunciation index - w2.GetTaggedValues(name_offset_map, pronunciation2, 0, w2_tagged_names, pronunciations); + w2.GetTaggedValues(name_offset_map, pronunciationMap, langMap, default_languages, + w2.tunnel_name_index(), 0, 0, w2_tagged_names, linguistics); EXPECT_EQ(w2_tagged_names.at(1), "1Fort McHenry Tunnel"); - TestKeyTypeValue(pronunciations.at(0), 1, PronunciationAlphabet::kIpa, "fɔːt McHenry ˈtʌnl"); - TestKeyTypeValue(pronunciations.at(1), 1, PronunciationAlphabet::kNtSampa, "fOrt m@kEnri t@n@l"); - TestKeyTypeValue(pronunciations.at(2), 1, PronunciationAlphabet::kXKatakana, - "フォート ムケンリー タネル"); - TestKeyTypeValue(pronunciations.at(3), 1, PronunciationAlphabet::kXJeita, - "チバダ'イガ&ク% セーモンマ'エ."); + TestPronunciationKeyTypeValue(linguistics.at(0), 1, PronunciationAlphabet::kIpa, + "fɔːt McHenry ˈtʌnl"); + TestPronunciationKeyTypeValue(linguistics.at(1), 1, PronunciationAlphabet::kNtSampa, + "fOrt m@kEnri t@n@l"); + TestPronunciationKeyTypeValue(linguistics.at(2), 1, PronunciationAlphabet::kKatakana, + "フォート ムケンリー タネル"); + TestPronunciationKeyTypeValue(linguistics.at(3), 1, PronunciationAlphabet::kJeita, + "チバダ'イガ&ク% セーモンマ'エ."); } } // namespace diff --git a/test/narrative_dictionary.cc b/test/narrative_dictionary.cc index fcfca5d6ae..1f6c716f26 100644 --- a/test/narrative_dictionary.cc +++ b/test/narrative_dictionary.cc @@ -13,9 +13,9 @@ using namespace valhalla::odin; namespace { // Expected strings -const std::vector kExpectedEmptyStreetNameLabels = {"the walkway", "the cycleway", - "the mountain bike trail", - "the crosswalk"}; +const std::vector kExpectedEmptyStreetNameLabels = + {"the walkway", "the cycleway", "the mountain bike trail", "the crosswalk", "the stairs", + "the bridge", "the tunnel"}; const std::vector kExpectedCardinalDirections = {"north", "northeast", "east", "southeast", "south", "southwest", "west", "northwest"}; diff --git a/test/narrativebuilder.cc b/test/narrativebuilder.cc index c8879bbba1..66b2d51fe6 100644 --- a/test/narrativebuilder.cc +++ b/test/narrativebuilder.cc @@ -1,5 +1,4 @@ #include -#include #include "baldr/verbal_text_formatter_factory.h" @@ -243,7 +242,7 @@ void PopulateTransitInfo(valhalla::odin::TransitRouteInfo* transit_info, transit_info->operator_url = operator_url; } -// TOOD - remove is_parent_stop +// TODO - remove is_parent_stop // TODO - add station_onestop_id and station_name TransitPlatformInfo GetTransitPlatformInfo(TransitPlatformInfo_Type type, const std::string& onestop_id, diff --git a/test/node_search.cc b/test/node_search.cc index ea2466906f..f10d217ff7 100644 --- a/test/node_search.cc +++ b/test/node_search.cc @@ -1,9 +1,10 @@ #include "loki/node_search.h" + #include +#include #include "baldr/rapidjson_utils.h" #include -#include #include "baldr/graphid.h" #include "baldr/graphreader.h" @@ -17,7 +18,6 @@ namespace vm = valhalla::midgard; namespace vb = valhalla::baldr; -#include "filesystem.h" #include "mjolnir/directededgebuilder.h" #include "mjolnir/graphtilebuilder.h" #include "mjolnir/graphvalidator.h" @@ -41,7 +41,7 @@ struct graph_writer { void write_tiles(); private: - const uint8_t m_level; + [[maybe_unused]] const uint8_t m_level; std::unordered_map> m_builders; }; @@ -272,8 +272,8 @@ void graph_builder::write_tiles(uint8_t level) const { void make_tile() { // make sure that all the old tiles are gone before trying to make new ones. - if (filesystem::is_directory(test_tile_dir)) { - filesystem::remove_all(test_tile_dir); + if (std::filesystem::is_directory(test_tile_dir)) { + std::filesystem::remove_all(test_tile_dir); } graph_builder builder; diff --git a/test/nodeinfo.cc b/test/nodeinfo.cc index 1c98465594..e485cb388f 100644 --- a/test/nodeinfo.cc +++ b/test/nodeinfo.cc @@ -84,6 +84,28 @@ TEST(NodeInfo, WriteRead) { EXPECT_EQ(nodeinfo.local_driveability(1), Traversability::kBackward); } +// Test elevation +TEST(NodeInfo, Elevation) { + // Test elevation at 0 + NodeInfo node; + node.set_elevation(0); + EXPECT_LE(std::abs(node.elevation()), 0.25f); + + // Elevation < -500 is set to -500 + node.set_elevation(-700.0f); + EXPECT_LE(std::abs((node.elevation() - -500.0f)), 0.25f); + + node.set_elevation(700.0f); + EXPECT_LE(std::abs((node.elevation() - 700.0f)), 0.25f); + + node.set_elevation(1426.511963f); + EXPECT_LE(std::abs((node.elevation() - 1426.511963f)), 0.25f); + + // Highest road elevation is ~5600m, test at 6000m to be safe + node.set_elevation(6000.0f); + EXPECT_LE(std::abs((node.elevation() - 6000.0f)), 0.25f); +} + } // namespace int main(int argc, char* argv[]) { diff --git a/test/parse_request.cc b/test/parse_request.cc index e44b54bd62..0d3e2d9d6a 100644 --- a/test/parse_request.cc +++ b/test/parse_request.cc @@ -254,8 +254,8 @@ std::string get_request_str(const std::string& grandparent_key, const std::string& parent_key, const std::string& key, const bool specified_value) { - return R"({")" + grandparent_key + R"(":{")" + parent_key + R"(":{")" + key + R"(":)" + - std::string(specified_value ? "true" : "false") + R"(}}})"; + return R"({"costing":")" + parent_key + R"(",")" + grandparent_key + R"(":{")" + parent_key + + R"(":{")" + key + R"(":)" + std::string(specified_value ? "true" : "false") + R"(}}})"; } std::string get_request_str(const std::string& key, const float expected_value) { @@ -271,8 +271,8 @@ std::string get_request_str(const std::string& grandparent_key, const std::string& parent_key, const std::string& key, const float specified_value) { - return R"({")" + grandparent_key + R"(":{")" + parent_key + R"(":{")" + key + R"(":)" + - std::to_string(specified_value) + R"(}}})"; + return R"({"costing":")" + parent_key + R"(",")" + grandparent_key + R"(":{")" + parent_key + + R"(":{")" + key + R"(":)" + std::to_string(specified_value) + R"(}}})"; } std::string get_request_str(const std::string& grandparent_key, @@ -281,16 +281,17 @@ std::string get_request_str(const std::string& grandparent_key, const std::string& sibling_value, const std::string& key, const float specified_value) { - return R"({")" + grandparent_key + R"(":{")" + parent_key + R"(":{")" + sibling_key + R"(":")" + - sibling_value + R"(",")" + key + R"(":)" + std::to_string(specified_value) + R"(}}})"; + return R"({"costing":")" + parent_key + R"(",")" + grandparent_key + R"(":{")" + parent_key + + R"(":{")" + sibling_key + R"(":")" + sibling_value + R"(",")" + key + R"(":)" + + std::to_string(specified_value) + R"(}}})"; } std::string get_request_str(const std::string& grandparent_key, const std::string& parent_key, const std::string& key, const uint32_t specified_value) { - return R"({")" + grandparent_key + R"(":{")" + parent_key + R"(":{")" + key + R"(":)" + - std::to_string(specified_value) + R"(}}})"; + return R"({"costing":")" + parent_key + R"(",")" + grandparent_key + R"(":{")" + parent_key + + R"(":{")" + key + R"(":)" + std::to_string(specified_value) + R"(}}})"; } std::string get_request_str(const std::string& grandparent_key, @@ -299,16 +300,17 @@ std::string get_request_str(const std::string& grandparent_key, const std::string& sibling_value, const std::string& key, const uint32_t specified_value) { - return R"({")" + grandparent_key + R"(":{")" + parent_key + R"(":{")" + sibling_key + R"(":")" + - sibling_value + R"(",")" + key + R"(":)" + std::to_string(specified_value) + R"(}}})"; + return R"({"costing":")" + parent_key + R"(",")" + grandparent_key + R"(":{")" + parent_key + + R"(":{")" + sibling_key + R"(":")" + sibling_value + R"(",")" + key + R"(":)" + + std::to_string(specified_value) + R"(}}})"; } std::string get_request_str(const std::string& grandparent_key, const std::string& parent_key, const std::string& key, const std::string& specified_value) { - return R"({")" + grandparent_key + R"(":{")" + parent_key + R"(":{")" + key + R"(":")" + - specified_value + R"("}}})"; + return R"({"costing":")" + parent_key + R"(",")" + grandparent_key + R"(":{")" + parent_key + + R"(":{")" + key + R"(":")" + specified_value + R"("}}})"; } std::string get_request_str(const std::string& key, const uint32_t expected_value) { @@ -359,8 +361,9 @@ std::string get_filter_request_str(const std::string& costing, const std::string& filter_type, const valhalla::FilterAction filter_action, const std::vector& filter_ids) { - return R"({"costing_options":{")" + costing + R"(":{"filters":{")" + filter_type + R"(":{)" + - get_kv_str("action", filter_action) + R"(,)" + get_kv_str("ids", filter_ids) + R"(}}}}})"; + return R"({"costing":")" + costing + R"(",)" + R"("costing_options":{")" + costing + + R"(":{"filters":{")" + filter_type + R"(":{)" + get_kv_str("action", filter_action) + + R"(,)" + get_kv_str("ids", filter_ids) + R"(}}}}})"; } Api get_request(const std::string& request_str, const Options::Action action) { @@ -1642,8 +1645,8 @@ void test_closure_factor_parsing(const Costing::Type costing_type, // Create costing options (reference: /test/astar.cc) void create_costing_options(Options& options, Costing::Type type) { const rapidjson::Document doc; - sif::ParseCosting(doc, "/costing_options", options); options.set_costing_type(type); + sif::ParseCosting(doc, "/costing_options", options); } // Set disable_hierarchy_pruning to true in costing options @@ -1779,11 +1782,6 @@ TEST(ParseRequest, test_default_base_cost_options) { TEST(ParseRequest, test_transport_type) { std::string transport_type_key = "type"; - std::string transport_type_value = "car"; - for (auto costing : get_base_auto_costing_list()) { - test_transport_type_parsing(costing, transport_type_key, transport_type_value, - transport_type_value); - } Costing::Type costing = Costing::pedestrian; for (const auto& transport_type_value : {"foot", "wheelchair"}) { diff --git a/test/pointtileindex.cc b/test/pointtileindex.cc index f4bb7d0600..b8539aed51 100644 --- a/test/pointtileindex.cc +++ b/test/pointtileindex.cc @@ -113,7 +113,7 @@ TEST(PointTileIndex, Intermediate) { { std::vector points_enhanced(points); size_t circle_pt_idx = points_enhanced.size(); - std::unordered_set circle_pt_indicies; + std::unordered_set circle_pt_indices; // add a bunch of points tightly circled around the origin int num_circle_pts = 100; @@ -122,7 +122,7 @@ TEST(PointTileIndex, Intermediate) { double x = std::cos(radians), y = std::sin(radians); constexpr double len = 1e-7; points_enhanced.emplace_back(PointLL{len * x, len * y}); - circle_pt_indicies.insert(circle_pt_idx++); + circle_pt_indices.insert(circle_pt_idx++); } // a tighter tiling width this time @@ -134,8 +134,8 @@ TEST(PointTileIndex, Intermediate) { near_pts = index.get_points_near(origin_pt); EXPECT_EQ(near_pts.size(), num_circle_pts + 1); - circle_pt_indicies.insert(origin_idx); - EXPECT_EQ(near_pts, circle_pt_indicies); + circle_pt_indices.insert(origin_idx); + EXPECT_EQ(near_pts, circle_pt_indices); } //-------------------------------------------------------- diff --git a/test/polyline2.cc b/test/polyline2.cc index 36d9981db2..f53d88e8d3 100644 --- a/test/polyline2.cc +++ b/test/polyline2.cc @@ -15,7 +15,7 @@ namespace { template void TryGeneralizeAndLength(Polyline2>& pl, const float& gen, const float& res) { - uint32_t size = pl.Generalize(gen); + pl.Generalize(gen); std::vector> pts = pl.pts(); diff --git a/test/predictedspeeds.cc b/test/predictedspeeds.cc index c77c889d4f..0fa4ae3229 100644 --- a/test/predictedspeeds.cc +++ b/test/predictedspeeds.cc @@ -42,14 +42,14 @@ void try_free_flow_speed(const std::string& encoded_str, EXPECT_EQ(constrained_flow_speed, exp_constrained_flow); } -TEST(PredicteSpeeds, test_free_flow_speed) { +TEST(PredictedSpeeds, test_free_flow_speed) { try_free_flow_speed("AAie", 8, 158); // Add additional cases below try_free_flow_speed("AACe", 0, 158); } -TEST(PredicteSpeeds, test_decoding) { +TEST(PredictedSpeeds, test_decoding) { // base64 encoded string std::string encoded_speed_string = "AQXFAAkABAAhAAz/+//bABn/3wAMABsAEQAF//gAAAAdABQAEv/wABf//gAsAAkAKgAAACj/+gBDAAQAbAALAQQAKv63AAD/mwAM/87/7P/TAAX/2P/1//P//f/sAAn/z//xAA7//P/y//z/8v/x////+wAMABX/+f/6AA4AGQAEABX/9//vAAf/8gAfAAb/9AAFABH//P/0ABQABv/2////4//7//0AE//+//n/5AATAAcAAQAL/+v//P/3ABMAAAAU//L/+v/8AAAAEP/3AAsABQAE/9f/7AABAAwAAQAGABP//QAJ/+4AB//gABUAAf/+AAv/6P/oABP//gAAABX/5f/5AAT//v/5AAgABv/3AB7/6gAdAAL/+P/r//sACwADAAT/9wAE//MACAAK//cACv/4//sABAAA//j//P/7//H/9v/y//wACwAHAAYABv/4AAL/+QAKAB7//wAHABX/8wAQ/+wAFAAL/+7//AAIAAgADf/9AAz/4gAQ//X/9//+//j/9wAEAAz//wADAAc="; @@ -188,7 +188,7 @@ TEST(PredicteSpeeds, test_decoding) { * where we see a negative speed we should trace it back to the input speeds and change the * encoded speed string here to see if the issue is valid. */ -TEST(PredicteSpeeds, test_negative_speeds) { +TEST(PredictedSpeeds, test_negative_speeds) { // base64 encoded string std::string encoded_speed_string = "AQRu//UAEAAC/+4AA//6//gAAwAFAA//9wAHAAH/4AAd/+wACwAH//0AGQAYAA7//wANAAL/9//mAAUACgATAAb/8v/2//8AC//1ABMAAAAGABX/9//0//0AAAAQAAIAAv/6////9gAJAAcACf/zAAQAAwAC//oACf/2//sADQAVABD/+QADAAcACf/2//gABwAHAAAABv/9AAf/+QAM//kAEAAE//r//wAMAAD/9AAN//D/7QAK//EAE//7AAkAAQAF//f/+AAB//z/6f/y//MAAP/6ABL//AATABX//wAFAAMAGv/2AAf//wAI//sACv/5AAb/8gAOAAYADv/5AAMACP////T/7gAH//P/+f/9//n/9f/0//0AAwAP//3/8gAA//8ACv////gAAgAHAAP//QALAAcAFAAA//8ABP/vAAIAEAAM/+3/9QAC//j//v/tABj/+wAA//sAC//6//0ABwAAAAoABgAMAAb/+P/3AAX/9//7//0ADP/sAAwAB//v/+3//wAMABAACgAF//o="; diff --git a/test/predictive_traffic.cc b/test/predictive_traffic.cc index d3f0fd10fc..e1ff13bae7 100644 --- a/test/predictive_traffic.cc +++ b/test/predictive_traffic.cc @@ -1,6 +1,5 @@ #include "test.h" -#include #include #include diff --git a/test/queue.cc b/test/queue.cc index 1d153e2411..5a5e1ab842 100644 --- a/test/queue.cc +++ b/test/queue.cc @@ -74,7 +74,7 @@ TEST(Queue, SimpleTestQueue) { queue.push(Label(1, 1)); EXPECT_EQ(queue.top(), Label(1, 1)) << "top should be changed now"; - EXPECT_EQ(queue.size(), 2) << "the old lable 1 should be replaced so size should be 2"; + EXPECT_EQ(queue.size(), 2) << "the old label 1 should be replaced so size should be 2"; queue.pop(); EXPECT_EQ(queue.top(), Label(2, 2)) << "<2, 2> should be popped"; diff --git a/test/reach.cc b/test/reach.cc index 0f69e1db56..c8b0fded5b 100644 --- a/test/reach.cc +++ b/test/reach.cc @@ -10,7 +10,6 @@ #include "sif/dynamiccost.h" #include -#include using namespace valhalla; using namespace valhalla::midgard; @@ -64,17 +63,17 @@ TEST(Reach, check_all_reach) { // if you have non zero reach but you dont have access, something is wrong EXPECT_FALSE((reach.outbound > 0 || reach.inbound > 0) && !(edge->forwardaccess() & kAutoAccess) && !(edge->reverseaccess() & kAutoAccess)) - << "This edge should have 0 reach as its not accessable: " + std::to_string(edge_id.value) + + << "This edge should have 0 reach as its not accessible: " + std::to_string(edge_id.value) + " " + shape_str; // if inbound is 0 and outbound is not then it must be an edge leaving a dead end - // meaning a begin node that is not accessable + // meaning a begin node that is not accessible EXPECT_FALSE(reach.inbound == 0 && reach.outbound > 0 && costing->Allowed(begin)) << "Only outbound reach should mean an edge that leaves a dead end: " + std::to_string(edge_id.value) + " " + shape_str; // if outbound is 0 and inbound is not then it must be an edge entering a dead end - // meaning an end node that is not accessable + // meaning an end node that is not accessible EXPECT_FALSE(reach.inbound > 0 && reach.outbound == 0 && costing->Allowed(end)) << "Only inbound reach should mean an edge that enters a dead end: " + std::to_string(edge_id.value) + " " + shape_str; diff --git a/test/recover_shortcut.cc b/test/recover_shortcut.cc index 0b68e1af35..726376851a 100644 --- a/test/recover_shortcut.cc +++ b/test/recover_shortcut.cc @@ -1,6 +1,5 @@ #include "test.h" -#include #include #include @@ -10,7 +9,6 @@ #include "midgard/encoded.h" #include "midgard/util.h" #include "src/baldr/shortcut_recovery.h" -#include using namespace valhalla; using namespace valhalla::baldr; @@ -103,7 +101,7 @@ void recover(bool cache) { continue; } - // check if the shape matches approximatly + // check if the shape matches approximately for (size_t k = 0; k < shortcut_shape.size(); ++k) { if (!shortcut_shape[k].ApproximatelyEqual(recovered_shape[k])) { // FAIL() << "edge shape points are not equal: " + @@ -144,7 +142,7 @@ TEST(GetShortcut, check_false_negatives) { auto tile = reader.GetGraphTile(tile_id); valhalla::baldr::GraphId edge_id{tile_id}; - for (int32_t i = 0; i < tile->header()->directededgecount(); i++, ++edge_id) { + for (size_t i = 0; i < tile->header()->directededgecount(); i++, ++edge_id) { auto directed_edge = tile->directededge(i); if (!directed_edge->is_shortcut()) { continue; @@ -183,7 +181,7 @@ TEST(GetShortcut, check_false_positives) { auto tile = reader.GetGraphTile(tile_id); valhalla::baldr::GraphId edge_id{tile_id}; - for (int32_t i = 0; i < tile->header()->directededgecount(); i++, ++edge_id) { + for (size_t i = 0; i < tile->header()->directededgecount(); i++, ++edge_id) { auto shortcut_id = reader.GetShortcut(edge_id); // Edges that don't belong to any shortcut will lead to invalid shortcut ID and this is fine diff --git a/test/routing.cc b/test/routing.cc index 1f5ba23bd0..07c0da181b 100644 --- a/test/routing.cc +++ b/test/routing.cc @@ -17,9 +17,9 @@ struct simple_label { }; void Add(baldr::DoubleBucketQueue& adjlist, const std::vector& costs) { - uint32_t idx = 0; - for (const auto cost : costs) { - adjlist.add(idx++); + // C++20 use iota + for (uint32_t idx = 0; idx < costs.size(); ++idx) { + adjlist.add(idx); } } diff --git a/test/scripts/test_valhalla_build_config.py b/test/scripts/test_valhalla_build_config.py index fd19a53455..8c2d422227 100644 --- a/test/scripts/test_valhalla_build_config.py +++ b/test/scripts/test_valhalla_build_config.py @@ -11,7 +11,7 @@ class TestBuildConfig(unittest.TestCase): def test_add_leaf_types(self): # test all data types, except for bool & list - # they have costum lambdas we can't test this way + # they have custom lambdas we can't test this way help_text = { "str": "string", "int": "integer", @@ -45,9 +45,14 @@ def test_add_leaf_types(self): # make sure the parser.add_argument was called with the right stuff while parsing the config for name, default in config.items(): - mock_parser.add_argument.assert_any_call( - f'--{name.replace("_", "-")}', type=value_types[name], help=help_text[name], default=default - ) + if "list" in name: + mock_parser.add_argument.assert_any_call( + f'--{name.replace("_", "-")}', nargs="+", help=help_text[name], default=default + ) + else: + mock_parser.add_argument.assert_any_call( + f'--{name.replace("_", "-")}', type=value_types[name], help=help_text[name], default=default + ) def test_nested_config(self): """tests if we get the values of nested dicts""" @@ -74,12 +79,12 @@ def test_override_config(self): valhalla_build_config.add_leaf_args('', config, leaves, mock_parser, help_text) # create the args mock - args = {'0_bool': True, "0_opt_str": valhalla_build_config.Optional(str), "0_1_list": [3, 4, 5]} + args = {'0_bool': True, "0_opt_str": valhalla_build_config.Optional(str), "0_1_list": ["item1", "item2", "item3"]} valhalla_build_config.override_config(args, leaves, config) # the Optional arg should be removed self.assertEqual(len(config["0"]), 2) - self.assertEqual(config, {"0": {"bool": True, "1": {"list": [3, 4, 5]}}}) + self.assertEqual(config, {"0": {"bool": True, "1": {"list": ["item1", "item2", "item3"]}}}) if __name__ == '__main__': diff --git a/test/scripts/test_valhalla_build_extract.py b/test/scripts/test_valhalla_build_extract.py index 14f643b0c5..ae62f3d903 100644 --- a/test/scripts/test_valhalla_build_extract.py +++ b/test/scripts/test_valhalla_build_extract.py @@ -9,7 +9,7 @@ import os import valhalla_build_extract -from valhalla_build_extract import TILE_SIZES, Bbox +from valhalla_build_extract import TILE_SIZES, Bbox, TileResolver INDEX_BIN_SIZE = valhalla_build_extract.INDEX_BIN_SIZE INDEX_BIN_FORMAT = valhalla_build_extract.INDEX_BIN_FORMAT @@ -37,41 +37,48 @@ def tile_base_to_path(base_x: int, base_y: int, level: int, fake_dir: Path) -> P level_tile_id = level | (tile_id << 3) path = str(level) + "{:,}".format(int(pow(10, TAR_PATH_LENGTHS[level])) + tile_id).replace(",", os.sep)[1:] - return fake_dir.joinpath(path + ".gph") + return Path(path + ".gph") class TestBuildExtract(unittest.TestCase): def test_tile_intersects_bbox(self): # bogus tile dir - tile_dir = Path("/home/") + tile_dir = Path("/foo/") + tile_resolver = TileResolver(tile_dir) # bbox with which to filter the tile paths bbox = "10.2,53.9,20,59.2" - input_paths = set([tile_base_to_path(*input_tuple, tile_dir) for input_tuple in ( + input_paths = [tile_base_to_path(*input_tuple, tile_dir) for input_tuple in ( (8, 50, 0), # barely intersecting in the lower left (12, 54, 1), # contained in bbox (20, 59, 2), # barely intersecting in the upper right - )]) - out_paths = valhalla_build_extract.get_tiles_with_bbox(input_paths, bbox, tile_dir) - self.assertSetEqual(input_paths, out_paths) + )] + tile_resolver.normalized_tile_paths = input_paths + tile_resolver.matched_paths = list() + valhalla_build_extract.get_tiles_with_bbox(tile_resolver, bbox) + self.assertListEqual(input_paths, tile_resolver.matched_paths) bbox = "-20,-59.2,-10.2,-53.9" - input_paths = set([tile_base_to_path(*input_tuple, tile_dir) for input_tuple in ( + input_paths = [tile_base_to_path(*input_tuple, tile_dir) for input_tuple in ( (-20, -59, 2), # barely intersecting in the lower left (-12, -54, 1), # contained in bbox (-12, -62, 0), - )]) - out_paths = valhalla_build_extract.get_tiles_with_bbox(input_paths, bbox, tile_dir) - self.assertSetEqual(input_paths, out_paths) + )] + tile_resolver.normalized_tile_paths = input_paths + tile_resolver.matched_paths = list() + valhalla_build_extract.get_tiles_with_bbox(tile_resolver, bbox) + self.assertListEqual(input_paths, tile_resolver.matched_paths) # don't find the ones not intersecting bbox = "0,10,4,14" - input_paths = set([tile_base_to_path(*input_tuple, tile_dir) for input_tuple in ( + input_paths = [tile_base_to_path(*input_tuple, tile_dir) for input_tuple in ( (8, 14, 0), (-0.50, 9.75, 2) - )]) - out_paths = valhalla_build_extract.get_tiles_with_bbox(input_paths, bbox, tile_dir) - self.assertSetEqual(out_paths, set()) + )] + tile_resolver.normalized_tile_paths = input_paths + tile_resolver.matched_paths = list() + valhalla_build_extract.get_tiles_with_bbox(tile_resolver, bbox) + self.assertListEqual(tile_resolver.matched_paths, list()) def test_tile_intersects_geojson(self): # create 1 polygon with 2 height & width, should leave out e.g. tile (2,2) @@ -80,7 +87,8 @@ def test_tile_intersects_geojson(self): # |___| # bogus tile dir - tile_dir = Path("/home/") + tile_dir = Path("/foo/") + tile_resolver = TileResolver(tile_dir) gj = { "type": "FeatureCollection", @@ -102,7 +110,7 @@ def test_tile_intersects_geojson(self): with open(gj_fp, 'w') as f: json.dump(gj, f) - input_paths = set([tile_base_to_path(*input_tuple, tile_dir) for input_tuple in ( + input_paths = [tile_base_to_path(*input_tuple, tile_dir) for input_tuple in ( (0, 2, 0), (0, 2, 1), (0, 3, 1), @@ -110,19 +118,23 @@ def test_tile_intersects_geojson(self): (0, 2, 2), (1.75, 2.75, 2), (0.75, 3.25, 2) - )]) - out_paths = valhalla_build_extract.get_tiles_with_geojson(input_paths, gj_dir, tile_dir) - self.assertSetEqual(input_paths, out_paths) + )] + tile_resolver.normalized_tile_paths = input_paths + tile_resolver.matched_paths = list() + valhalla_build_extract.get_tiles_with_geojson(tile_resolver, gj_dir) + self.assertListEqual(input_paths, tile_resolver.matched_paths) # don't find the ones not intersecting - input_paths = set([tile_base_to_path(*input_tuple, tile_dir) for input_tuple in ( + input_paths = [tile_base_to_path(*input_tuple, tile_dir) for input_tuple in ( (4, 2, 0), (1, 3, 1), (1, 4.75, 2), (1, 3, 2), - )]) - out_paths = valhalla_build_extract.get_tiles_with_geojson(input_paths, gj_dir, tile_dir) - self.assertSetEqual(out_paths, set()) + )] + tile_resolver.normalized_tile_paths = input_paths + tile_resolver.matched_paths = list() + valhalla_build_extract.get_tiles_with_geojson(tile_resolver, gj_dir) + self.assertListEqual(tile_resolver.matched_paths, list()) gj_fp.unlink() gj_dir.rmdir() @@ -132,17 +144,27 @@ def test_create_extracts(self): "traffic_extract": str(TRAFFIC_PATH)}} # it will open the tars in write mode, so other test output can't interfere - tile_paths = sorted(TILE_PATH.rglob('*.gph')) - valhalla_build_extract.create_extracts(config, True, tile_paths) - tile_count = len(tile_paths) + # tests the implementation using the tile_dir + tile_resolver = TileResolver(TILE_PATH) + tile_resolver.matched_paths = tile_resolver.normalized_tile_paths + valhalla_build_extract.create_extracts(config, True, tile_resolver, EXTRACT_PATH) + tile_count = len(tile_resolver.matched_paths) # test that the index has the right offsets/sizes - exp_tuples = ((2560, 25568, 291912), (296448, 410441, 662496), (960512, 6549282, 6059792)) + exp_tuples = ((2560, 25568, 296768), (301056, 410441, 665624), (968704, 6549282, 6137080)) self.check_tar(EXTRACT_PATH, exp_tuples, tile_count * INDEX_BIN_SIZE) # same for traffic.tar - exp_tuples = ((1536, 25568, 26416), (28672, 410441, 65552), (95232, 6549282, 604608)) + exp_tuples = ((1536, 25568, 25856), (28160, 410441, 64400), (93184, 6549282, 604608)) self.check_tar(TRAFFIC_PATH, exp_tuples, tile_count * INDEX_BIN_SIZE) + # tests the implementation using the tile_dir + new_tile_extract = TILE_PATH.joinpath("tiles2.tar") + exp_tuples = ((2560, 25568, 296768), (301056, 410441, 665624), (968704, 6549282, 6137080)) + tile_resolver = TileResolver(EXTRACT_PATH) + tile_resolver.matched_paths = tile_resolver.normalized_tile_paths + valhalla_build_extract.create_extracts(config, True, tile_resolver, new_tile_extract) + self.check_tar(new_tile_extract, exp_tuples, tile_count * INDEX_BIN_SIZE) + def check_tar(self, p: Path, exp_tuples, end_index): with open(p, 'r+b') as f: f.seek(tarfile.BLOCKSIZE) diff --git a/test/search.cc b/test/search.cc index 1ce7f39567..0053d1e760 100644 --- a/test/search.cc +++ b/test/search.cc @@ -1,15 +1,15 @@ #include "loki/search.h" + #include +#include #include -#include #include "baldr/graphid.h" #include "baldr/graphreader.h" #include "baldr/location.h" #include "baldr/pathlocation.h" #include "baldr/tilehierarchy.h" -#include "filesystem.h" #include "midgard/pointll.h" #include "midgard/vector2.h" #include "sif/nocost.h" @@ -26,20 +26,21 @@ namespace vs = valhalla::sif; namespace { -// this is what it looks like -// b -// |\ -// 1 | \ 0 -// | \ -// 2 | \ 7 -// | \ -// a-3-8-d -// | / -// 4 | / 9 -// | / -// 5 | / 6 -// |/ -// c +/* this is what it looks like + b + |\ + 1 | \ 0 + | \ + 2 | \ 7 + | \ + a-3-8-d + | / + 4 | / 9 + | / + 5 | / 6 + |/ + c +*/ const std::string tile_dir = "test/search_tiles"; GraphId tile_id = TileHierarchy::GetGraphId({.125, .125}, 2); PointLL base_ll = TileHierarchy::get_tiling(tile_id.level()).Base(tile_id.tileid()); @@ -49,8 +50,8 @@ std::pair c({tile_id.tileid(), tile_id.level(), 2}, {.01, .01} std::pair d({tile_id.tileid(), tile_id.level(), 3}, {.2, .1}); void clean_tiles() { - if (filesystem::is_directory(tile_dir)) { - filesystem::remove_all(tile_dir); + if (std::filesystem::is_directory(tile_dir)) { + std::filesystem::remove_all(tile_dir); } } @@ -106,23 +107,23 @@ void make_tile() { tile.directededges().emplace_back(edge_builder); }; - // this is what it looks like - // b 0 - // |\ - // 1 | \ 0 - // | \ - // 2 | \ 7 - // | \ - // 1a-3-8-d 3 - // | / - // 4 | / 9 - // | / - // 5 | / 6 - // |/ - // c 2 - - // NOTE: edge ids are in the order the edges are added, so b->d is 0, b->a is 1, a->b is 2 and so - // on + /* this is what it looks like + b 0 + |\ + 1 | \ 0 + | \ + 2 | \ 7 + | \ + 1a-3-8-d 3 + | / + 4 | / 9 + | / + 5 | / 6 + |/ + c 2 + + NOTE: edge ids are in the order the edges are added, so b->d is 0, b->a is 1, a->b is 2 and so + */ // B { diff --git a/test/shape_attributes.cc b/test/shape_attributes.cc index 4c38b4200f..21d47b86da 100644 --- a/test/shape_attributes.cc +++ b/test/shape_attributes.cc @@ -1,8 +1,5 @@ #include "test.h" -#include -#include - #include "midgard/distanceapproximator.h" #include "midgard/encoded.h" #include "midgard/logging.h" @@ -50,7 +47,7 @@ TEST(ShapeAttributes, test_shape_attributes_included) { EXPECT_EQ(shape_attributes_speed.Size(), shape.size() - 1); // Measures the length between point - for (int i = 1; i < shape.size(); i++) { + for (size_t i = 1; i < shape.size(); i++) { auto distance = shape[i].Distance(shape[i - 1]) * .001f; // Measuring that the length between shape pts is approx. to the shape attributes length @@ -59,7 +56,7 @@ TEST(ShapeAttributes, test_shape_attributes_included) { // Assert that the shape attributes (time, length, speed) are equal to their corresponding edge // attributes - for (int e = 0; e < edges.Size(); e++) { + for (rapidjson::SizeType e = 0; e < edges.Size(); e++) { auto edge_length = edges[e]["length"].GetDouble(); auto edge_speed = edges[e]["speed"].GetDouble(); @@ -110,7 +107,7 @@ TEST(ShapeAttributes, test_shape_attributes_duplicated_point) { EXPECT_EQ(shape_attributes_speed.Size(), shape.size() - 1); // Measures the length between point - for (int i = 1; i < shape.size(); i++) { + for (size_t i = 1; i < shape.size(); i++) { auto distance = shape[i].Distance(shape[i - 1]) * .001f; // Measuring that the length between shape pts is approx. to the shape attributes length @@ -119,7 +116,7 @@ TEST(ShapeAttributes, test_shape_attributes_duplicated_point) { // Assert that the shape attributes (time, length, speed) are equal to their corresponding edge // attributes - for (int e = 0; e < edges.Size(); e++) { + for (rapidjson::SizeType e = 0; e < edges.Size(); e++) { auto edge_length = edges[e]["length"].GetDouble(); auto edge_speed = edges[e]["speed"].GetDouble(); @@ -168,7 +165,7 @@ TEST(ShapeAttributes, test_shape_attributes_no_turncosts) { EXPECT_EQ(shape_attributes_speed.Size(), shape.size() - 1); // Measures the length between point - for (int i = 1; i < shape.size(); i++) { + for (size_t i = 1; i < shape.size(); i++) { auto distance = shape[i].Distance(shape[i - 1]) * .001f; // Measuring that the length between shape pts is approx. to the shape attributes length diff --git a/test/signinfo.cc b/test/signinfo.cc index 87cec08c1d..c544bad0d6 100644 --- a/test/signinfo.cc +++ b/test/signinfo.cc @@ -6,7 +6,6 @@ #include "mjolnir/graphbuilder.h" -using namespace std; using namespace valhalla::mjolnir; using namespace valhalla::baldr; using valhalla::mjolnir::GraphBuilder; @@ -19,7 +18,8 @@ TEST(Signinfo, ExitToTest) { OSMWay way{}; OSMData osmdata{}; - OSMPronunciation pronunciation{}; + std::map, uint32_t> pronunciationMap; + const std::map, uint32_t> langMap; bool fork = false; bool forward = true; @@ -28,9 +28,14 @@ TEST(Signinfo, ExitToTest) { exit_node.set_name_index(osmdata.node_names.index("PATP West Exit")); std::vector signs; - std::vector pronunciations; - bool has_guide = GraphBuilder::CreateSignInfoList(exit_node, way, pronunciation, osmdata, signs, - pronunciations, fork, forward, true, false); + std::vector linguistics; + std::vector> default_languages; + const std::string linguistic_node_file = "test_sign_linguistic_node.bin"; + sequence linguistic_node(linguistic_node_file, true); + + bool has_guide = GraphBuilder::CreateSignInfoList(exit_node, way, pronunciationMap, langMap, + osmdata, default_languages, linguistic_node, + signs, linguistics, fork, forward, true, false); EXPECT_FALSE(has_guide) << "Exits should not be Guides"; @@ -45,8 +50,9 @@ TEST(Signinfo, ExitToTest) { node.set_exit_to_index(osmdata.node_names.index("US 11;To I 81;Carlisle;Harrisburg")); signs.clear(); - has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciation, osmdata, signs, - pronunciations, fork, forward, true, false); + has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciationMap, langMap, osmdata, + default_languages, linguistic_node, signs, linguistics, + fork, forward, true, false); EXPECT_FALSE(has_guide) << "Exits should not be Guides"; @@ -66,8 +72,9 @@ TEST(Signinfo, ExitToTest) { signs.clear(); node.set_exit_to_index(osmdata.node_names.index("US 11;Toward I 81;Carlisle;Harrisburg")); - has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciation, osmdata, signs, - pronunciations, fork, forward, true, false); + has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciationMap, langMap, osmdata, + default_languages, linguistic_node, signs, linguistics, + fork, forward, true, false); EXPECT_FALSE(has_guide) << "Exits should not be Guides"; @@ -87,8 +94,9 @@ TEST(Signinfo, ExitToTest) { signs.clear(); node.set_exit_to_index(osmdata.node_names.index("I 95 To I 695")); - has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciation, osmdata, signs, - pronunciations, fork, forward, true, false); + has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciationMap, langMap, osmdata, + default_languages, linguistic_node, signs, linguistics, + fork, forward, true, false); EXPECT_FALSE(has_guide) << "Exits should not be Guides"; @@ -104,8 +112,9 @@ TEST(Signinfo, ExitToTest) { signs.clear(); node.set_exit_to_index(osmdata.node_names.index("I 495 Toward I 270")); - has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciation, osmdata, signs, - pronunciations, fork, forward, true, false); + has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciationMap, langMap, osmdata, + default_languages, linguistic_node, signs, linguistics, + fork, forward, true, false); EXPECT_FALSE(has_guide) << "Exits should not be Guides"; @@ -122,8 +131,9 @@ TEST(Signinfo, ExitToTest) { node.set_exit_to_index( osmdata.node_names.index("I 495 Toward I 270 To I 95")); // default to toward. Punt on parsing. - has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciation, osmdata, signs, - pronunciations, fork, forward, true, false); + has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciationMap, langMap, osmdata, + default_languages, linguistic_node, signs, linguistics, + fork, forward, true, false); EXPECT_FALSE(has_guide) << "Exits should not be Guides"; @@ -140,8 +150,9 @@ TEST(Signinfo, ExitToTest) { signs.clear(); auto index = osmdata.name_offset_map.index("I 495 North"); way.set_destination_ref_index(index); - has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciation, osmdata, signs, - pronunciations, fork, forward, true, false); + has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciationMap, langMap, osmdata, + default_languages, linguistic_node, signs, linguistics, + fork, forward, true, false); EXPECT_FALSE(has_guide) << "Exits should not be Guides"; @@ -157,8 +168,9 @@ TEST(Signinfo, ExitToTest) { signs.clear(); index = osmdata.name_offset_map.index("I 495 North"); way.set_destination_ref_index(index); - has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciation, osmdata, signs, - pronunciations, fork, forward, false, true); + has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciationMap, langMap, osmdata, + default_languages, linguistic_node, signs, linguistics, + fork, forward, false, true); EXPECT_TRUE(has_guide) << "Guides should not be Exits"; @@ -176,8 +188,9 @@ TEST(Signinfo, ExitToTest) { signs.clear(); auto index2 = osmdata.name_offset_map.index("I 695 North"); way2.set_destination_ref_to_index(index2); - has_guide = GraphBuilder::CreateSignInfoList(node, way2, pronunciation, osmdata, signs, - pronunciations, fork, forward, true, false); + has_guide = GraphBuilder::CreateSignInfoList(node, way2, pronunciationMap, langMap, osmdata, + default_languages, linguistic_node, signs, linguistics, + fork, forward, true, false); EXPECT_FALSE(has_guide) << "Exits should not be Guides"; @@ -195,8 +208,9 @@ TEST(Signinfo, ExitToTest) { signs.clear(); auto index3 = osmdata.name_offset_map.index("I 695 North"); way3.set_destination_ref_to_index(index3); - has_guide = GraphBuilder::CreateSignInfoList(node, way2, pronunciation, osmdata, signs, - pronunciations, fork, forward, false, true); + has_guide = GraphBuilder::CreateSignInfoList(node, way2, pronunciationMap, langMap, osmdata, + default_languages, linguistic_node, signs, linguistics, + fork, forward, false, true); EXPECT_TRUE(has_guide) << "Guides should not be Exits"; @@ -212,8 +226,9 @@ TEST(Signinfo, ExitToTest) { // Add a ref toward guide sign and we should not add a exit number or exit name. note: using // exit_node signs.clear(); - has_guide = GraphBuilder::CreateSignInfoList(exit_node, way2, pronunciation, osmdata, signs, - pronunciations, fork, forward, false, true); + has_guide = GraphBuilder::CreateSignInfoList(exit_node, way2, pronunciationMap, langMap, osmdata, + default_languages, linguistic_node, signs, linguistics, + fork, forward, false, true); EXPECT_TRUE(has_guide) << "Guides should not be Exits"; @@ -226,6 +241,8 @@ TEST(Signinfo, ExitToTest) { } else { FAIL() << "destination ref I 695 North failed to create exist sign. No exit 5 should exist."; } + + filesystem::remove(linguistic_node_file); } } // namespace diff --git a/test/skadi_service.cc b/test/skadi_service.cc index 1263d44995..b6287a023e 100644 --- a/test/skadi_service.cc +++ b/test/skadi_service.cc @@ -2,7 +2,6 @@ #include "pixels.h" #include "test.h" -#include #include #include @@ -155,7 +154,7 @@ const std::vector responses{ "244,243,240,239,239,238,239,241,241,239,236,221,221,225,224]}"), }; -const auto config = +const auto cfg = test::make_config(VALHALLA_BUILD_DIR "test" + std::string(1, filesystem::path::preferred_separator) + "skadi_service_tmp"); @@ -173,7 +172,7 @@ void create_tile() { tile[p.first] = p.second; // a place to store it - auto tile_dir = config.get("additional_data.elevation"); + auto tile_dir = cfg.get("additional_data.elevation"); if (!filesystem::is_directory(tile_dir) && !filesystem::create_directories(tile_dir)) throw std::runtime_error("Couldnt make directory to store elevation"); @@ -188,26 +187,26 @@ void create_tile() { zmq::context_t context; void start_service() { // need a place to drop our sockets - auto run_dir = config.get("mjolnir.tile_dir"); + auto run_dir = cfg.get("mjolnir.tile_dir"); if (!filesystem::is_directory(run_dir) && !filesystem::create_directories(run_dir)) throw std::runtime_error("Couldnt make directory to run from"); // server std::thread server(std::bind(&http_server_t::serve, - http_server_t(context, config.get("httpd.service.listen"), - config.get("loki.service.proxy") + "_in", - config.get("httpd.service.loopback"), - config.get("httpd.service.interrupt")))); + http_server_t(context, cfg.get("httpd.service.listen"), + cfg.get("loki.service.proxy") + "_in", + cfg.get("httpd.service.loopback"), + cfg.get("httpd.service.interrupt")))); server.detach(); // load balancer std::thread proxy(std::bind(&proxy_t::forward, - proxy_t(context, config.get("loki.service.proxy") + "_in", - config.get("loki.service.proxy") + "_out"))); + proxy_t(context, cfg.get("loki.service.proxy") + "_in", + cfg.get("loki.service.proxy") + "_out"))); proxy.detach(); // service worker - std::thread worker(valhalla::loki::run_service, config); + std::thread worker(valhalla::loki::run_service, cfg); worker.detach(); } @@ -224,7 +223,7 @@ TEST(SkadiService, test_requests) { auto request = requests.cbegin(); std::string request_str; http_client_t client( - context, config.get("httpd.service.listen"), + context, cfg.get("httpd.service.listen"), [&request, &request_str]() { // we dont have any more requests so bail if (request == requests.cend()) @@ -241,9 +240,6 @@ TEST(SkadiService, test_requests) { }, 1); - // make this whole thing bail if it doesnt finish fast - alarm(120); - // request and receive client.batch(); } diff --git a/test/statsd.cc b/test/statsd.cc index ed9a0109c4..0e8d1fb113 100644 --- a/test/statsd.cc +++ b/test/statsd.cc @@ -54,7 +54,7 @@ class test_worker_t : public service_worker_t { std::string service_name() const override { return "test"; } -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES virtual prime_server::worker_t::result_t work(const std::list&, void*, const std::function&) { throw std::runtime_error("We arent testing the work method directly"); diff --git a/test/streetname.cc b/test/streetname.cc index 4c215845e5..c302a5d121 100644 --- a/test/streetname.cc +++ b/test/streetname.cc @@ -1,7 +1,5 @@ #include "baldr/streetname.h" -#include - #include "test.h" using namespace std; diff --git a/test/streetname_us.cc b/test/streetname_us.cc index 9171ad30be..2ed76e263c 100644 --- a/test/streetname_us.cc +++ b/test/streetname_us.cc @@ -1,7 +1,5 @@ #include "baldr/streetname_us.h" -#include - #include "test.h" using namespace std; diff --git a/test/streetnames_us.cc b/test/streetnames_us.cc index 255ff84345..893d7d1e20 100644 --- a/test/streetnames_us.cc +++ b/test/streetnames_us.cc @@ -1,7 +1,6 @@ #include "baldr/streetnames_us.h" #include "baldr/streetname_us.h" -#include #include #include "test.h" diff --git a/test/summary.cc b/test/summary.cc index 61d2992860..e0ea71686b 100644 --- a/test/summary.cc +++ b/test/summary.cc @@ -1,8 +1,6 @@ #include "test.h" -#include #include -#include #include "baldr/graphreader.h" #include "baldr/rapidjson_utils.h" @@ -10,7 +8,6 @@ #include "odin/worker.h" #include "thor/worker.h" #include "tyr/serializers.h" -#include using namespace valhalla; using namespace valhalla::baldr; @@ -52,9 +49,11 @@ TEST(Summary, test_time_summary) { "filters":{"attributes":["shape_attributes.time"],"action":"include"}})"; auto response = tester.test(request); const auto& leg = response.trip().routes(0).legs(0); - EXPECT_EQ( - leg.shape(), - "ibykbB}afxHWge@oAqh@y@sZoPy[{HuMeHmMgDgSq@iFS{@sDsIgAz@iR`LoHlEyIjFcSfF{P}@gJAmL{MqNkLaReSuUc^cIcPsIeUoOkf@sRqs@mKgg@_Qs~@kMy|@wHi{@sC__@y@{_@|@g^fEs_@lI}^lJaX|NqWjMyNpa@gYbk@yUto@_WnvAmn@~l@uVnc@mRdGcDnNaHza@kSfg@cUjqBs{@pdB}w@jrDsyAv`C}|@dZqKlWuL~{B}_Ajf@yOhgAy`@lr@q^fj@cYzp@qa@j]aU|ZaTv`@g[vmAobAjXyNp\\_PbPmAtTnA|UrJjNjKzMbQfM`XzKtZz\\p_BtQfbA~NhcAdJfj@~~@roEvn@dmC~DdPpKiJ~N_MdNsJdOkIbNmFx^iDlP\\hOdAdOpDzCnApChAnGhCbOvJlQjPdL`LfYnZpgAhnAd|@t{@xZlSne@x\\xaBfdAnd@xZ|uBjtAvKjHdkAfw@fWzNvJrF~IdGxLrIpLlIrSjQ`PdRbNlTlPzZvPjc@fK`Zbg@d{A~Pph@hXvv@tMb^j[zs@~Qz]jMnTtRx[nh@pu@~UzZ`UtZr^tc@na@tf@fYrZx`@j^n_@`X~]tSzyAzl@~|@l]~}Avm@zjAdd@|y@hZbgApa@rStJbJjIjElGpDnHhIzTxHjXrB`Mv@zZIzb@u@hXeQ~qBm_@`vDap@baFsHvm@sE|SsFzX_Kjd@eIj\\mHnXwHzZ{DrQqDzS{ApLyBp\\]dEYtDGtCb@jE|@zCxBxDtHfBno@pOr[hEbT|A`LWbGWhJeB`C]`S{EpWqKtHyDjeB}t@hViFpKk@|MtAnSdH~VdS~Wld@dKfSnJfWtIpa@b\\~yBrChQhGlVrH`UpLpV~@jBrCnGdInUdGjZ|Ejd@|Ala@dAxa@@d_@{Apc@kB`_@aCx^iAfKiBzKqB~LwCnQwHb]cHvWiHdU}[nz@kEbLaP`a@|F|GxDdFrl@~{@|n@``Ahi@~v@pUt_@dg@h~@b{@jaBj\\dp@bYjg@fWdd@vGnL}OtnAqCtd@uB`OaR~uAq@lDoCfMaC`NaAjFsAjKu@pHcCxUSlJ{ClXmB`H}@nF[fFIzHp@nH|CtKxDrGpDlC~WdO|CxBE~TaKh}@sVaLyBbAsFlh@A`FMt`@c@xC"); + EXPECT_TRUE( + test:: + encoded_shape_equality( + leg.shape(), + "ibykbB}afxHWge@oAqh@y@sZoPy[{HuMeHmMgDgSq@iFS{@sDsIgAz@iR`LoHlEyIjFcSfF{P}@gJAmL{MqNkLaReSuUc^cIcPsIeUoOkf@sRqs@mKgg@_Qs~@kMy|@wHi{@sC__@y@{_@|@g^fEs_@lI}^lJaX|NqWjMyNpa@gYbk@yUto@_WnvAmn@~l@uVnc@mRdGcDnNaHza@kSfg@cUjqBs{@pdB}w@jrDsyAv`C}|@dZqKlWuL~{B}_Ajf@yOhgAy`@lr@q^fj@cYzp@qa@j]aU|ZaTv`@g[vmAobAjXyNp\\_PbPmAtTnA|UrJjNjKzMbQfM`XzKtZz\\p_BtQfbA~NhcAdJfj@~~@roEvn@dmC~DdPpKiJ~N_MdNsJdOkIbNmFx^iDlP\\hOdAdOpDzCnApChAnGhCbOvJlQjPdL`LfYnZpgAhnAd|@t{@xZlSne@x\\xaBfdAnd@xZ|uBjtAvKjHdkAfw@fWzNvJrF~IdGxLrIpLlIrSjQ`PdRbNlTlPzZvPjc@fK`Zbg@d{A~Pph@hXvv@tMb^j[zs@~Qz]jMnTtRx[nh@pu@~UzZ`UtZr^tc@na@tf@fYrZx`@j^n_@`X~]tSzyAzl@~|@l]~}Avm@zjAdd@|y@hZbgApa@rStJbJjIjElGpDnHhIzTxHjXrB`Mv@zZIzb@u@hXeQ~qBm_@`vDap@baFsHvm@sE|SsFzX_Kjd@eIj\\mHnXwHzZ{DrQqDzS{ApLyBp\\]dEYtDGtCb@jE|@zCxBxDtHfBno@pOr[hEbT|A`LWbGWhJeB`C]`S{EpWqKtHyDjeB}t@hViFpKk@|MtAnSdH~VdS~Wld@dKfSnJfWtIpa@b\\~yBrChQhGlVrH`UpLpV~@jBrCnGdInUdGjZ|Ejd@|Ala@dAxa@@d_@{Apc@kB`_@aCx^iAfKiBzKqB~LwCnQwHb]cHvWiHdU}[nz@kEbLaP`a@|F|GxDdFrl@~{@|n@``Ahi@~v@pUt_@dg@h~@b{@jaBj\\dp@bYjg@fWdd@vGnL}OtnAqCtd@uB`OaR~uAq@lDoCfMaC`NaAjFsAjKu@pHcCxUSlJ{ClXmB`H}@nF[fFIzHp@nH|CtKxDrGpDlC~WdO|CxBE~TaKh}@sVaLyBbAsFlh@A`FMt`@c@xC")); // loop over all routes all legs auto trip_route = response.trip().routes().begin(); diff --git a/test/tar_index.cc b/test/tar_index.cc index 982e8274c1..e44d14c333 100644 --- a/test/tar_index.cc +++ b/test/tar_index.cc @@ -1,7 +1,6 @@ #include "test.h" #include "baldr/graphreader.h" -#include namespace vb = valhalla::baldr; diff --git a/test/test.cc b/test/test.cc index 7656f86b33..f501d08a8d 100644 --- a/test/test.cc +++ b/test/test.cc @@ -8,8 +8,8 @@ #include "mjolnir/graphtilebuilder.h" #include +#include #include -#include #include #include #include @@ -23,7 +23,6 @@ #include -#include "filesystem.h" #include "microtar.h" namespace { @@ -222,6 +221,7 @@ boost::property_tree::ptree make_config(const std::string& path_prefix, "mjolnir": { "concurrency": 1, "admin": "%%/admin.sqlite", + "landmarks": "%%/landmarks.sqlite", "data_processing": { "allow_alt_name": false, "apply_country_overrides": true, @@ -441,8 +441,8 @@ void build_live_traffic_data(const boost::property_tree::ptree& config, std::string tile_dir = config.get("mjolnir.tile_dir"); std::string traffic_extract = config.get("mjolnir.traffic_extract"); - filesystem::path parent_dir = filesystem::path(traffic_extract).parent_path(); - if (!filesystem::exists(parent_dir)) { + std::filesystem::path parent_dir = std::filesystem::path(traffic_extract).parent_path(); + if (!std::filesystem::exists(parent_dir)) { std::stringstream ss; ss << "Traffic extract directory " << parent_dir.string() << " does not exist"; throw std::runtime_error(ss.str()); diff --git a/test/test.h b/test/test.h index 30e7402427..68ec4f1bcc 100644 --- a/test/test.h +++ b/test/test.h @@ -5,13 +5,11 @@ #include "baldr/rapidjson_utils.h" #include "baldr/traffictile.h" #include "config.h" +#include "midgard/polyline2.h" #include "mjolnir/graphtilebuilder.h" #include -#include #include -#include -#include #include #ifndef _MSC_VER #include @@ -53,6 +51,23 @@ make_config(const std::string& path_prefix, const std::unordered_map& overrides = {}, const std::unordered_set& removes = {}); +template +testing::AssertionResult shape_equality(const container_t& expected, + const container_t& actual, + typename container_t::value_type::first_type tolerance = 1) { + auto hd = Polyline2::HausdorffDistance(expected, actual); + if (hd > tolerance) + return testing::AssertionFailure() << "shape exceeds tolerance by " << hd - tolerance; + return testing::AssertionSuccess(); +} + +inline testing::AssertionResult +encoded_shape_equality(const std::string& expected, const std::string& actual, double tolerance = 1) { + auto expected_shp = decode>(expected); + auto actual_shp = decode>(actual); + return shape_equality(expected_shp, actual_shp, tolerance); +} + /** * Generate a new GraphReader that doesn't re-use a previously * statically initizalized tile_extract member variable. diff --git a/test/thor_worker.cc b/test/thor_worker.cc index 2287cb9d0a..3c2d7b58ff 100644 --- a/test/thor_worker.cc +++ b/test/thor_worker.cc @@ -6,7 +6,6 @@ #include "thor/worker.h" #include "tyr/actor.h" #include -#include #include using namespace valhalla; diff --git a/test/tilehierarchy.cc b/test/tilehierarchy.cc index db47053bda..3a52bf2050 100644 --- a/test/tilehierarchy.cc +++ b/test/tilehierarchy.cc @@ -4,8 +4,6 @@ #include "test.h" -#include - using namespace std; using namespace valhalla::baldr; using namespace valhalla::midgard; @@ -57,7 +55,7 @@ TEST(TileHierarchy, Tiles) { EXPECT_EQ(ids.size(), 4) << "Should have found 4 results."; } -TEST(TileHeirarchy, parent) { +TEST(TileHierarchy, parent) { GraphId id(1440 * 16 + 16, 3, 0); auto level2 = TileHierarchy::parent(id); EXPECT_EQ(level2, GraphId(id.tileid(), 2, 0)); diff --git a/test/tiles.cc b/test/tiles.cc index 8da5d2783e..9c08975873 100644 --- a/test/tiles.cc +++ b/test/tiles.cc @@ -4,6 +4,7 @@ #include "midgard/pointll.h" #include "midgard/util.h" +#include #include #include "test.h" @@ -34,7 +35,7 @@ TEST(Tiles, TestBase) { EXPECT_EQ(ll.lng(), -179); EXPECT_EQ(ll.lat(), -90); - // right bottm + // right bottom ll = tiles.Base(359); EXPECT_EQ(ll.lng(), 179); EXPECT_EQ(ll.lat(), -90); @@ -58,6 +59,32 @@ TEST(Tiles, TestRowCol) { EXPECT_EQ(tileid1, tileid2) << "TileId does not match using row,col"; } +TEST(Tiles, TestTileBounds) { + Tiles tiles(AABB2(PointLL(-180, -90), PointLL(180, 90)), 1); + auto n_tiles = tiles.ncolumns() * tiles.nrows(); + EXPECT_EQ(n_tiles, 360 * 180) << "Number of tiles not correct"; + auto ids = std::array{0, + 1, + tiles.ncolumns() - 1, + tiles.ncolumns(), + n_tiles / 2 - 1, + n_tiles / 2, + n_tiles / 2 + 1, + n_tiles - tiles.ncolumns() - 1, + n_tiles - tiles.ncolumns(), + n_tiles - 2, + n_tiles - 1}; + for (auto id : ids) { + auto bounds1 = tiles.TileBounds(id); + auto [row, col] = tiles.GetRowColumn(id); + auto bounds2 = tiles.TileBounds(col, row); + EXPECT_DOUBLE_EQ(bounds1.minx(), bounds2.minx()) << "Bounds of tile not equal"; + EXPECT_DOUBLE_EQ(bounds1.maxx(), bounds2.maxx()) << "Bounds of tile not equal"; + EXPECT_DOUBLE_EQ(bounds1.miny(), bounds2.miny()) << "Bounds of tile not equal"; + EXPECT_DOUBLE_EQ(bounds1.miny(), bounds2.miny()) << "Bounds of tile not equal"; + } +} + TEST(Tiles, TestNeighbors) { Tiles tiles(AABB2(PointLL(-180, -90), PointLL(180, 90)), 1); @@ -342,7 +369,7 @@ TEST(Tiles, test_random_linestring) { auto answer = t.Intersect(linestring); for (const auto& tile : answer) for (auto sub : tile.second) - ASSERT_LE(sub, 24) << "Non-existant bin!"; + ASSERT_LE(sub, 24) << "Non-existent bin!"; } } @@ -542,6 +569,18 @@ TEST(Tiles, test_intersect_bbox_rounding) { EXPECT_EQ(bin_id, 0); } +TEST(Tiles, float_roundoff_issue) { + AABB2 world_box{-180, -90, 180, 90}; + Tiles t(world_box, 0.25, 5); + + PointLL ll(179.999978, -16.805363); + auto tile_id = t.TileId(ll); + EXPECT_EQ(tile_id, 421919); + auto base_ll = t.Base(tile_id); + EXPECT_EQ(base_ll.lat(), -17.0); + EXPECT_EQ(base_ll.lng(), 179.75); +} + } // namespace int main(int argc, char* argv[]) { diff --git a/test/timedep_paths.cc b/test/timedep_paths.cc index 28845e809b..4322762508 100644 --- a/test/timedep_paths.cc +++ b/test/timedep_paths.cc @@ -1,8 +1,6 @@ #include "test.h" -#include #include -#include #include "baldr/rapidjson_utils.h" #include "loki/worker.h" @@ -37,7 +35,7 @@ const std::unordered_map kMaxDistances = { // a scale factor to apply to the score so that we bias towards closer results more constexpr float kDistanceScale = 10.f; -const auto config = test::make_config("test/data/utrecht_tiles"); +const auto cfg = test::make_config(VALHALLA_BUILD_DIR "test/data/utrecht_tiles"); void try_path(GraphReader& reader, loki_worker_t& loki_worker, @@ -70,15 +68,15 @@ void try_path(GraphReader& reader, TEST(TimeDepPaths, test_depart_at_paths) { // Test setup - loki_worker_t loki_worker(config); - GraphReader reader(config.get_child("mjolnir")); + loki_worker_t loki_worker(cfg); + GraphReader reader(cfg.get_child("mjolnir")); - // Simple path along oneway edge in the driveable direction - should return a single edge + // Simple path along oneway edge in the drivable direction - should return a single edge const auto test_request1 = R"({"locations":[{"lat":52.079079,"lon":5.115197}, {"lat":52.078937,"lon":5.115321}],"costing":"auto","date_time":{"type":1,"value":"2018-06-28T07:00"}})"; try_path(reader, loki_worker, true, test_request1, 1); - // Simple path along oneway edge opposing the driveable direction -must not + // Simple path along oneway edge opposing the drivable direction -must not // return a single edge (edge count is 10) const auto test_request2 = R"({"locations":[{"lat":52.078937,"lon":5.115321}, {"lat":52.079079,"lon":5.115197}],"costing":"auto","date_time":{"type":1,"value":"2018-06-28T07:00"}})"; @@ -87,10 +85,10 @@ TEST(TimeDepPaths, test_depart_at_paths) { TEST(TimeDepPaths, test_arrive_by_paths) { // Test setup - loki_worker_t loki_worker(config); - GraphReader reader(config.get_child("mjolnir")); + loki_worker_t loki_worker(cfg); + GraphReader reader(cfg.get_child("mjolnir")); - // Simple path along oneway edge in the driveable direction - should return a single edge + // Simple path along oneway edge in the drivable direction - should return a single edge const auto test_request1 = R"({"locations":[{"lat":52.079079,"lon":5.115197}, {"lat":52.078937,"lon":5.115321}],"costing":"auto","date_time":{"type":2,"value":"2018-06-28T07:00"}})"; try_path(reader, loki_worker, false, test_request1, 1); diff --git a/test/timeparsing.cc b/test/timeparsing.cc index 04ef77961a..68c8bf7004 100644 --- a/test/timeparsing.cc +++ b/test/timeparsing.cc @@ -1,5 +1,4 @@ #include -#include #include #include @@ -49,20 +48,20 @@ void TryConditionalRestrictions(const std::string& condition, } } +struct DateTimePoint { + uint32_t month; + uint32_t day; + uint32_t week; + uint32_t hour; + uint32_t minute; +}; + void TryConditionalRestrictions(const std::string& condition, const uint32_t index, const uint32_t type, const uint32_t dow, - const uint32_t begin_month, - const uint32_t begin_day, - const uint32_t begin_week, - const uint32_t begin_hrs, - const uint32_t begin_mins, - const uint32_t end_month, - const uint32_t end_day, - const uint32_t end_week, - const uint32_t end_hrs, - const uint32_t end_mins) { + const DateTimePoint begin, + const DateTimePoint end) { std::vector results = get_time_range(condition); @@ -70,16 +69,16 @@ void TryConditionalRestrictions(const std::string& condition, EXPECT_EQ(res.type(), type); EXPECT_EQ(res.dow(), dow); - EXPECT_EQ(res.begin_month(), begin_month); - EXPECT_EQ(res.begin_day_dow(), begin_day); - EXPECT_EQ(res.begin_week(), begin_week); - EXPECT_EQ(res.begin_hrs(), begin_hrs); - EXPECT_EQ(res.begin_mins(), begin_mins); - EXPECT_EQ(res.end_month(), end_month); - EXPECT_EQ(res.end_day_dow(), end_day); - EXPECT_EQ(res.end_week(), end_week); - EXPECT_EQ(res.end_hrs(), end_hrs); - EXPECT_EQ(res.end_mins(), end_mins); + EXPECT_EQ(res.begin_month(), begin.month); + EXPECT_EQ(res.begin_day_dow(), begin.day); + EXPECT_EQ(res.begin_week(), begin.week); + EXPECT_EQ(res.begin_hrs(), begin.hour); + EXPECT_EQ(res.begin_mins(), begin.minute); + EXPECT_EQ(res.end_month(), end.month); + EXPECT_EQ(res.end_day_dow(), end.day); + EXPECT_EQ(res.end_week(), end.week); + EXPECT_EQ(res.end_hrs(), end.hour); + EXPECT_EQ(res.end_mins(), end.minute); if (::testing::Test::HasFailure()) { std::cerr << "Time domain: " << condition << std::endl; @@ -103,15 +102,15 @@ TEST(TimeParsing, TestConditionalRestrictions) { TryConditionalRestrictions(conditions.at(x), expected_values); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 62, 0, 0, 0, 6, 0, 0, 0, 0, 11, 0); - TryConditionalRestrictions(conditions.at(x), 1, 0, 62, 0, 0, 0, 17, 0, 0, 0, 0, 19, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 62, {0, 0, 0, 6, 0}, {0, 0, 0, 11, 0}); + TryConditionalRestrictions(conditions.at(x), 1, 0, 62, {0, 0, 0, 17, 0}, {0, 0, 0, 19, 0}); } else if (x == 1) { // Sa 03:30-19:00 std::vector expected_values; expected_values.push_back(40802435968); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 64, 0, 0, 0, 3, 30, 0, 0, 0, 19, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 64, {0, 0, 0, 3, 30}, {0, 0, 0, 19, 0}); } } @@ -124,14 +123,14 @@ TEST(TimeParsing, TestConditionalRestrictions) { std::vector expected_values; expected_values.push_back(38654708852); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 58, 0, 0, 0, 12, 0, 0, 0, 0, 18, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 58, {0, 0, 0, 12, 0}, {0, 0, 0, 18, 0}); } else if (x == 1) { // Sa-Su 12:00-17:00 std::vector expected_values; expected_values.push_back(36507225218); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 65, 0, 0, 0, 12, 0, 0, 0, 0, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 65, {0, 0, 0, 12, 0}, {0, 0, 0, 17, 0}); } } @@ -145,14 +144,14 @@ TEST(TimeParsing, TestConditionalRestrictions) { expected_values.push_back(1512971146104448); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 64, 7, 23, 0, 14, 0, 8, 21, 0, 20, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 64, {7, 23, 0, 14, 0}, {8, 21, 0, 20, 0}); } else if (x == 1) { // JUL 23-jUl 28 Fr,PH 10:00-20:00 std::vector expected_values; expected_values.push_back(2001154308835904); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 32, 7, 23, 0, 10, 0, 7, 28, 0, 20, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 32, {7, 23, 0, 10, 0}, {7, 28, 0, 20, 0}); } } @@ -172,7 +171,7 @@ TEST(TimeParsing, TestConditionalRestrictions) { expected_values.push_back(39610337987200); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 64, 4, 0, 0, 10, 0, 9, 0, 0, 13, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 64, {4, 0, 0, 10, 0}, {9, 0, 0, 13, 0}); } } @@ -192,7 +191,7 @@ TEST(TimeParsing, TestConditionalRestrictions) { expected_values.push_back(39610337987200); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 64, 4, 0, 0, 10, 0, 9, 0, 0, 13, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 64, {4, 0, 0, 10, 0}, {9, 0, 0, 13, 0}); } } @@ -206,8 +205,8 @@ TEST(TimeParsing, TestConditionalRestrictions) { expected_values.push_back(23622321664); expected_values.push_back(3133178646784); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 11, 0); - TryConditionalRestrictions(conditions.at(x), 1, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 19, 45); + TryConditionalRestrictions(conditions.at(x), 0, 0, 0, {0, 0, 0, 6, 0}, {0, 0, 0, 11, 0}); + TryConditionalRestrictions(conditions.at(x), 1, 0, 0, {0, 0, 0, 17, 0}, {0, 0, 0, 19, 45}); } } @@ -223,13 +222,13 @@ TEST(TimeParsing, TestConditionalRestrictions) { std::vector expected_values; expected_values.push_back(1106007905274112); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 0, 10, 16, 0, 9, 0, 11, 15, 0, 17, 30); + TryConditionalRestrictions(conditions.at(x), 0, 0, 0, {10, 16, 0, 9, 0}, {11, 15, 0, 17, 30}); } else if (x == 2) { // Nov 16-Feb 15: 09:00-16:30 std::vector expected_values; expected_values.push_back(1066423339714816); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 0, 11, 16, 0, 9, 0, 02, 15, 0, 16, 30); + TryConditionalRestrictions(conditions.at(x), 0, 0, 0, {11, 16, 0, 9, 0}, {02, 15, 0, 16, 30}); } } @@ -242,27 +241,27 @@ TEST(TimeParsing, TestConditionalRestrictions) { std::vector expected_values; expected_values.push_back(2078764173088); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 16, 0, 0, 0, 7, 0, 0, 0, 0, 8, 30); + TryConditionalRestrictions(conditions.at(x), 0, 0, 16, {0, 0, 0, 7, 0}, {0, 0, 0, 8, 30}); } else if (x == 1) { // th-friday 06:00-09:30 std::vector expected_values; expected_values.push_back(2080911656544); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 48, 0, 0, 0, 6, 0, 0, 0, 0, 9, 30); + TryConditionalRestrictions(conditions.at(x), 0, 0, 48, {0, 0, 0, 6, 0}, {0, 0, 0, 9, 30}); } else if (x == 2) { // May 15 09:00-11:30 std::vector expected_values; expected_values.push_back(1079606730295552); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 0, 5, 15, 0, 9, 0, 5, 15, 0, 11, 30); + TryConditionalRestrictions(conditions.at(x), 0, 0, 0, {5, 15, 0, 9, 0}, {5, 15, 0, 11, 30}); } else if (x == 3) { // May 07:00-08:30 std::vector expected_values; expected_values.push_back(24068999350016); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 0, 5, 0, 0, 7, 0, 5, 0, 0, 8, 30); + TryConditionalRestrictions(conditions.at(x), 0, 0, 0, {5, 0, 0, 7, 0}, {5, 0, 0, 8, 30}); } else if (x == 4) { // May 16-31 11:00-13:30 std::vector expected_values; expected_values.push_back(2205510940494592); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 0, 5, 16, 0, 11, 0, 5, 31, 0, 13, 30); + TryConditionalRestrictions(conditions.at(x), 0, 0, 0, {5, 16, 0, 11, 0}, {5, 31, 0, 13, 30}); } } @@ -275,16 +274,16 @@ TEST(TimeParsing, TestConditionalRestrictions) { expected_values.push_back(29856470044524); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 54, 9, 0, 0, 8, 15, 6, 0, 0, 8, 45); - TryConditionalRestrictions(conditions.at(x), 1, 0, 54, 9, 0, 0, 15, 20, 6, 0, 0, 15, 50); + TryConditionalRestrictions(conditions.at(x), 0, 0, 54, {9, 0, 0, 8, 15}, {6, 0, 0, 8, 45}); + TryConditionalRestrictions(conditions.at(x), 1, 0, 54, {9, 0, 0, 15, 20}, {6, 0, 0, 15, 50}); } else if (x == 1) { // Sep-Jun We 08:15-08:45,11:55-12:35 std::vector expected_values; expected_values.push_back(29497840232464); expected_values.push_back(28819235728144); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 8, 9, 0, 0, 8, 15, 6, 0, 0, 8, 45); - TryConditionalRestrictions(conditions.at(x), 1, 0, 8, 9, 0, 0, 11, 55, 6, 0, 0, 12, 35); + TryConditionalRestrictions(conditions.at(x), 0, 0, 8, {9, 0, 0, 8, 15}, {6, 0, 0, 8, 45}); + TryConditionalRestrictions(conditions.at(x), 1, 0, 8, {9, 0, 0, 11, 55}, {6, 0, 0, 12, 35}); } } @@ -297,7 +296,7 @@ TEST(TimeParsing, TestConditionalRestrictions) { std::vector expected_values; expected_values.push_back(9372272830712067); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 1, 10, 1, 5, 9, 0, 3, 5, 4, 16, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 1, {10, 1, 5, 9, 0}, {3, 5, 4, 16, 0}); } else if (x == 1) { // PH 09:00-16:00 Holidays are tossed for now std::vector expected_values; expected_values.push_back(0); @@ -306,7 +305,7 @@ TEST(TimeParsing, TestConditionalRestrictions) { std::vector expected_values; expected_values.push_back(11373388284561667); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 1, 3, 1, 5, 9, 0, 10, 1, 5, 18, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 1, {3, 1, 5, 9, 0}, {10, 1, 5, 18, 0}); } else if (x == 3) { // PH 09:00-18:00 Holidays are tossed for now std::vector expected_values; expected_values.push_back(0); @@ -323,14 +322,14 @@ TEST(TimeParsing, TestConditionalRestrictions) { expected_values.push_back(7252416602836867); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 65, 12, 6, 5, 9, 0, 1, 7, 3, 16, 0); - TryConditionalRestrictions(conditions.at(x), 1, 1, 65, 12, 6, 5, 15, 0, 1, 7, 3, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 65, {12, 6, 5, 9, 0}, {1, 7, 3, 16, 0}); + TryConditionalRestrictions(conditions.at(x), 1, 1, 65, {12, 6, 5, 15, 0}, {1, 7, 3, 17, 0}); } else if (x == 1) { // Dec Su[-1] Su-Sa 15:00-17:00 std::vector expected_values; expected_values.push_back(11311813490642943); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 127, 12, 1, 5, 15, 0, 12, 0, 5, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 127, {12, 1, 5, 15, 0}, {12, 0, 5, 17, 0}); } } @@ -344,62 +343,62 @@ TEST(TimeParsing, TestConditionalRestrictions) { std::vector expected_values; expected_values.push_back(34359740674); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 1, 0, 0, 0, 9, 0, 0, 0, 0, 16, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 1, {0, 0, 0, 9, 0}, {0, 0, 0, 16, 0}); } else if (x == 1) { // Su[1] std::vector expected_values; expected_values.push_back(268435459); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 1, {0, 0, 1, 0, 0}, {0, 0, 0, 0, 0}); } else if (x == 2) { // Dec std::vector expected_values; expected_values.push_back(52776564424704); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 0, 12, 0, 0, 0, 0, 12, 0, 0, 0, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 0, {12, 0, 0, 0, 0}, {12, 0, 0, 0, 0}); } else if (x == 3) { // Dec Su[-1] 15:00-17:00 std::vector expected_values; expected_values.push_back(11311813490642943); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 127, 12, 1, 5, 15, 0, 12, 0, 5, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 127, {12, 1, 5, 15, 0}, {12, 0, 5, 17, 0}); } else if (x == 4) { // Dec Su[-1] Th 15:00-17:00 std::vector expected_values; expected_values.push_back(11311813490642721); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 16, 12, 1, 5, 15, 0, 12, 0, 5, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 16, {12, 1, 5, 15, 0}, {12, 0, 5, 17, 0}); } else if (x == 5) { // Dec Su[-1] std::vector expected_values; expected_values.push_back(11311776983417087); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 127, 12, 1, 5, 0, 0, 12, 0, 5, 0, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 127, {12, 1, 5, 0, 0}, {12, 0, 5, 0, 0}); } else if (x == 6) { // Dec Su[-1]-Mar 3 Sat std::vector expected_values; expected_values.push_back(224301728923777); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 64, 12, 1, 5, 0, 0, 3, 3, 0, 0, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 64, {12, 1, 5, 0, 0}, {3, 3, 0, 0, 0}); } else if (x == 7) { // Mar 3-Dec Su[-1] Sat std::vector expected_values; expected_values.push_back(11382144397475969); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 64, 3, 3, 0, 0, 0, 12, 1, 5, 0, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 64, {3, 3, 0, 0, 0}, {12, 1, 5, 0, 0}); } else if (x == 8) { // Dec Su[-1]-Mar 3 Sat 15:00-17:00 std::vector expected_values; expected_values.push_back(224338236149633); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 64, 12, 1, 5, 15, 0, 3, 3, 0, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 64, {12, 1, 5, 15, 0}, {3, 3, 0, 17, 0}); } else if (x == 9) { // Mar 3-Dec Su[-1] Sat 15:00-17:00 std::vector expected_values; expected_values.push_back(11382180904701825); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 64, 3, 3, 0, 15, 0, 12, 1, 5, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 64, {3, 3, 0, 15, 0}, {12, 1, 5, 17, 0}); } else if (x == 10) { // Mar 3-Dec Su[-1] Sat,PH 15:00-17:00 std::vector expected_values; expected_values.push_back(11382180904701825); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 64, 3, 3, 0, 15, 0, 12, 1, 5, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 64, {3, 3, 0, 15, 0}, {12, 1, 5, 17, 0}); } else if (x == 11) { // Mar 3-Dec Su[-1] PH,Sat 15:00-17:00 std::vector expected_values; expected_values.push_back(11382180904701825); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 64, 3, 3, 0, 15, 0, 12, 1, 5, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 64, {3, 3, 0, 15, 0}, {12, 1, 5, 17, 0}); } } @@ -408,13 +407,13 @@ TEST(TimeParsing, TestConditionalRestrictions) { for (uint32_t x = 0; x < conditions.size(); x++) { if (x == 0) { TryConditionalRestrictions(conditions.at(x), {4}); - TryConditionalRestrictions(conditions.at(x), 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 2, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}); } else if (x == 1) { TryConditionalRestrictions(conditions.at(x), {16}); - TryConditionalRestrictions(conditions.at(x), 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 8, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}); } else if (x == 2 || x == 3) { TryConditionalRestrictions(conditions.at(x), {64}); - TryConditionalRestrictions(conditions.at(x), 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 32, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}); } } @@ -428,8 +427,8 @@ TEST(TimeParsing, TestConditionalRestrictions) { conditions = GetTagTokens(str, ';'); for (uint32_t x = 0; x < conditions.size(); x++) { TryConditionalRestrictions(conditions.at(x), {2080911656828, 32212258172}); - TryConditionalRestrictions(conditions.at(x), 0, 0, 62, 0, 0, 0, 7, 0, 0, 0, 0, 9, 30); - TryConditionalRestrictions(conditions.at(x), 1, 0, 62, 0, 0, 0, 13, 0, 0, 0, 0, 15, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 62, {0, 0, 0, 7, 0}, {0, 0, 0, 9, 30}); + TryConditionalRestrictions(conditions.at(x), 1, 0, 62, {0, 0, 0, 13, 0}, {0, 0, 0, 15, 0}); } // includes end of year @@ -461,6 +460,79 @@ TEST(TimeParsing, TestConditionalRestrictions) { } } +// A test case with exotic conditions extracted from `maxspeed:conditional` OSM field. +TEST(TimeParsing, TestConditionalMaxspeed) { + TryConditionalRestrictions("(19:00-06:00)", 0, 0, 0, {0, 0, 0, 19, 0}, {0, 0, 0, 6, 0}); + TryConditionalRestrictions("(06:00-18:00)", 0, 0, 0, {0, 0, 0, 6, 0}, {0, 0, 0, 18, 0}); + + std::string condition = "(07:00-09:00,13:00-16:00; SH off)"; + ASSERT_EQ(get_time_range(condition).size(), 2); + TryConditionalRestrictions(condition, 0, 0, 0, {0, 0, 0, 7, 0}, {0, 0, 0, 9, 0}); + TryConditionalRestrictions(condition, 1, 0, 0, {0, 0, 0, 13, 0}, {0, 0, 0, 16, 0}); + + TryConditionalRestrictions("Mo-Fr 19:00-07:00,Sa,Su", 0, 0, 62, {0, 0, 0, 19, 0}, {0, 0, 0, 7, 0}); + + condition = "Mo-Fr 06:00-10:00,15:00-19:00 "bij grote verkeersdrukte""; + ASSERT_EQ(get_time_range(condition).size(), 2); + TryConditionalRestrictions(condition, 0, 0, 62, {0, 0, 0, 6, 0}, {0, 0, 0, 10, 0}); + TryConditionalRestrictions(condition, 1, 0, 62, {0, 0, 0, 15, 0}, {0, 0, 0, 19, 0}); + + condition = "(Mo, We, Th, Sa 07:00-15:00)"; + ASSERT_EQ(get_time_range(condition).size(), 1); + TryConditionalRestrictions(condition, 0, 0, 0b01011010, {0, 0, 0, 7, 0}, {0, 0, 0, 15, 0}); + + condition = "(Mo-Sa 07:00-20:00,07:00-20:00; Su 00:00-24:00; PH 00:00-24:00)"; + { + const auto conditions = GetTagTokens(condition, ';'); + ASSERT_EQ(conditions.size(), 3); + + ASSERT_EQ(get_time_range(conditions[0]).size(), 2); + TryConditionalRestrictions(conditions[0], 0, 0, 126, {0, 0, 0, 7, 0}, {0, 0, 0, 20, 0}); + TryConditionalRestrictions(conditions[0], 1, 0, 126, {0, 0, 0, 7, 0}, {0, 0, 0, 20, 0}); + + ASSERT_EQ(get_time_range(conditions[1]).size(), 1); + TryConditionalRestrictions(conditions[1], 0, 0, 1, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}); + + EXPECT_EQ(get_time_range(conditions[2]).size(), 0); + } + + TryConditionalRestrictions("Jun-Aug", 0, 0, 0, {6, 0, 0, 0, 0}, {8, 0, 0, 0, 0}); + TryConditionalRestrictions("(Nov - Mar)", 0, 0, 0, {11, 0, 0, 0, 0}, {3, 0, 0, 0, 0}); + + TryConditionalRestrictions("(Apr 15-Oct 15 00:00-24:00)", 0, 0, 0, {4, 15, 0, 0, 0}, + {10, 15, 0, 0, 0}); + TryConditionalRestrictions("(Aug 01-Jun 30 06:00-18:00)", 0, 0, 0, {8, 1, 0, 6, 0}, + {6, 30, 0, 18, 0}); + TryConditionalRestrictions("(Aug 01-Jun 30 Mo-Fr 07:00-17:00; PH -1 day off; PH off)", 0, 0, 62, + {8, 1, 0, 7, 0}, {6, 30, 0, 17, 0}); + + condition = + "(Jan 01-Jun 15 Mo-Fr 07:00-18:00; PH -1 day off; PH off; Aug 15-Dec 31 Mo-Fr 00:00-24:00; PH -1 day off; PH off)"; + { + const auto conditions = GetTagTokens(condition, ';'); + ASSERT_EQ(conditions.size(), 6); + + ASSERT_EQ(get_time_range(conditions[0]).size(), 1); + TryConditionalRestrictions(conditions[0], 0, 0, 62, {1, 1, 0, 7, 0}, {6, 15, 0, 18, 0}); + + ASSERT_EQ(get_time_range(conditions[1]).size(), 0); + ASSERT_EQ(get_time_range(conditions[2]).size(), 0); + + ASSERT_EQ(get_time_range(conditions[3]).size(), 1); + TryConditionalRestrictions(conditions[3], 0, 0, 62, {8, 15, 0, 0, 0}, {12, 31, 0, 0, 0}); + + ASSERT_EQ(get_time_range(conditions[4]).size(), 0); + ASSERT_EQ(get_time_range(conditions[5]).size(), 0); + } + + TryConditionalRestrictions("(Jun 1-Aug 31 00:00-24:00)", 0, 0, 0, {6, 1, 0, 0, 0}, + {8, 31, 0, 0, 0}); + + // non-standard seasons that are not supported + EXPECT_TRUE(get_time_range("summer").empty()); + EXPECT_TRUE(get_time_range("winter").empty()); +} + int main(int argc, char* argv[]) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/test/trivial_paths.cc b/test/trivial_paths.cc index b9e55ba684..b09aa0561f 100644 --- a/test/trivial_paths.cc +++ b/test/trivial_paths.cc @@ -1,8 +1,6 @@ #include "test.h" -#include #include -#include #include "loki/worker.h" #include "midgard/logging.h" @@ -69,7 +67,7 @@ void adjust_scores(Options& options) { } } -const auto config = test::make_config("test/data/utrecht_tiles"); +const auto cfg = test::make_config("test/data/utrecht_tiles"); void try_path(GraphReader& reader, loki_worker_t& loki_worker, @@ -97,15 +95,15 @@ void try_path(GraphReader& reader, TEST(TrivialPaths, test_trivial_paths) { // Test setup - loki_worker_t loki_worker(config); - GraphReader reader(config.get_child("mjolnir")); + loki_worker_t loki_worker(cfg); + GraphReader reader(cfg.get_child("mjolnir")); - // Simple path along oneway edge in the driveable direction - should return a single edge + // Simple path along oneway edge in the drivable direction - should return a single edge const auto test_request1 = R"({"locations":[{"lat":52.079079,"lon":5.115197}, {"lat":52.078937,"lon":5.115321}],"costing":"auto"})"; try_path(reader, loki_worker, test_request1, 1); - // Simple path along oneway edge opposing the driveable direction -must not + // Simple path along oneway edge opposing the drivable direction -must not // return a single edge (edge count is 10) const auto test_request2 = R"({"locations":[{"lat":52.078937,"lon":5.115321}, {"lat":52.079079,"lon":5.115197}],"costing":"auto"})"; @@ -117,13 +115,13 @@ TEST(TrivialPaths, test_trivial_paths) { try_path(reader, loki_worker, test_request3, 1); // Simple path along two way edge (opposite direction to request 3) - should edge opposing the - // driveable direction -must not return a single edge + // drivable direction -must not return a single edge const auto test_request4 = R"({"locations":[{"lat":52.078882,"lon":5.1104848}, {"lat":52.0785070,"lon":5.110835}],"costing":"auto"})"; try_path(reader, loki_worker, test_request4, 1); // Test avoidance of parking aisles. Path should avoid the shortcut via a parking aisle. - // driveable direction -must not return a single edge + // drivable direction -must not return a single edge const auto test_request5 = R"({"locations":[{"lat":52.072534,"lon":5.125980}, {"lat":52.072862,"lon":5.124025}],"costing":"auto"})"; try_path(reader, loki_worker, test_request5, 5); diff --git a/test/turn.cc b/test/turn.cc index d962c370f9..49a0cddf8a 100644 --- a/test/turn.cc +++ b/test/turn.cc @@ -1,9 +1,5 @@ -#include - #include "baldr/turn.h" -#include - #include "test.h" using namespace std; diff --git a/test/turnlanes.cc b/test/turnlanes.cc index 46620a23ea..c9ab3594f4 100644 --- a/test/turnlanes.cc +++ b/test/turnlanes.cc @@ -1,8 +1,6 @@ #include #include #include -#include -#include #include #include diff --git a/test/urban.cc b/test/urban.cc index 8049aacacd..f6f6720a3a 100644 --- a/test/urban.cc +++ b/test/urban.cc @@ -1,7 +1,6 @@ #include "test.h" #include -#include #include "baldr/graphreader.h" #include "baldr/rapidjson_utils.h" diff --git a/test/util_midgard.cc b/test/util_midgard.cc index 920a4b6758..934554b819 100644 --- a/test/util_midgard.cc +++ b/test/util_midgard.cc @@ -27,7 +27,7 @@ TEST(UtilMidgard, TestRangedDefaultT) { for (unsigned i = 0; i < 100; ++i) { ranged_default_t testRange{lower, defaultDistributor(generator), upper}; - float defaultVal = testRange.def; + float testVal = testDistributor(generator); float finalVal = testRange(testVal); @@ -198,7 +198,6 @@ TEST(UtilMidgard, TestResample) { auto length = pl.Length(); resampled = resample_polyline(input_shape, length, resolution); size_t n = std::round(length / resolution); - float sample_distance = length / n; EXPECT_EQ(resampled.size(), n + 1) << "resample_polyline - Sampled polyline is not the expected length"; } @@ -270,15 +269,15 @@ TEST(UtilMidgard, TestIterable) { sum += i; EXPECT_EQ(sum, 15) << "integer array sum failed"; - std::string concatinated; + std::string concatenated; for (const auto& i : iterable_t(b, 5)) - concatinated.push_back(i); - EXPECT_EQ(concatinated, "abcde") << "char concatenation failed"; + concatenated.push_back(i); + EXPECT_EQ(concatenated, "abcde") << "char concatenation failed"; - concatinated = ""; + concatenated = ""; for (const auto& i : iterable_t(c, 5)) - concatinated.append(i); - EXPECT_EQ(concatinated, "onetwothreefourfive") << "string concatenation failed"; + concatenated.append(i); + EXPECT_EQ(concatenated, "onetwothreefourfive") << "string concatenation failed"; size_t cumulative_product = 1; iterable_t iterable(d, 5); @@ -416,7 +415,8 @@ TEST(UtilMidgard, TestTrimPolylineWithFloatGeoPoint) { // Worst case is they may quantized at 1.69m intervals (for an epsilon change). // https://stackoverflow.com/a/28420164 // The length comparisons below do better than that, but not a lot. - constexpr double MAX_FLOAT_PRECISION = 0.05; // Should be good for 5cm at this lon/lat + constexpr double MAX_FLOAT_PRECISION = 0.07; // Should be good for 5cm at this lon/lat, + // also account for some float point inaccuracies auto clip = trim_polyline(line.begin(), line.end(), 0.f, 1.f); EXPECT_DOUBLE_EQ(length(clip.begin(), clip.end()), length(line.begin(), line.end())) @@ -649,7 +649,7 @@ TEST(UtilMidgard, TestExpandLocation) { EXPECT_GE(area, 199.0f * 199.0f); // Should throw an exception if negative value is sent - EXPECT_THROW(AABB2 box = ExpandMeters(loc, -10.0f);, std::invalid_argument) + EXPECT_THROW(ExpandMeters(loc, -10.0f);, std::invalid_argument) << "ExpandLocation: should throw exception with negative meters supplied"; } diff --git a/test/util_mjolnir.cc b/test/util_mjolnir.cc index 82ca6a5530..7d7581c0b2 100644 --- a/test/util_mjolnir.cc +++ b/test/util_mjolnir.cc @@ -1,12 +1,10 @@ -#include +#include #include -#include #include #include "baldr/graphid.h" #include "baldr/rapidjson_utils.h" -#include "filesystem.h" #include "mjolnir/util.h" #include "test.h" @@ -36,8 +34,8 @@ TEST(UtilMjolnir, BuildTileSet) { EXPECT_TRUE(build_tile_set(config, {VALHALLA_SOURCE_DIR "test/data/harrisburg.osm.pbf"}, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kCleanup)); // Clear the tile directory so it doesn't interfere with the next test. - filesystem::remove_all(tile_dir); - EXPECT_TRUE(!filesystem::exists(tile_dir)); + std::filesystem::remove_all(tile_dir); + EXPECT_TRUE(!std::filesystem::exists(tile_dir)); } TEST(UtilMjolnir, TileManifestReadFromFile) { diff --git a/test/utrecht.cc b/test/utrecht.cc index 0db6901bec..6c3ff96ab0 100644 --- a/test/utrecht.cc +++ b/test/utrecht.cc @@ -1,11 +1,11 @@ -#include "filesystem.h" +#include + #include "midgard/sequence.h" #include "mjolnir/osmnode.h" #include "mjolnir/pbfgraphparser.h" #include #include -#include #include "baldr/directededge.h" #include "baldr/graphconstants.h" @@ -25,21 +25,10 @@ namespace { std::string ways_file = "test_ways_utrecht.bin"; std::string way_nodes_file = "test_way_nodes_utrecht.bin"; std::string access_file = "test_access_utrecht.bin"; -std::string pronunciation_file = "test_pronunciation_utrecht.bin"; std::string from_restriction_file = "test_from_complex_restrictions_utrecht.bin"; std::string to_restriction_file = "test_to_complex_restrictions_utrecht.bin"; std::string bss_file = "test_bss_nodes_utrecht.bin"; - -const auto node_predicate = [](const OSMWayNode& a, const OSMWayNode& b) { - return a.node.osmid_ < b.node.osmid_; -}; - -OSMNode GetNode(uint64_t node_id, sequence& way_nodes) { - auto found = way_nodes.find({node_id}, node_predicate); - if (found == way_nodes.end()) - throw std::runtime_error("Couldn't find node: " + std::to_string(node_id)); - return (*found).node; -} +std::string linguistic_node_file = "test_linguistic_node_utrecht.bin"; auto way_predicate = [](const OSMWay& a, const OSMWay& b) { return a.osmwayid_ < b.osmwayid_; }; @@ -221,7 +210,7 @@ class UtrecthTestSuiteEnv : public ::testing::Environment { auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/utrecht_netherlands.osm.pbf"}, - ways_file, way_nodes_file, access_file, pronunciation_file); + ways_file, way_nodes_file, access_file); PBFGraphParser::ParseRelations(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/utrecht_netherlands.osm.pbf"}, @@ -229,16 +218,17 @@ class UtrecthTestSuiteEnv : public ::testing::Environment { PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/utrecht_netherlands.osm.pbf"}, - way_nodes_file, bss_file, osmdata); + way_nodes_file, bss_file, linguistic_node_file, osmdata); } void TearDown() override { - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(bss_file); + std::filesystem::remove(ways_file); + std::filesystem::remove(way_nodes_file); + std::filesystem::remove(access_file); + std::filesystem::remove(from_restriction_file); + std::filesystem::remove(to_restriction_file); + std::filesystem::remove(bss_file); + std::filesystem::remove(linguistic_node_file); } }; diff --git a/test/vector2.cc b/test/vector2.cc index e3f6693934..e0aeca2f30 100644 --- a/test/vector2.cc +++ b/test/vector2.cc @@ -45,7 +45,7 @@ TEST(Vector2, TestCtorPoint2Point2) { } void TryCtorVector2(const Vector2& v, const Vector2& expected) { - Vector2 result(v); + const Vector2& result(v); EXPECT_EQ(expected, result); } @@ -187,7 +187,7 @@ void TryOpMultiplication(const Vector2& v, const float scalar, const Vector2& ex EXPECT_EQ(expected, result) << "scalar pre"; Vector2 result2 = scalar * v; - EXPECT_EQ(expected, result) << "scalar post"; + EXPECT_EQ(expected, result2) << "scalar post"; } TEST(Vector2, TestOpMultiplication) { diff --git a/test/viterbi_search.cc b/test/viterbi_search.cc index ce8e273f18..e45ef59ce7 100644 --- a/test/viterbi_search.cc +++ b/test/viterbi_search.cc @@ -68,8 +68,7 @@ template void print_path(iterator_t rbegin, iterator_t ren void AddColumns(IViterbiSearch& vs, const std::vector& columns) { StateId::Time time = 0; for (const auto& column : columns) { - uint32_t idx = 0; - for (const auto& state : column) { + for (uint32_t idx = 0; idx < column.size(); ++idx) { StateId stateid(time, idx); const auto added = vs.AddStateId(stateid); @@ -77,7 +76,6 @@ void AddColumns(IViterbiSearch& vs, const std::vector& columns) { << " must be added"; ASSERT_TRUE(vs.HasStateId(stateid)) << "must contain it"; - idx++; } time++; } @@ -338,7 +336,6 @@ class TransitionCostModel { float operator()(const StateId& lhs, const StateId& rhs) const { const auto& left = get_state(columns_, lhs); - const auto& right = get_state(columns_, rhs); const auto it = left.transition_costs.find(rhs.id()); if (it == left.transition_costs.end()) { return -1.0; @@ -369,7 +366,7 @@ std::vector sort_all_paths(const std::vector& columns, const auto& sub_pcs = sort_all_paths(columns, since_time + 1); std::vector pcs; - for (auto id = 0; id < columns[since_time].size(); id++) { + for (size_t id = 0; id < columns[since_time].size(); id++) { const StateId stateid(since_time, id); const auto& state = get_state(columns, stateid); for (const auto& sub_pc : sub_pcs) { diff --git a/test/win/valhalla.json b/test/win/valhalla.json index aaca958dba..ab339069f8 100644 --- a/test/win/valhalla.json +++ b/test/win/valhalla.json @@ -100,6 +100,7 @@ }, "mjolnir": { "admin": "test\\data\\netherlands_admin.sqlite", + "landmarks": "test\\data\\landmarks.sqlite", "hierarchy": true, "include_driveways": true, "logging": { diff --git a/third_party/benchmark b/third_party/benchmark deleted file mode 160000 index e991355c02..0000000000 --- a/third_party/benchmark +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e991355c02b93fe17713efe04cbc2e278e00fdbd diff --git a/third_party/cpp-statsd-client b/third_party/cpp-statsd-client index 8cc00d0bba..b60bc7bb93 160000 --- a/third_party/cpp-statsd-client +++ b/third_party/cpp-statsd-client @@ -1 +1 @@ -Subproject commit 8cc00d0bba3f53c1966fe4e199d032c9c183048d +Subproject commit b60bc7bb930df664b4ab9573ad8bee80d29dc72d diff --git a/third_party/cxxopts b/third_party/cxxopts index 302302b308..eb787304d6 160000 --- a/third_party/cxxopts +++ b/third_party/cxxopts @@ -1 +1 @@ -Subproject commit 302302b30839505703d37fb82f536c53cf9172fa +Subproject commit eb787304d67ec22f7c3a184ee8b4c481d04357fd diff --git a/third_party/fastcov b/third_party/fastcov index 1128b0f828..40dffe81d6 160000 --- a/third_party/fastcov +++ b/third_party/fastcov @@ -1 +1 @@ -Subproject commit 1128b0f82898df32381a7f7e47a54023a0711e5b +Subproject commit 40dffe81d62c0d897afe4108f3b5489487ff3bce diff --git a/third_party/googletest b/third_party/googletest index e2239ee604..b796f7d446 160000 --- a/third_party/googletest +++ b/third_party/googletest @@ -1 +1 @@ -Subproject commit e2239ee6043f73722e7aa812a459f54a28552929 +Subproject commit b796f7d44681514f58a683a3a71ff17c94edb0c1 diff --git a/third_party/just_gtfs b/third_party/just_gtfs index 94b35eea71..375be1cce2 160000 --- a/third_party/just_gtfs +++ b/third_party/just_gtfs @@ -1 +1 @@ -Subproject commit 94b35eea71c09aefccf994c28ee7d373de4460e6 +Subproject commit 375be1cce2280baea3464019d3b19ddd4e3a4e5a diff --git a/third_party/libosmium b/third_party/libosmium index 9c50fde428..a44ae7be0f 160000 --- a/third_party/libosmium +++ b/third_party/libosmium @@ -1 +1 @@ -Subproject commit 9c50fde42843dbfb1df0394164d578cda2c3b82e +Subproject commit a44ae7be0fc161dc4b110c01fa176cfe5bf45503 diff --git a/third_party/lz4 b/third_party/lz4 deleted file mode 160000 index d44371841a..0000000000 --- a/third_party/lz4 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d44371841a2f1728a3f36839fd4b7e872d0927d3 diff --git a/third_party/protozero b/third_party/protozero index 7487f8109a..f379578a3f 160000 --- a/third_party/protozero +++ b/third_party/protozero @@ -1 +1 @@ -Subproject commit 7487f8109acc8637b48e3cb291784cd58f943a1c +Subproject commit f379578a3f7c8162aac0ac31c2696de09a5b5f93 diff --git a/third_party/pybind11 b/third_party/pybind11 index 5b0a6fc201..8a099e44b3 160000 --- a/third_party/pybind11 +++ b/third_party/pybind11 @@ -1 +1 @@ -Subproject commit 5b0a6fc2017fcc176545afe3e09c9f9885283242 +Subproject commit 8a099e44b3d5f85b20f05828d919d2332a8de841 diff --git a/third_party/robin-hood-hashing b/third_party/robin-hood-hashing index f2cae2e6b8..9145f963d8 160000 --- a/third_party/robin-hood-hashing +++ b/third_party/robin-hood-hashing @@ -1 +1 @@ -Subproject commit f2cae2e6b8231c46fe9c324e31b0a1f64e0de8c6 +Subproject commit 9145f963d80d6a02f0f96a47758050a89184a3ed diff --git a/third_party/tz b/third_party/tz new file mode 160000 index 0000000000..380c07cef0 --- /dev/null +++ b/third_party/tz @@ -0,0 +1 @@ +Subproject commit 380c07cef01c71c1f93e9709d9f8c79b91cff063 diff --git a/valhalla/baldr/accessrestriction.h b/valhalla/baldr/accessrestriction.h index 33050a8b9e..672bfe77e3 100644 --- a/valhalla/baldr/accessrestriction.h +++ b/valhalla/baldr/accessrestriction.h @@ -3,6 +3,7 @@ #include #include +#include namespace valhalla { namespace baldr { @@ -38,7 +39,7 @@ class AccessRestriction { /** * Get the modes impacted by access restriction. - * @return Returns a bit field of affected modes. + * @return Returns a bit mask of affected modes. */ uint32_t modes() const; @@ -54,6 +55,8 @@ class AccessRestriction { */ void set_value(const uint64_t v); + const json::MapPtr json() const; + /** * operator < - for sorting. Sort by edge Id. * @param other Other access restriction to compare to. diff --git a/valhalla/baldr/admininfo.h b/valhalla/baldr/admininfo.h index 75ef99b064..0933da7d18 100644 --- a/valhalla/baldr/admininfo.h +++ b/valhalla/baldr/admininfo.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_BALDR_ADMININFO_H_ #define VALHALLA_BALDR_ADMININFO_H_ -#include #include namespace valhalla { diff --git a/valhalla/baldr/attributes_controller.h b/valhalla/baldr/attributes_controller.h index 8e8724369c..4b93d5789e 100644 --- a/valhalla/baldr/attributes_controller.h +++ b/valhalla/baldr/attributes_controller.h @@ -59,6 +59,7 @@ const std::string kEdgeWeightedGrade = "edge.weighted_grade"; const std::string kEdgeMaxUpwardGrade = "edge.max_upward_grade"; const std::string kEdgeMaxDownwardGrade = "edge.max_downward_grade"; const std::string kEdgeMeanElevation = "edge.mean_elevation"; +const std::string kEdgeElevation = "edge.elevation"; const std::string kEdgeLaneCount = "edge.lane_count"; const std::string kEdgeLaneConnectivity = "edge.lane_connectivity"; const std::string kEdgeCycleLane = "edge.cycle_lane"; @@ -75,6 +76,8 @@ const std::string kEdgeDestinationOnly = "edge.destination_only"; const std::string kEdgeIsUrban = "edge.is_urban"; const std::string kEdgeTaggedValues = "edge.tagged_values"; const std::string kEdgeIndoor = "edge.indoor"; +const std::string kEdgeLandmarks = "edge.landmarks"; +const std::string kEdgeCountryCrossing = "edge.country_crossing"; // Node keys const std::string kNodeIntersectingEdgeBeginHeading = "node.intersecting_edge.begin_heading"; diff --git a/valhalla/baldr/complexrestriction.h b/valhalla/baldr/complexrestriction.h index 4c6723473a..399a3280e8 100644 --- a/valhalla/baldr/complexrestriction.h +++ b/valhalla/baldr/complexrestriction.h @@ -2,10 +2,6 @@ #define VALHALLA_BALDR_COMPLEXRESTRICTION_H_ #include -#include -#include -#include -#include #include #include diff --git a/valhalla/baldr/connectivity_map.h b/valhalla/baldr/connectivity_map.h index de6f0bdd22..acde7ddb86 100644 --- a/valhalla/baldr/connectivity_map.h +++ b/valhalla/baldr/connectivity_map.h @@ -42,7 +42,7 @@ class connectivity_map_t { /** * Returns the colors for the given level,point,radius * - * @param hierarchy_level the hierarchy level whos connectivity you are querying + * @param hierarchy_level the hierarchy level whose connectivity you are querying * @param location the center of the circle * @param radius the radius of the circle * @return colors the colors of the tiles that intersect this circle at this level @@ -54,7 +54,7 @@ class connectivity_map_t { /** * Returns the geojson representing the connectivity map * - * @param hierarchy_level the hierarchy level whos connectivity you want to see + * @param hierarchy_level the hierarchy level whose connectivity you want to see * @return string the geojson */ std::string to_geojson(const uint32_t hierarchy_level) const; @@ -62,7 +62,7 @@ class connectivity_map_t { /** * Returns the vector of colors (one per tile) representing the connectivity map * - * @param hierarchy_level the hierarchy level whos connectivity you want to see + * @param hierarchy_level the hierarchy level whose connectivity you want to see * @return vector the vector of colors per tile */ std::vector to_image(const uint32_t hierarchy_level) const; diff --git a/valhalla/baldr/curler.h b/valhalla/baldr/curler.h index 70f2da1cb5..06d5d03fa1 100644 --- a/valhalla/baldr/curler.h +++ b/valhalla/baldr/curler.h @@ -2,7 +2,6 @@ #include -#include #include #include #include diff --git a/valhalla/baldr/datetime.h b/valhalla/baldr/datetime.h index ebb3fee595..8080e5a434 100644 --- a/valhalla/baldr/datetime.h +++ b/valhalla/baldr/datetime.h @@ -4,9 +4,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -46,8 +44,21 @@ struct tz_db_t { const date::time_zone* from_index(size_t index) const; protected: - std::unordered_map names; - const date::tzdb& db; + std::unordered_map zones; +}; + +struct dt_info_t { + dt_info_t() { + } + dt_info_t(const std::string& dt, const std::string& tzo, const std::string& tzn) + : + + date_time(dt), time_zone_offset(tzo), time_zone_name(tzn) { + } + + std::string date_time; + std::string time_zone_offset; + std::string time_zone_name; }; /** @@ -84,7 +95,7 @@ uint32_t days_from_pivot_date(const date::local_seconds& seconds); /** * Get the iso date and time from the current date and time. * @param time_zone Timezone. - * @return Returns the formated date 2015-05-06. + * @return Returns the formatted date 2015-05-06. */ std::string iso_date_time(const date::time_zone* time_zone); @@ -138,7 +149,7 @@ void seconds_to_date(const uint64_t origin_seconds, /** * Get utc formatted timestamp, skip using zoned times since we have utc special case * @param seconds since epoch in UTC zone - * @return formated string like: 2020-08-12T14:17:09Z + * @return formatted string like: 2020-08-12T14:17:09Z */ inline std::string seconds_to_date_utc(const uint64_t seconds) { std::stringstream ss; @@ -209,6 +220,20 @@ bool is_conditional_active(const bool type, */ uint32_t second_of_week(uint32_t epoch_time, const date::time_zone* time_zone); +/** + * Offset a time by some number of seconds, optionally taking into account timezones at the origin & + * destination. + * + * @param in_dt the input date time string + * @param in_tz the start timezone + * @param out_tz the end timezone + * @param offset the offset in seconds from the input date time string + * @return out_dt a struct containing the time, UTC offset and timezone name at the out_edge in + * local time after the offset is applied to the in_dt + */ +dt_info_t +offset_date(const std::string& in_dt, const uint32_t in_tz, const uint32_t out_tz, float offset); + /** * Convert ISO 8601 time into std::tm. * @param iso ISO time string (YYYY-mm-ddTHH:MM) diff --git a/valhalla/baldr/directededge.h b/valhalla/baldr/directededge.h index 1ec48189b0..f3b7747bba 100644 --- a/valhalla/baldr/directededge.h +++ b/valhalla/baldr/directededge.h @@ -237,7 +237,7 @@ class DirectedEdge { void set_curvature(const uint32_t factor); /** - * Flag indicating the edge is a dead end (no other driveable + * Flag indicating the edge is a dead end (no other drivable * roads at the end node of this edge). * @return Returns true if this edge is a dead end. */ @@ -246,7 +246,7 @@ class DirectedEdge { } /** - * Set the flag indicating the edge is a dead end (no other driveable + * Set the flag indicating the edge is a dead end (no other drivable * roads at the end node of this edge). * @param d True if this edge is a dead end. */ @@ -299,6 +299,24 @@ class DirectedEdge { */ void set_dest_only(const bool destonly); + /** + * Is this edge part of a private or no through road for HGV that allows access + * only if required to get to a destination? If destonly() is true, this is true. + * @return Returns true if the edge is destination only / private access for HGV. + */ + bool destonly_hgv() const { + return dest_only_hgv_; + } + + /** + * Sets the destination only (private) flag for HGV. This indicates the + * edge should allow access only to locations that are destinations and + * not allow HGV "through" traffic + * @param destonly_hgv True if the edge is private for HGV (allows access to + * destination only), false if not. + */ + void set_dest_only_hgv(const bool destonly_hgv); + /** * Is this edge part of a tunnel? * @return Returns true if this edge is part of a tunnel, false if not. @@ -862,16 +880,16 @@ class DirectedEdge { void set_dismount(const bool dismount); /** - * Get if a sidepath for bicycling should be preffered instead of this edge - * @return Returns if a sidepath should be preffered for cycling + * Get if a sidepath for bicycling should be preferred instead of this edge + * @return Returns if a sidepath should be preferred for cycling */ bool use_sidepath() const { return use_sidepath_; } /** - * Set if a sidepath for bicycling should be preffered instead of this edge - * @param use_sidepath true if sidepath should be preffered for cycling over this edge + * Set if a sidepath for bicycling should be preferred instead of this edge + * @param use_sidepath true if sidepath should be preferred for cycling over this edge */ void set_use_sidepath(const bool use_sidepath); @@ -1234,14 +1252,15 @@ class DirectedEdge { uint64_t bridge_ : 1; // Is this edge part of a bridge? uint64_t traffic_signal_ : 1; // Traffic signal at end of the directed edge uint64_t seasonal_ : 1; // Seasonal access (ex. no access in winter) - uint64_t deadend_ : 1; // Leads to a dead-end (no other driveable roads) TODO + uint64_t deadend_ : 1; // Leads to a dead-end (no other drivable roads) TODO uint64_t bss_connection_ : 1; // Does this lead to(come out from) a bike share station? uint64_t stop_sign_ : 1; // Stop sign at end of the directed edge uint64_t yield_sign_ : 1; // Yield/give way sign at end of the directed edge uint64_t hov_type_ : 1; // if (is_hov_only()==true), this means (HOV2=0, HOV3=1) uint64_t indoor_ : 1; // Is this edge indoor uint64_t lit_ : 1; // Is the edge lit? - uint64_t spare4_ : 4; + uint64_t dest_only_hgv_ : 1; // destonly for HGV specifically + uint64_t spare4_ : 3; // 5th 8-byte word uint64_t turntype_ : 24; // Turn type (see graphconstants.h) diff --git a/valhalla/baldr/double_bucket_queue.h b/valhalla/baldr/double_bucket_queue.h index 8975430cb9..d307c418b9 100644 --- a/valhalla/baldr/double_bucket_queue.h +++ b/valhalla/baldr/double_bucket_queue.h @@ -98,7 +98,7 @@ template class DoubleBucketQueue final { } /** - * Clear all labels from the low-level buckets and the overflow buckets and deallocates buckets + * Clear all labels from the low-level buckets and the overflow bucket and deallocate the buckets' * memory. */ void clear() { diff --git a/valhalla/baldr/edgeinfo.h b/valhalla/baldr/edgeinfo.h index 8cdd19f1e6..b34732c205 100644 --- a/valhalla/baldr/edgeinfo.h +++ b/valhalla/baldr/edgeinfo.h @@ -54,6 +54,15 @@ struct NameInfo { } }; +// indexes of linguistic attributes in the linguistic map value-tuple +constexpr size_t kLinguisticMapTupleLanguageIndex = 0; +constexpr size_t kLinguisticMapTuplePhoneticAlphabetIndex = 1; +constexpr size_t kLinguisticMapTuplePronunciationIndex = 2; +constexpr size_t kLinguisticHeaderSize = 3; + +// Unfortunately a bug was found where we were returning a blank phoneme (kNone = 0) for a linguistic +// record where it just contained a language and no phoneme. This caused us to stop reading the +// header and in turn caused the name_index to be off. This is why kNone is now equal to 5 struct linguistic_text_header_t { uint32_t language_ : 8; // this is just the language as we will derive locale by getting admin info uint32_t length_ : 8; // pronunciation length @@ -123,6 +132,14 @@ class EdgeInfo { return ei_.speed_limit_; } + /** + * Does this EdgeInfo have elevation data. + * @return Returns true if the EdgeInfo record has elevation along the edge. + */ + bool has_elevation() const { + return ei_.has_elevation_; + } + /** * Get the number of names. * @return Returns the name count. @@ -163,12 +180,20 @@ class EdgeInfo { std::vector> GetNames(bool include_tagged_values) const; /** - * Convenience method to get the names for an edge - * @param only_pronunciations Bool indicating whether or not to return only the pronunciations + * Convenience method to get the non linguistic, tagged values for an edge. + * * * @return Returns a list (vector) of tagged names. */ - std::vector GetTaggedValues(bool only_pronunciations = false) const; + std::vector GetTaggedValues() const; + + /** + * Convenience method to get the linguistic names for an edge + * @param type type of linguistic names we are interested in obtaining. + * + * @return Returns a list (vector) of linguistic names. + */ + std::vector GetLinguisticTaggedValues() const; /** * Convenience method to get the names, route number flags and tag value type for an edge. @@ -187,11 +212,12 @@ class EdgeInfo { const std::multimap& GetTags() const; /** - * Convenience method to get a pronunciation map for an edge. - * @return Returns a unordered_map of type/name pairs with a key that references the name - * index from GetNamesAndTypes + * Convenience method to get a Linguistic map for an edge. + * @return Returns a unordered_map in which the key is a index into the name list from + * GetNamesAndTypes and the tuple contains a pronunciation (w/wo a language) or no pronunciation and + * just a language */ - std::unordered_map> GetPronunciationsMap() const; + std::unordered_map> GetLinguisticMap() const; /** * Convenience method to get the types for the names. @@ -216,6 +242,15 @@ class EdgeInfo { */ std::string encoded_shape() const; + /** + * Returns the encoded elevation along the edge. The sampling interval is uniform + * (based on the length of the edge). The sampling interval is returned via argument. + * @param length Length of the edge. Used to determine sampling interval. + * @param interval Sampling interval (reference - value is returned). + * @return Returns a vector holding delta encoded elevation along the edge. + */ + std::vector encoded_elevation(const uint32_t length, double& interval) const; + /** * Get layer index of the edge relatively to other edges(Z-level). Can be negative. * @see https://wiki.openstreetmap.org/wiki/Key:layer @@ -259,7 +294,8 @@ class EdgeInfo { uint32_t encoded_shape_size_ : 16; // How many bytes long the encoded shape is uint32_t extended_wayid1_ : 8; // Next next byte of the way id uint32_t extended_wayid_size_ : 2; // How many more bytes the way id is stored in - uint32_t spare0_ : 2; // not used + uint32_t has_elevation_ : 1; // Does the edgeinfo have elevation? + uint32_t spare0_ : 1; // not used }; protected: @@ -279,6 +315,9 @@ class EdgeInfo { // Lng, lat shape of the edge mutable std::vector shape_; + // Encoded elevation + const int8_t* encoded_elevation_; + // The list of names within the tile const char* names_list_; diff --git a/valhalla/baldr/graphconstants.h b/valhalla/baldr/graphconstants.h index 9605ddda5f..f5fb6bf499 100644 --- a/valhalla/baldr/graphconstants.h +++ b/valhalla/baldr/graphconstants.h @@ -99,6 +99,8 @@ constexpr uint8_t kMaxTrafficSpeed = 252; // ~157 MPH // clamped to this maximum value. constexpr uint32_t kMaxSpeedKph = std::max(kMaxTrafficSpeed, kMaxAssumedSpeed); +constexpr uint32_t kMaxAssumedTruckSpeed = 120; // ~75 MPH + // Minimum speed. This is a stop gap for dubious traffic data. While its possible // to measure a probe going this slow via stop and go traffic over a long enough // stretch, its unlikely to be good signal below this value @@ -364,7 +366,7 @@ inline std::string to_string(Use u) { {static_cast(Use::kRail), "rail"}, {static_cast(Use::kBus), "bus"}, {static_cast(Use::kEgressConnection), "egress_connection"}, - {static_cast(Use::kPlatformConnection), "platform_connnection"}, + {static_cast(Use::kPlatformConnection), "platform_connection"}, {static_cast(Use::kTransitConnection), "transit_connection"}, {static_cast(Use::kConstruction), "construction"}, }; @@ -378,26 +380,173 @@ inline std::string to_string(Use u) { enum class TaggedValue : uint8_t { // must start at 1 due to nulls kLayer = 1, - kPronunciation = 2, + kLinguistic = 2, kBssInfo = 3, kLevel = 4, kLevelRef = 5, + kLandmark = 6, // we used to have bug when we encoded 1 and 2 as their ASCII codes, but not actual 1 and 2 values // see https://github.com/valhalla/valhalla/issues/3262 kTunnel = static_cast('1'), kBridge = static_cast('2'), - }; enum class PronunciationAlphabet : uint8_t { - kNone = 0, + // kNone = 0 has been deprecated as this introduced a bug while processing the linguistic records. kIpa = 1, - kXKatakana = 2, - kXJeita = 3, - kNtSampa = 4 + kKatakana = 2, + kJeita = 3, + kNtSampa = 4, + kNone = 5 }; // must start at 1 due to nulls -enum class Language : uint8_t { kNone = 1 }; +// must start at 1 due to nulls +enum class Language : uint8_t { + kAb = 1, + kAm = 2, + kAr = 3, + kAz = 4, + kBe = 5, + kBg = 6, + kBn = 7, + kBs = 8, + kCa = 9, + kCkb = 10, + kCs = 11, + kDa = 12, + kDe = 13, + kDv = 14, + kDz = 15, + kEl = 16, + kEn = 17, + kEs = 18, + kEt = 19, + kFa = 20, + kFi = 21, + kFr = 22, + kFy = 23, + kGl = 24, + kHe = 25, + kHr = 26, + kHu = 27, + kHy = 28, + kId = 29, + kIs = 30, + kIt = 31, + kJa = 32, + kKa = 33, + kKl = 34, + kKm = 35, + kKo = 36, + kLo = 37, + kLt = 38, + kLv = 39, + kMg = 40, + kMk = 41, + kMn = 42, + kMo = 43, + kMt = 44, + kMy = 45, + kNe = 46, + kNl = 47, + kNo = 48, + kOc = 49, + kPap = 50, + kPl = 51, + kPs = 52, + kPt = 53, + kRm = 54, + kRo = 55, + kRu = 56, + kSk = 57, + kSl = 58, + kSq = 59, + kSr = 60, + kSrLatn = 61, + kSv = 62, + kTg = 63, + kTh = 64, + kTk = 65, + kTr = 66, + kUk = 67, + kUr = 68, + kUz = 69, + kVi = 70, + kZh = 71, + kCy = 72, + kTa = 73, + kMs = 74, + kNone = 255 +}; + +inline Language stringLanguage(const std::string& s) { + static const std::unordered_map stringToLanguage = + {{"ab", Language::kAb}, {"am", Language::kAm}, {"ar", Language::kAr}, + {"az", Language::kAz}, {"be", Language::kBe}, {"bg", Language::kBg}, + {"bn", Language::kBn}, {"bs", Language::kBs}, {"ca", Language::kCa}, + {"ckb", Language::kCkb}, {"cs", Language::kCs}, {"da", Language::kDa}, + {"de", Language::kDe}, {"dv", Language::kDv}, {"dz", Language::kDz}, + {"el", Language::kEl}, {"en", Language::kEn}, {"es", Language::kEs}, + {"et", Language::kEt}, {"fa", Language::kFa}, {"fi", Language::kFi}, + {"fr", Language::kFr}, {"fy", Language::kFy}, {"gl", Language::kGl}, + {"he", Language::kHe}, {"hr", Language::kHr}, {"hu", Language::kHu}, + {"hy", Language::kHy}, {"id", Language::kId}, {"is", Language::kIs}, + {"it", Language::kIt}, {"ja", Language::kJa}, {"ka", Language::kKa}, + {"kl", Language::kKl}, {"km", Language::kKm}, {"ko", Language::kKo}, + {"lo", Language::kLo}, {"lt", Language::kLt}, {"lv", Language::kLv}, + {"mg", Language::kMg}, {"mk", Language::kMk}, {"mn", Language::kMn}, + {"mo", Language::kMo}, {"mt", Language::kMt}, {"my", Language::kMy}, + {"ne", Language::kNe}, {"nl", Language::kNl}, {"no", Language::kNo}, + {"oc", Language::kOc}, {"pap", Language::kPap}, {"pl", Language::kPl}, + {"ps", Language::kPs}, {"pt", Language::kPt}, {"rm", Language::kRm}, + {"ro", Language::kRo}, {"ru", Language::kRu}, {"sk", Language::kSk}, + {"sl", Language::kSl}, {"sq", Language::kSq}, {"sr", Language::kSr}, + {"sr-Latn", Language::kSrLatn}, {"sv", Language::kSv}, {"tg", Language::kTg}, + {"th", Language::kTh}, {"tk", Language::kTk}, {"tr", Language::kTr}, + {"uk", Language::kUk}, {"ur", Language::kUr}, {"uz", Language::kUz}, + {"vi", Language::kVi}, {"zh", Language::kZh}, {"cy", Language::kCy}, + {"ta", Language::kTa}, {"ms", Language::kMs}, {"none", Language::kNone}}; + + auto i = stringToLanguage.find(s); + if (i == stringToLanguage.cend()) { + return Language::kNone; + } + return i->second; +} +inline std::string to_string(Language l) { + static const std::unordered_map LanguageStrings = + {{Language::kAb, "ab"}, {Language::kAm, "am"}, {Language::kAr, "ar"}, + {Language::kAz, "az"}, {Language::kBe, "be"}, {Language::kBg, "bg"}, + {Language::kBn, "bn"}, {Language::kBs, "bs"}, {Language::kCa, "ca"}, + {Language::kCkb, "ckb"}, {Language::kCs, "cs"}, {Language::kDa, "da"}, + {Language::kDe, "de"}, {Language::kDv, "dv"}, {Language::kDz, "dz"}, + {Language::kEl, "el"}, {Language::kEn, "en"}, {Language::kEs, "es"}, + {Language::kEt, "et"}, {Language::kFa, "fa"}, {Language::kFi, "fi"}, + {Language::kFr, "fr"}, {Language::kFy, "fy"}, {Language::kGl, "gl"}, + {Language::kHe, "he"}, {Language::kHr, "hr"}, {Language::kHu, "hu"}, + {Language::kHy, "hy"}, {Language::kId, "id"}, {Language::kIs, "is"}, + {Language::kIt, "it"}, {Language::kJa, "ja"}, {Language::kKa, "ka"}, + {Language::kKl, "kl"}, {Language::kKm, "km"}, {Language::kKo, "ko"}, + {Language::kLo, "lo"}, {Language::kLt, "lt"}, {Language::kLv, "lv"}, + {Language::kMg, "mg"}, {Language::kMk, "mk"}, {Language::kMn, "mn"}, + {Language::kMo, "mo"}, {Language::kMt, "mt"}, {Language::kMy, "my"}, + {Language::kNe, "ne"}, {Language::kNl, "nl"}, {Language::kNo, "no"}, + {Language::kOc, "oc"}, {Language::kPap, "pap"}, {Language::kPl, "pl"}, + {Language::kPs, "ps"}, {Language::kPt, "pt"}, {Language::kRm, "rm"}, + {Language::kRo, "ro"}, {Language::kRu, "ru"}, {Language::kSk, "sk"}, + {Language::kSl, "sl"}, {Language::kSq, "sq"}, {Language::kSr, "sr"}, + {Language::kSrLatn, "sr-Latn"}, {Language::kSv, "sv"}, {Language::kTg, "tg"}, + {Language::kTh, "th"}, {Language::kTk, "tk"}, {Language::kTr, "tr"}, + {Language::kUk, "uk"}, {Language::kUr, "ur"}, {Language::kUz, "uz"}, + {Language::kVi, "vi"}, {Language::kZh, "zh"}, {Language::kCy, "cy"}, + {Language::kTa, "ta"}, {Language::kMs, "ms"}, {Language::kNone, "none"}}; + + auto i = LanguageStrings.find((l)); + if (i == LanguageStrings.cend()) { + return "none"; + } + return i->second; +} // Speed type enum class SpeedType : uint8_t { @@ -615,6 +764,7 @@ inline float GetOffsetForHeading(RoadClass road_class, Use use) { case Use::kPedestrian: case Use::kBridleway: { offset *= 0.5f; + break; } default: break; diff --git a/valhalla/baldr/graphid.h b/valhalla/baldr/graphid.h index 7d0ff421e8..d4ba7c9cf8 100644 --- a/valhalla/baldr/graphid.h +++ b/valhalla/baldr/graphid.h @@ -2,8 +2,6 @@ #define VALHALLA_BALDR_GRAPHID_H_ #include -#include -#include #include #include diff --git a/valhalla/baldr/graphreader.h b/valhalla/baldr/graphreader.h index c82526d00b..47e0cf6ee7 100644 --- a/valhalla/baldr/graphreader.h +++ b/valhalla/baldr/graphreader.h @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -10,7 +9,6 @@ #include -#include #include #include #include @@ -165,7 +163,7 @@ class FlatTileCache : public TileCache { // The actual cached GraphTile objects std::vector cache_; - // Indicies into the array of actual cached items + // Indices into the array of actual cached items std::vector cache_indices_; // Offsets in the indices list for where a set of tile indices begin @@ -425,7 +423,7 @@ class TileCacheFactory final { public: /** * Constructs tile cache. - * @param pt Property tree listing the configuration for the cahce configuration + * @param pt Property tree listing the configuration for the cache configuration */ static TileCache* createTileCache(const boost::property_tree::ptree& pt); }; @@ -561,7 +559,8 @@ class GraphReader { * @return Returns the graph Id of the opposing directed edge. An * invalid graph Id is returned if the opposing edge does not * exist (can occur with a regional extract where adjacent tile - * is missing). + * is missing). If successful the opp_tile will point to the + * tile containing the opp_edge */ GraphId GetOpposingEdgeId(const GraphId& edgeid, graph_tile_ptr& opp_tile); @@ -573,13 +572,14 @@ class GraphReader { * @return Returns the graph Id of the opposing directed edge. An * invalid graph Id is returned if the opposing edge does not * exist (can occur with a regional extract where adjacent tile - * is missing). + * is missing). If successful the opp_tile will point to the + * tile containing the opp_edge */ GraphId - GetOpposingEdgeId(const GraphId& edgeid, const DirectedEdge*& opp_edge, graph_tile_ptr& tile) { - GraphId opp_edgeid = GetOpposingEdgeId(edgeid, tile); + GetOpposingEdgeId(const GraphId& edgeid, const DirectedEdge*& opp_edge, graph_tile_ptr& opp_tile) { + GraphId opp_edgeid = GetOpposingEdgeId(edgeid, opp_tile); if (opp_edgeid) - opp_edge = tile->directededge(opp_edgeid); + opp_edge = opp_tile->directededge(opp_edgeid); return opp_edgeid; } @@ -601,11 +601,12 @@ class GraphReader { * @param tile Reference to a pointer to a const tile. * @return Returns the opposing directed edge or nullptr if the * opposing edge does not exist (can occur with a regional extract - * where the adjacent tile is missing) + * where the adjacent tile is missing). If successful the opp_tile + * will point to the tile containing the opp_edge */ - const DirectedEdge* GetOpposingEdge(const GraphId& edgeid, graph_tile_ptr& tile) { - GraphId oppedgeid = GetOpposingEdgeId(edgeid, tile); - return oppedgeid.Is_Valid() ? tile->directededge(oppedgeid) : nullptr; + const DirectedEdge* GetOpposingEdge(const GraphId& edgeid, graph_tile_ptr& opp_tile) { + GraphId oppedgeid = GetOpposingEdgeId(edgeid, opp_tile); + return oppedgeid.Is_Valid() ? opp_tile->directededge(oppedgeid) : nullptr; } /** @@ -614,12 +615,13 @@ class GraphReader { * @param tile Reference to a pointer to a const tile. * @return Returns the opposing directed edge or nullptr if the * opposing edge does not exist (can occur with a regional extract - * where the adjacent tile is missing) + * where the adjacent tile is missing). If successful the opp_tile + * will point to the tile containing the opp_edge */ - const DirectedEdge* GetOpposingEdge(const DirectedEdge* edge, graph_tile_ptr& tile) { - if (GetGraphTile(edge->endnode(), tile)) { - const auto* node = tile->node(edge->endnode()); - return tile->directededge(node->edge_index() + edge->opp_index()); + const DirectedEdge* GetOpposingEdge(const DirectedEdge* edge, graph_tile_ptr& opp_tile) { + if (GetGraphTile(edge->endnode(), opp_tile)) { + const auto* node = opp_tile->node(edge->endnode()); + return opp_tile->directededge(node->edge_index() + edge->opp_index()); } return nullptr; } @@ -628,25 +630,32 @@ class GraphReader { * Convenience method to get an end node. * @param edge the edge whose end node you want * @param tile Reference to a pointer to a const tile. - * @return returns the end node of edge or nullptr if it couldn't + * @return returns the end node of edge or nullptr if it couldn't. if successful the end_node_tile + * will point to the tile containing the end_node of edge */ - const NodeInfo* GetEndNode(const DirectedEdge* edge, graph_tile_ptr& tile) { - return GetGraphTile(edge->endnode(), tile) ? tile->node(edge->endnode()) : nullptr; + const NodeInfo* GetEndNode(const DirectedEdge* edge, graph_tile_ptr& end_node_tile) { + return GetGraphTile(edge->endnode(), end_node_tile) ? end_node_tile->node(edge->endnode()) + : nullptr; } /** * Method to get the begin node of an edge by using its opposing edges end node * @param edge the edge whose begin node you want - * @param tile reference to a pointer to a const tile - * @return returns GraphId of begin node of the edge (empty if couldn't find) - */ - GraphId GetBeginNodeId(const DirectedEdge* edge, graph_tile_ptr& tile) { - // grab the node - if (!GetGraphTile(edge->endnode(), tile)) + * @param tile reference to a pointer to a const tile containing the begin node + * @return returns GraphId of begin node of the edge (empty if couldn't find). + * if successful begin_node_tile will point to the tile containing the + * begin_node of edge + */ + GraphId GetBeginNodeId(const DirectedEdge* edge, graph_tile_ptr& begin_node_tile) { + // grab the end node maybe in an adjacent tile + graph_tile_ptr maybe_other_tile = begin_node_tile; + if (!GetGraphTile(edge->endnode(), maybe_other_tile)) return {}; - const auto* node = tile->node(edge->endnode()); - // grab the opp edges end node - const auto* opp_edge = tile->directededge(node->edge_index() + edge->opp_index()); + const auto* node = maybe_other_tile->node(edge->endnode()); + // grab the opp edge also could be in this adjacent tile + const auto* opp_edge = maybe_other_tile->directededge(node->edge_index() + edge->opp_index()); + // grab the end node of the opp_edge, it should be in the original tile + GetGraphTile(opp_edge->endnode(), begin_node_tile); // no-op if original tile is already correct return opp_edge->endnode(); } @@ -666,9 +675,12 @@ class GraphReader { * @param edge2 GraphId of second directed edge. * @param tile Reference to a pointer to a const tile. * @return Returns true if the directed edges are directly connected - * at a node, false if not. + * at a node, false if not. If successful the edge1_end_node_tile will point + * to the tile containing the end node of edge1 */ - bool AreEdgesConnectedForward(const GraphId& edge1, const GraphId& edge2, graph_tile_ptr& tile); + bool AreEdgesConnectedForward(const GraphId& edge1, + const GraphId& edge2, + graph_tile_ptr& edge1_end_node_tile); /** * Convenience method to determine if 2 directed edges are connected from @@ -711,10 +723,11 @@ class GraphReader { * Get node information for the specified node. * @param nodeid Node Id (GraphId) * @param tile Reference to a pointer to a const tile. - * @return Returns a pointer to the node information. + * @return Returns a pointer to the node information. If successful node_tile will + * point to the tile containing nodeid */ - const NodeInfo* nodeinfo(const GraphId& nodeid, graph_tile_ptr& tile) { - return GetGraphTile(nodeid, tile) ? tile->node(nodeid) : nullptr; + const NodeInfo* nodeinfo(const GraphId& nodeid, graph_tile_ptr& node_tile) { + return GetGraphTile(nodeid, node_tile) ? node_tile->node(nodeid) : nullptr; } /** @@ -731,10 +744,11 @@ class GraphReader { * Get the directed edge given its GraphId. * @param edgeid Directed edge Id. * @param tile Reference to a pointer to a const tile. - * @return Returns a pointer to the directed edge. + * @return Returns a pointer to the directed edge. If successful edge_tile will point to the tile + * which contains edgeid */ - const DirectedEdge* directededge(const GraphId& edgeid, graph_tile_ptr& tile) { - return GetGraphTile(edgeid, tile) ? tile->directededge(edgeid) : nullptr; + const DirectedEdge* directededge(const GraphId& edgeid, graph_tile_ptr& edge_tile) { + return GetGraphTile(edgeid, edge_tile) ? edge_tile->directededge(edgeid) : nullptr; } /** @@ -765,16 +779,17 @@ class GraphReader { * @return Returns a pair of GraphIds: the first is the start node * and the second is the end node. An invalid start node * can occur in regional extracts (where the end node tile - * is not available). + * is not available). If successful edge_tile will point to + * the one containing edgeid */ - std::pair GetDirectedEdgeNodes(const GraphId& edgeid, graph_tile_ptr& tile) { - if (tile && tile->id().Tile_Base() == edgeid.Tile_Base()) { - return GetDirectedEdgeNodes(tile, tile->directededge(edgeid)); + std::pair GetDirectedEdgeNodes(const GraphId& edgeid, graph_tile_ptr& edge_tile) { + if (edge_tile && edge_tile->id().Tile_Base() == edgeid.Tile_Base()) { + return GetDirectedEdgeNodes(edge_tile, edge_tile->directededge(edgeid)); } else { - tile = GetGraphTile(edgeid); - if (!tile) + edge_tile = GetGraphTile(edgeid); + if (!edge_tile) return {}; - return GetDirectedEdgeNodes(tile, tile->directededge(edgeid)); + return GetDirectedEdgeNodes(edge_tile, edge_tile->directededge(edgeid)); } } @@ -792,10 +807,11 @@ class GraphReader { * Get the end node of an edge. The current tile is accepted as an * argiment. * @param edgeid Edge Id. - * @return Returns the end node of the edge. + * @return Returns the end node of the edge. If successful edge_tile will point to the one + * containing edgeid */ - GraphId edge_endnode(const GraphId& edgeid, graph_tile_ptr& tile) { - const DirectedEdge* de = directededge(edgeid, tile); + GraphId edge_endnode(const GraphId& edgeid, graph_tile_ptr& edge_tile) { + const DirectedEdge* de = directededge(edgeid, edge_tile); if (de) { return de->endnode(); } else { @@ -807,7 +823,8 @@ class GraphReader { * Get the start node of an edge. * @param edgeid Edge Id (Graph Id) * @param tile Current tile. - * @return Returns the start node of the edge. + * @return Returns the start node of the edge. If successful end_node_tile will point to the tile + * containing the end_node of the input edge */ GraphId edge_startnode(const GraphId& edgeid, graph_tile_ptr& tile) { GraphId opp_edgeid = GetOpposingEdgeId(edgeid, tile); @@ -834,14 +851,15 @@ class GraphReader { * Get the edgeinfo of an edge * @param edgeid Edge Id (Graph Id) * @param tile Current tile. - * @returns Returns the edgeinfo for the specified id. + * @returns Returns the edgeinfo for the specified id. If successful edge_tile will point to the + * tile containing edgeid */ - EdgeInfo edgeinfo(const GraphId& edgeid, graph_tile_ptr& tile) { - auto* edge = directededge(edgeid, tile); + EdgeInfo edgeinfo(const GraphId& edgeid, graph_tile_ptr& edge_tile) { + auto* edge = directededge(edgeid, edge_tile); if (edge == nullptr) { throw std::runtime_error("Cannot find edgeinfo for edge: " + std::to_string(edgeid)); } - return tile->edgeinfo(edge); + return edge_tile->edgeinfo(edge); } /** @@ -927,6 +945,16 @@ class GraphReader { */ int GetTimezone(const baldr::GraphId& node, graph_tile_ptr& tile); + /** + * Convenience method to get the timezone index from an edge. Preferably it returns + * the start's node's timezone. + * @param edge GraphId of the edge to get the timezone index. + * @param tile Current tile. Can be changed to the tile of the edge's end node. + * @return Returns the timezone index. A value of 0 indicates an invalid timezone. + * It's possible that the tile changes to the edge's end node's tile. + */ + int GetTimezoneFromEdge(const baldr::GraphId& edge, graph_tile_ptr& tile); + /** * Returns an incident tile for the given tile id * @param tile_id the tile id for which incidents should be returned @@ -938,9 +966,9 @@ class GraphReader { * Returns a vector of incidents for the given edge * @param edge_id which edge you need incidents for * @param tile which tile the edge lives in, is updated if not correct - * @return IncidentResult + * @return IncidentResult. If successful edge_tile will point to the tile containing the edge_id */ - IncidentResult GetIncidents(const GraphId& edge_id, graph_tile_ptr& tile); + IncidentResult GetIncidents(const GraphId& edge_id, graph_tile_ptr& edge_tile); protected: // (Tar) extract of tiles - the contents are empty if not being used diff --git a/valhalla/baldr/graphtile.h b/valhalla/baldr/graphtile.h index e93c054bde..9b979ad028 100644 --- a/valhalla/baldr/graphtile.h +++ b/valhalla/baldr/graphtile.h @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -466,27 +465,39 @@ class GraphTile { std::string GetName(const uint32_t textlist_offset) const; /** - * Convenience method to get the signs for an edge given the directed - * edge index. + * Given either a DirectedEdge or Node index, returns all the signs including + * languages and phonemes. + * + * To retrieve signs/languages/phonemes for a node, pass signs_on_node=true. Note + * that the sign-types specific to nodes are Sign::Type::kJunctionName and + * Sign::Type::kTollName. + * + * The sign types Sign::Type::kPronunciation and Sign::Type::kLanguage can apply + * to either node or edge. + * + * All other sign types are specific to edges. + * * @param idx Directed edge or node index. Used to lookup list of signs. - * @param signs_on_node Are we looking for signs at the node? These are the - * intersection names. - * @return Returns a list (vector) of signs. + * @param signs_on_node Are we looking for signs at the node? + * @return Returns a vector of signs. */ std::vector GetSigns(const uint32_t idx, bool signs_on_node = false) const; /** - * Convenience method to get the signs for an edge given the directed - * edge index. - * @param idx Directed edge or node index. Used to lookup list of signs. - * @param signs_on_node Are we looking for signs at the node? These are the + * Given either a DirectedEdge or Node index, returns all the signs. + * + * @param idx Directed edge or node index. Used to lookup list of signs. + * @param index_linguistic_map unordered_map in which the key is a index into the vector of + * shields and the tuple contains a pronunciation (w/wo a language) or no pronunciation and just a + * language + * @param signs_on_node Are we looking for signs at the node? These are the * intersection names. * @return Returns a list (vector) of signs. */ - std::vector - GetSigns(const uint32_t idx, - std::unordered_map>& index_pronunciation_map, - bool signs_on_node = false) const; + std::vector GetSigns( + const uint32_t idx, + std::unordered_map>& index_linguistic_map, + bool signs_on_node = false) const; /** * Get the next departure given the directed edge Id and the current @@ -495,7 +506,7 @@ class GraphTile { * @param current_time Current time (seconds from midnight). * @param day Days since the tile creation date. * @param dow Day of week (see graphconstants.h) - * @param date_before_tile Is the date that was inputed before + * @param date_before_tile Is the date that was input before * the tile creation date? * @param wheelchair Only find departures with wheelchair access if true * @param bicycle Only find departures with bicycle access if true @@ -584,7 +595,7 @@ class GraphTile { const uint32_t access) const; /** - * Get an iteratable list of GraphIds given a bin in the tile + * Get an iterable list of GraphIds given a bin in the tile * @param column the bin's column * @param row the bin's row * @return iterable container of graphids contained in the bin @@ -592,7 +603,7 @@ class GraphTile { midgard::iterable_t GetBin(size_t column, size_t row) const; /** - * Get an iteratable list of GraphIds given a bin in the tile + * Get an iterable list of GraphIds given a bin in the tile * @param index the bin's index in the row major array * @return iterable container of graphids contained in the bin */ @@ -608,7 +619,7 @@ class GraphTile { /** * Convenience method for use with costing to get the speed for an edge given the directed * edge and a time (seconds since start of the week). If the current speed of the edge - * is 0 then the current speed is ignore and other speed sources are used to prevent + * is 0 then the current speed is ignored and other speed sources are used to prevent * issues with costing * * @param de Directed edge information. diff --git a/valhalla/baldr/graphtileheader.h b/valhalla/baldr/graphtileheader.h index 41de45f621..4687415298 100644 --- a/valhalla/baldr/graphtileheader.h +++ b/valhalla/baldr/graphtileheader.h @@ -656,7 +656,7 @@ class GraphTileHeader { // On x86 based systems the compiler will only pad to 32bits // This made tiles incompatibilte between x64 and x86 platforms // To fix it we insert spare in the right places so that its the same as what - // the compiler automatically does on an 64bit system but makes 32bit sysems + // the compiler automatically does on an 64bit system but makes 32bit systems // see the layout of the structure that way as well. This way both platforms agree // that the x64 implementation, which is what we generally use to build tiles, // is the correct implementation diff --git a/valhalla/baldr/json.h b/valhalla/baldr/json.h index 903aea0f22..663cbfdda4 100644 --- a/valhalla/baldr/json.h +++ b/valhalla/baldr/json.h @@ -7,10 +7,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -196,12 +194,12 @@ inline void applyOutputVisitor(std::ostream& stream, Visitable& visitable) { inline std::ostream& operator<<(std::ostream& stream, const Jmap& json) { stream << '{'; - bool seprator = false; + bool separator = false; for (const auto& key_value : json) { - if (seprator) { + if (separator) { stream << ','; } - seprator = true; + separator = true; stream << '"' << key_value.first << "\":"; applyOutputVisitor(stream, key_value.second); } @@ -211,12 +209,12 @@ inline std::ostream& operator<<(std::ostream& stream, const Jmap& json) { inline std::ostream& operator<<(std::ostream& stream, const Jarray& json) { stream << '['; - bool seprator = false; + bool separator = false; for (const auto& element : json) { - if (seprator) { + if (separator) { stream << ','; } - seprator = true; + separator = true; applyOutputVisitor(stream, element); } stream << ']'; diff --git a/valhalla/baldr/landmark.h b/valhalla/baldr/landmark.h new file mode 100644 index 0000000000..23becf3700 --- /dev/null +++ b/valhalla/baldr/landmark.h @@ -0,0 +1,157 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace valhalla { +namespace baldr { + +// obvious landmark types for vehicle routing +enum class LandmarkType : uint8_t { + // these will almost always be obvious by their function and don't require a name + fuel = 1, + post_office = 2, + police = 3, + fire_station = 4, + car_wash = 5, + // could be obvious if its a chain but could also just be a building + restaurant = 6, + // this will always be a chain in which case the name is a required distinguisher (as they are often + // next to each other on either side of a street) + fast_food = 7, + // this could be a chain making it easier to distinguish (starbucks) but could be a one off cafe in + // which case the name will be required + cafe = 8, + // on the fence about this one, these are often obvious without a name, maybe should go in the upper + // list? + bank = 9, + // same with this one, in some countries these are obvious and have the little plus sign, in america + // its not the case and they can be different chains or could be part of a grocery store or + // anything... speaking of grocery store, why isnt that on the list? also why arent department + // stores on the list, surely stores would be a good landmark to use (again when they are named) + pharmacy = 10, + // again these could just be a random building with nothing other than a sign like "early years" or + // something, they better have a name or it will be ambiguous + kindergarten = 11, + // same thing, often just a building + bar = 12, + // this could be very very big like a college campus, i think we should remove this + hospital = 13, + // same as bar could be just a building need at least a name to have a chance of seeing it + pub = 14, + // generic, could be many many types of clinics also often there are multiple of these located in + // same "complex", you might have a dentist, orthodontist, ear nose throat and eye specialists all + // next to each other. + clinic = 15, + // on the fence about this one, to me this would be in the same category as "museum" or something + // where its so specialized maybe it doesnt need a name. what keeps me from say ing that is you + // often cant tell from the outside what function these amenities serve until you read the name + theatre = 16, + // this could work without a name in my opinion, they often have a board out front that shows what + // movies are playing which would probably remove the need for a name + cinema = 17, + // these are usually quite large as well but not usually multiple buildings, how often are they + // located near one another though? if its often perhaps we need a name to differentiate between + // this casino and the next one down on the next block? maybe that argument is a good argument for + // everything has to have a name? + casino = 18, +}; + +constexpr uint8_t LandmarkTypeFirstValue = static_cast(LandmarkType::fuel); +constexpr uint8_t LandmarkTypeLastValue = static_cast(LandmarkType::casino); + +inline LandmarkType string_to_landmark_type(const std::string& s) { + static const std::unordered_map string_to_landmark_type = + {{"restaurant", LandmarkType::restaurant}, + {"fast_food", LandmarkType::fast_food}, + {"cafe", LandmarkType::cafe}, + {"fuel", LandmarkType::fuel}, + {"bank", LandmarkType::bank}, + {"pharmacy", LandmarkType::pharmacy}, + {"kindergarten", LandmarkType::kindergarten}, + {"bar", LandmarkType::bar}, + {"hospital", LandmarkType::hospital}, + {"post_office", LandmarkType::post_office}, + {"pub", LandmarkType::pub}, + {"clinic", LandmarkType::clinic}, + {"police", LandmarkType::police}, + {"fire_station", LandmarkType::fire_station}, + {"car_wash", LandmarkType::car_wash}, + {"theatre", LandmarkType::theatre}, + {"cinema", LandmarkType::cinema}, + {"casino", LandmarkType::casino}}; + + auto it = string_to_landmark_type.find(s); + if (it == string_to_landmark_type.cend()) { + throw std::runtime_error("unknown landmark type"); + } + return it->second; +} + +struct Landmark { + int64_t id; + std::string name; + LandmarkType type; + double lng; + double lat; + + Landmark(const int64_t id, + const std::string name, + const LandmarkType type, + const double lng, + const double lat) + : id(id), name(name), type(type), lng(lng), lat(lat) { + } + + /** + * Constructor: convert the given string to a Landmark object. + * The input string should consist of at least 9 bytes: 1 byte for landmark type, + * 8 bytes for location (lng and lat), and the remaining for landmark name. + * + * @param str The string to be converted. + */ + Landmark(const std::string& str) : id(0) { + // needs at least 8 bytes for ll, 1 for type and 1 for the name + if (str.size() < 10) { + throw std::runtime_error("Invalid Landmark string: too short"); + } + name = str.substr(9); + + // and that the type is valid + if (str[8] < LandmarkTypeFirstValue || str[8] > LandmarkTypeLastValue) { + throw std::logic_error("Invalid landmark type"); + } + type = static_cast(str[8]); + + // extract the ll + uint64_t ll = midgard::unaligned_read(static_cast(str.data())); + lat = ((ll >> 32) & ((1ull << 31) - 1)) / static_cast(1e7) - 90; + lng = (ll & ((1ull << 32) - 1)) / static_cast(1e7) - 180; + } + + /** + * Convert a Landmark object to a string. + * The string consists of 1 byte of kLandmark tag, 1 byte of landmark type, + * 8 bytes of location (lng and lat) and some bytes for landmark name at last. + * + * @param landmark The Landmark object to be converted. + */ + std::string to_str() const { + // lon,lat stuffed into 63 bits at 7digit precision + uint64_t ll = uint64_t((lng + 180) * 1e7 + .5) | (uint64_t((lat + 90) * 1e7 + .5) << 32); + std::string value(static_cast(static_cast(&ll)), sizeof(ll)); + value.push_back(static_cast(type)); + value += name; + return value; + } +}; + +} // namespace baldr +} // namespace valhalla diff --git a/valhalla/baldr/location.h b/valhalla/baldr/location.h index 2e1f56688c..5cba77dd76 100644 --- a/valhalla/baldr/location.h +++ b/valhalla/baldr/location.h @@ -35,7 +35,7 @@ struct Location { * Optional filters supplied in the request. * * NOTE: this struct must be kept in sync with the protobuf defined - * valhalla::Location::SearchFilter in tripcommon.proto. + * valhalla::Location::SearchFilter in common.proto. */ struct SearchFilter { public: @@ -71,6 +71,7 @@ struct Location { unsigned int min_inbound_reach = 0, unsigned long radius = 0, const PreferredSide& side = PreferredSide::EITHER, + valhalla::RoadClass street_side_cutoff = valhalla::RoadClass::kServiceOther, const SearchFilter& search_filter = SearchFilter(), std::optional preferred_layer = {}); @@ -109,6 +110,7 @@ struct Location { float search_cutoff_; float street_side_tolerance_; float street_side_max_distance_; + valhalla::RoadClass street_side_cutoff_; SearchFilter search_filter_; // coordinates of the location as used for altering the side of street diff --git a/valhalla/baldr/nodeinfo.h b/valhalla/baldr/nodeinfo.h index 15c5fbb97d..56934ab9b7 100644 --- a/valhalla/baldr/nodeinfo.h +++ b/valhalla/baldr/nodeinfo.h @@ -12,10 +12,19 @@ namespace valhalla { namespace baldr { -constexpr uint32_t kMaxEdgesPerNode = 127; // Maximum edges per node -constexpr uint32_t kMaxAdminsPerTile = 4095; // Maximum Admins per tile -constexpr uint32_t kMaxTimeZonesPerTile = 511; // Maximum TimeZones index -constexpr uint32_t kMaxLocalEdgeIndex = 7; // Max. index of edges on local level +constexpr uint32_t kMaxEdgesPerNode = 127; // Maximum edges per node +constexpr uint32_t kMaxAdminsPerTile = 4095; // Maximum Admins per tile +constexpr uint32_t kMaxTimeZoneIdExt1 = 1023; // Maximum TimeZones index for first extension level +// constexpr uint32_t kMaxTimeZoneIdExt2 = +// 2047; // Maximum TimeZones index for second extension level; not needed yet +constexpr uint32_t kMaxLocalEdgeIndex = 7; // Max. index of edges on local level + +// Elevation precision. Elevation is clamped to a range of -500 meters to 7683 meters +constexpr uint32_t kNodeMaxStoredElevation = 32767; // 15 bits +constexpr float kNodeElevationPrecision = 0.25f; +constexpr float kNodeMinElevation = -500.0f; +constexpr float kNodeMaxElevation = + kNodeMinElevation + (kNodeElevationPrecision * kNodeMaxStoredElevation); // Heading shrink factor to reduce max heading of 359 to 255 constexpr float kHeadingShrinkFactor = (255.0f / 359.0f); @@ -149,7 +158,10 @@ class NodeInfo { * @return Returns the timezone index. */ uint32_t timezone() const { - return timezone_; + return timezone_ | (timezone_ext_1_ << 9); + // replace with this if a new timezone ever gets created from a previously new + // timezone (reference release is 2023c) + // return timezone_ | (timezone_ext_1_ << 9) | (time_zone_ext_2_ << 10); } /** @@ -256,6 +268,20 @@ class NodeInfo { */ void set_drive_on_right(const bool rsd); + /** + * Get the elevation at this node. + * @return Returns the elevation in meters. + */ + float elevation() const { + return kNodeMinElevation + (elevation_ * kNodeElevationPrecision); + } + + /** + * Set the elevation at this node. + * @param Elevation in meters. + */ + void set_elevation(const float elevation); + /** * Was the access information originally set in the data? * True if any tags like "access", "auto", "truck", "foot", etc were specified. @@ -409,8 +435,8 @@ class NodeInfo { */ inline uint32_t heading(const uint32_t localidx) const { if (localidx > kMaxLocalEdgeIndex) { - LOG_WARN("Local index " + std::to_string(localidx) + " exceeds max value of " + - std::to_string(kMaxLocalEdgeIndex) + ", returning heading of 0"); + LOG_DEBUG("Local index " + std::to_string(localidx) + " exceeds max value of " + + std::to_string(kMaxLocalEdgeIndex) + ", returning heading of 0"); return 0; } // Make sure everything is 64 bit! @@ -486,6 +512,7 @@ class NodeInfo { uint64_t density_ : 4; // Relative road density uint64_t traffic_signal_ : 1; // Traffic signal uint64_t mode_change_ : 1; // Mode change allowed? + // Also used for aggregation of edges at filter stage uint64_t named_ : 1; // Is this a named intersection? uint64_t transition_index_ : 21; // Index into the node transitions to the first transition @@ -499,7 +526,13 @@ class NodeInfo { uint64_t tagged_access_ : 1; // Was access initially tagged? uint64_t private_access_ : 1; // Is the access private? uint64_t cash_only_toll_ : 1; // Is this toll cash only? - uint64_t spare2_ : 17; + uint64_t elevation_ : 15; // Encoded elevation (meters) + uint64_t timezone_ext_1_ : 1; // To keep compatibility when new timezones are added + // uncomment a new timezone ever gets created from a previously new + // timezone (reference release is 2023c) + // uint64_t timezone_ext_2_ : 1; + + uint64_t spare2_ : 1; // For not transit levels its the headings of up to kMaxLocalEdgeIndex+1 local edges (rounded to // nearest 2 degrees)for all other levels. diff --git a/valhalla/baldr/openlr.h b/valhalla/baldr/openlr.h index 4ff1bd9589..435d91b70b 100644 --- a/valhalla/baldr/openlr.h +++ b/valhalla/baldr/openlr.h @@ -145,7 +145,7 @@ struct LocationReferencePoint { /** * Strict equality here is ok because the floating point values came from integers * @param lrp - * @return true if the provided lrp exactly matchs this lrp + * @return true if the provided lrp exactly matches this lrp */ bool operator==(const LocationReferencePoint& lrp) const { return longitude == lrp.longitude && latitude == lrp.latitude && bearing == lrp.bearing && diff --git a/valhalla/baldr/pathlocation.h b/valhalla/baldr/pathlocation.h index 4969fdd598..909c66c520 100644 --- a/valhalla/baldr/pathlocation.h +++ b/valhalla/baldr/pathlocation.h @@ -1,8 +1,6 @@ #ifndef VALHALLA_BALDR_PATHLOCATION_H_ #define VALHALLA_BALDR_PATHLOCATION_H_ -#include -#include #include #include @@ -124,6 +122,7 @@ struct PathLocation : public Location { l->set_search_cutoff(pl.radius_ > pl.search_cutoff_ ? pl.radius_ : pl.search_cutoff_); l->set_street_side_tolerance(pl.street_side_tolerance_); l->set_street_side_max_distance(pl.street_side_max_distance_); + l->set_street_side_cutoff(pl.street_side_cutoff_); l->mutable_search_filter()->set_min_road_class(pl.search_filter_.min_road_class_); l->mutable_search_filter()->set_max_road_class(pl.search_filter_.max_road_class_); l->mutable_search_filter()->set_exclude_tunnel(pl.search_filter_.exclude_tunnel_); @@ -192,7 +191,8 @@ struct PathLocation : public Location { SearchFilter search_filter = SearchFilter(); Location l({loc.ll().lng(), loc.ll().lat()}, fromPBF(loc.type()), loc.minimum_reachability(), - loc.minimum_reachability(), loc.radius(), side, search_filter); + loc.minimum_reachability(), loc.radius(), side, valhalla::RoadClass::kServiceOther, + search_filter); l.name_ = loc.name(); l.street_ = loc.street(); @@ -218,6 +218,9 @@ struct PathLocation : public Location { if (loc.has_street_side_max_distance_case()) { l.street_side_max_distance_ = loc.street_side_max_distance(); } + if (loc.has_street_side_cutoff_case()) { + l.street_side_cutoff_ = loc.street_side_cutoff(); + } if (loc.has_search_filter()) { l.search_filter_.min_road_class_ = loc.search_filter().min_road_class(); l.search_filter_.max_road_class_ = loc.search_filter().max_road_class(); diff --git a/valhalla/baldr/rapidjson_utils.h b/valhalla/baldr/rapidjson_utils.h index dacf83d15c..b9594d42c3 100644 --- a/valhalla/baldr/rapidjson_utils.h +++ b/valhalla/baldr/rapidjson_utils.h @@ -20,7 +20,7 @@ throw std::logic_error(RAPIDJSON_STRINGIFY(x)) // Because we now throw exceptions, we need to turn off RAPIDJSON_NOEXCEPT #define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 -// Enbale std::string overloads +// Enable std::string overloads #define RAPIDJSON_HAS_STDSTRING 1 #include diff --git a/valhalla/baldr/sign.h b/valhalla/baldr/sign.h index b94b16d00c..253546cfae 100644 --- a/valhalla/baldr/sign.h +++ b/valhalla/baldr/sign.h @@ -26,7 +26,8 @@ class Sign { kJunctionName, kGuidanceViewJunction, kGuidanceViewSignboard, - kPronunciation = 255 + kTollName, + kLinguistic = 255 }; /** diff --git a/valhalla/baldr/signinfo.h b/valhalla/baldr/signinfo.h index bff0d21aa3..d0bf1f9f29 100644 --- a/valhalla/baldr/signinfo.h +++ b/valhalla/baldr/signinfo.h @@ -14,36 +14,40 @@ class SignInfo { public: /** * Constructor. - * @param type Sign type. - * - * @param rn Bool indicating if this sign is a route number. + * @param type Sign type. + * @param rn Bool indicating if this sign is a route number. + * @param tagged Bool indicating if this sign is a special tagged type. + * @param has_linguistic Bool indicating if this has a linguistic record or not. + * @param linguistic_start_index uint32_t. The linguistic start index. + * @param linguistic_count uint32_t. The number of linguistic records * @param text Text string. */ SignInfo(const Sign::Type& type, const bool rn, const bool tagged, - const bool has_phoneme, - const uint32_t phoneme_start_index, - const uint32_t phoneme_count, + const bool has_linguistic, + const uint32_t linguistic_start_index, + const uint32_t linguistic_count, const std::string& text) - : phoneme_start_index_(phoneme_start_index), phoneme_count_(phoneme_count), type_(type), - is_route_num_(rn), is_tagged_(tagged), has_phoneme_(has_phoneme), text_(text) { + : linguistic_start_index_(linguistic_start_index), linguistic_count_(linguistic_count), + type_(type), is_route_num_(rn), is_tagged_(tagged), has_linguistic_(has_linguistic), + text_(text) { } /** - * Returns the phoneme start index. - * @return Returns the phoneme start index. + * Returns the linguistic start index. + * @return Returns the linguistic start index. */ - uint32_t phoneme_start_index() const { - return phoneme_start_index_; + uint32_t linguistic_start_index() const { + return linguistic_start_index_; } /** - * Returns the phoneme count. - * @return Returns the phoneme count. + * Returns the linguistic count. + * @return Returns the linguistic count. */ - uint32_t phoneme_count() const { - return phoneme_count_; + uint32_t linguistic_count() const { + return linguistic_count_; } /** @@ -71,11 +75,11 @@ class SignInfo { } /** - * Does the sign have a phoneme? - * @return Returns true the sign has a phoneme? + * Does the sign have a linguistic set? + * @return Returns true the sign has a linguistic set? */ - bool has_phoneme() const { - return has_phoneme_; + bool has_linguistic() const { + return has_linguistic_; } /** @@ -92,12 +96,13 @@ class SignInfo { } protected: - uint32_t phoneme_start_index_; - uint32_t phoneme_count_; + uint32_t linguistic_start_index_; + uint32_t linguistic_count_; + Sign::Type type_; bool is_route_num_; bool is_tagged_; - bool has_phoneme_; + bool has_linguistic_; std::string text_; }; diff --git a/valhalla/baldr/streetname.h b/valhalla/baldr/streetname.h index 5266117115..9c389b3005 100644 --- a/valhalla/baldr/streetname.h +++ b/valhalla/baldr/streetname.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_BALDR_STREETNAME_H_ #define VALHALLA_BALDR_STREETNAME_H_ -#include #include #include diff --git a/valhalla/baldr/streetname_us.h b/valhalla/baldr/streetname_us.h index 52729402de..a19d513f0d 100644 --- a/valhalla/baldr/streetname_us.h +++ b/valhalla/baldr/streetname_us.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_BALDR_STREETNAME_US_H_ #define VALHALLA_BALDR_STREETNAME_US_H_ -#include #include #include #include diff --git a/valhalla/baldr/streetnames_us.h b/valhalla/baldr/streetnames_us.h index 4b01863f7e..8e73aeb346 100644 --- a/valhalla/baldr/streetnames_us.h +++ b/valhalla/baldr/streetnames_us.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_BALDR_STREETNAMES_US_H_ #define VALHALLA_BALDR_STREETNAMES_US_H_ -#include #include #include diff --git a/valhalla/baldr/tilehierarchy.h b/valhalla/baldr/tilehierarchy.h index c18fcfb445..01a0b0d89a 100644 --- a/valhalla/baldr/tilehierarchy.h +++ b/valhalla/baldr/tilehierarchy.h @@ -2,7 +2,6 @@ #define VALHALLA_MIDGARD_TILEHIERARCHY_H #include -#include #include #include diff --git a/valhalla/baldr/timedomain.h b/valhalla/baldr/timedomain.h index 845cfb2580..57bf50caec 100644 --- a/valhalla/baldr/timedomain.h +++ b/valhalla/baldr/timedomain.h @@ -2,7 +2,6 @@ #define VALHALLA_BALDR_TIMEDOMAIN_H_ #include -#include namespace valhalla { namespace baldr { @@ -93,8 +92,8 @@ union TimeDomain { /** * Set the day when this time domain starts - * @param beging_day_dow the begin_day_dow. - * begin_day_dow = begin day or dow enum (i.e. 1st Sunday in some month) + * @param begin_day_dow the begin_day_dow. + * begin_day_dow = begin day or dow enum (i.e. 1st Sunday in some month) */ void set_begin_day_dow(const uint8_t begin_day_dow) { if (daterange.type == kYMD && begin_day_dow > kMaxDateRangeDay) { diff --git a/valhalla/baldr/traffictile.h b/valhalla/baldr/traffictile.h index c898a7fb7c..cf4678b27d 100644 --- a/valhalla/baldr/traffictile.h +++ b/valhalla/baldr/traffictile.h @@ -16,7 +16,6 @@ #include #include #include -#include #else #include #endif diff --git a/valhalla/baldr/verbal_text_formatter_us_co.h b/valhalla/baldr/verbal_text_formatter_us_co.h index a04f50bb1e..8e96c2954f 100644 --- a/valhalla/baldr/verbal_text_formatter_us_co.h +++ b/valhalla/baldr/verbal_text_formatter_us_co.h @@ -3,9 +3,7 @@ #include -#include #include -#include namespace valhalla { namespace baldr { diff --git a/valhalla/baldr/verbal_text_formatter_us_tx.h b/valhalla/baldr/verbal_text_formatter_us_tx.h index b3f3781d25..c72349b0ef 100644 --- a/valhalla/baldr/verbal_text_formatter_us_tx.h +++ b/valhalla/baldr/verbal_text_formatter_us_tx.h @@ -1,9 +1,7 @@ #ifndef VALHALLA_BALDR_VERBAL_TEXT_FORMATTER_US_TX_H_ #define VALHALLA_BALDR_VERBAL_TEXT_FORMATTER_US_TX_H_ -#include #include -#include #include diff --git a/valhalla/config.h b/valhalla/config.h index c7b145ff0c..1d05138239 100644 --- a/valhalla/config.h +++ b/valhalla/config.h @@ -1,6 +1,11 @@ -/* Minimal valhalla/config.h template for minimal CMake build configuration */ +#pragma once +#include +#include -#include +#include + +#include "baldr/rapidjson_utils.h" +#include "valhalla.h" #define VALHALLA_STRINGIZE_NX(A) #A #define VALHALLA_STRINGIZE(A) VALHALLA_STRINGIZE_NX(A) @@ -31,3 +36,9 @@ /* Define to the version of this package. */ #define PACKAGE_VERSION VALHALLA_VERSION + +namespace valhalla { + +const boost::property_tree::ptree& config(const std::string& config_file_or_inline = ""); + +} // namespace valhalla diff --git a/valhalla/filesystem.h b/valhalla/filesystem.h index 605e02e51a..7115760754 100644 --- a/valhalla/filesystem.h +++ b/valhalla/filesystem.h @@ -67,6 +67,14 @@ class path { return *this; return path(path_name_.substr(separators_.back() + 1)); } + path stem() const { + auto file_name = filename().string(); + auto pos = file_name.rfind('.'); + if (pos == std::string::npos || pos == 0 || file_name == "..") { + return path(""); + } + return path(file_name.substr(0, pos)); + } const char* c_str() const { return path_name_.c_str(); } diff --git a/valhalla/loki/node_search.h b/valhalla/loki/node_search.h index f3bf5eb53c..316150c31f 100644 --- a/valhalla/loki/node_search.h +++ b/valhalla/loki/node_search.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_LOKI_NODE_SEARCH_H_ #define VALHALLA_LOKI_NODE_SEARCH_H_ -#include #include namespace valhalla { diff --git a/valhalla/loki/search.h b/valhalla/loki/search.h index 6b62120a98..cb52ceedeb 100644 --- a/valhalla/loki/search.h +++ b/valhalla/loki/search.h @@ -1,15 +1,12 @@ #ifndef VALHALLA_LOKI_SEARCH_H_ #define VALHALLA_LOKI_SEARCH_H_ -#include #include #include #include #include #include -#include - namespace valhalla { namespace loki { @@ -21,7 +18,7 @@ namespace loki { * @param reader and object used to access tiled route data TODO: switch this out for a * proper cache * @param costing a costing object by which we can determine which portions of the graph are - * accessable and therefor potential candidates + * accessible and therefor potential candidates * @return pathLocations the correlated data with in the tile that matches the inputs. If a * projection is not found, it will not have any entry in the returned value. */ diff --git a/valhalla/loki/worker.h b/valhalla/loki/worker.h index c1b24f2e99..aad9d57f6f 100644 --- a/valhalla/loki/worker.h +++ b/valhalla/loki/worker.h @@ -1,7 +1,6 @@ #ifndef __VALHALLA_LOKI_SERVICE_H__ #define __VALHALLA_LOKI_SERVICE_H__ -#include #include #include @@ -21,7 +20,7 @@ namespace valhalla { namespace loki { -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES void run_service(const boost::property_tree::ptree& config); #endif @@ -29,7 +28,7 @@ class loki_worker_t : public service_worker_t { public: loki_worker_t(const boost::property_tree::ptree& config, const std::shared_ptr& graph_reader = {}); -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES virtual prime_server::worker_t::result_t work(const std::list& job, void* request_info, const std::function& interrupt) override; diff --git a/valhalla/meili/candidate_search.h b/valhalla/meili/candidate_search.h index ea190a9e9b..d3837120a0 100644 --- a/valhalla/meili/candidate_search.h +++ b/valhalla/meili/candidate_search.h @@ -4,9 +4,6 @@ #include #include -#include - -#include #include #include diff --git a/valhalla/meili/grid_range_query.h b/valhalla/meili/grid_range_query.h index baec98745f..3b8ee636ef 100644 --- a/valhalla/meili/grid_range_query.h +++ b/valhalla/meili/grid_range_query.h @@ -128,7 +128,7 @@ template class GridRangeQuery { GridTraversal grid_; // Using vector to represent the grid would be faster than using -// unordered map but it consumes (much) more memeory as well +// unordered map but it consumes (much) more memory as well #ifdef GRID_USE_VECTOR std::vector> items_; #else diff --git a/valhalla/meili/map_matcher.h b/valhalla/meili/map_matcher.h index f63f53bf55..32159af229 100644 --- a/valhalla/meili/map_matcher.h +++ b/valhalla/meili/map_matcher.h @@ -2,7 +2,6 @@ #ifndef MMP_MAP_MATCHER_H_ #define MMP_MAP_MATCHER_H_ -#include #include #include @@ -113,13 +112,13 @@ class MapMatcher final { /** * Here we return the vector of edge segments between the source and target states. If its a node to - * node route (meaning no realy edge is traversed) then we use the target_result to say what edge the + * node route (meaning no real edge is traversed) then we use the target_result to say what edge the * segment should use * @param source source state to use to find the route * @param target target state which candidate in the next column to fetch the route for * @param route a place to put the edge segments as we create them * @param target_result in case we have a node to node route we have a no-op edge segment to return - * @return the vector of segments reprsenting the route between source and target + * @return the vector of segments representing the route between source and target */ bool MergeRoute(const State& source, const State& target, diff --git a/valhalla/meili/map_matcher_factory.h b/valhalla/meili/map_matcher_factory.h index c5025f638a..be206e3a7a 100644 --- a/valhalla/meili/map_matcher_factory.h +++ b/valhalla/meili/map_matcher_factory.h @@ -1,9 +1,6 @@ // -*- mode: c++ -*- #ifndef MMP_MAP_MATCHER_FACTORY_H_ #define MMP_MAP_MATCHER_FACTORY_H_ -#include - -#include #include diff --git a/valhalla/meili/routing.h b/valhalla/meili/routing.h index a9a7495689..34e3b21ccb 100644 --- a/valhalla/meili/routing.h +++ b/valhalla/meili/routing.h @@ -56,10 +56,8 @@ class Label : public sif::EdgeLabel { edge, cost, sortcost, - 0.0f, mode, 0, - sif::Cost{}, restriction_idx, true, false, diff --git a/valhalla/meili/state.h b/valhalla/meili/state.h index 054110c049..aaa3d369ac 100644 --- a/valhalla/meili/state.h +++ b/valhalla/meili/state.h @@ -43,7 +43,7 @@ class State { // Cache results label_idx_.clear(); uint16_t dest = 1; // dest at 0 is reserved for the origin - uint16_t found = 0; + [[maybe_unused]] uint16_t found = 0; for (const auto& stateid : stateids) { const auto it = results.find(dest); if (it != results.end()) { diff --git a/valhalla/meili/stateid.h b/valhalla/meili/stateid.h index 4dba146c3b..6938344b33 100644 --- a/valhalla/meili/stateid.h +++ b/valhalla/meili/stateid.h @@ -2,7 +2,6 @@ #define VALHALLA_MEILI_STATE_H_ #include -#include #include namespace valhalla { diff --git a/valhalla/meili/traffic_segment_matcher.h b/valhalla/meili/traffic_segment_matcher.h index 43c88a0a9f..0c4aef2d78 100644 --- a/valhalla/meili/traffic_segment_matcher.h +++ b/valhalla/meili/traffic_segment_matcher.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #include diff --git a/valhalla/meili/transition_cost_model.h b/valhalla/meili/transition_cost_model.h index 4d5a6bac73..b7961cf319 100644 --- a/valhalla/meili/transition_cost_model.h +++ b/valhalla/meili/transition_cost_model.h @@ -1,8 +1,6 @@ #ifndef MMP_TRANSITION_COST_MODEL_H_ #define MMP_TRANSITION_COST_MODEL_H_ -#include - #include #include #include diff --git a/valhalla/meili/viterbi_search.h b/valhalla/meili/viterbi_search.h index 240994e9fc..bf81f04b2e 100644 --- a/valhalla/meili/viterbi_search.h +++ b/valhalla/meili/viterbi_search.h @@ -2,7 +2,6 @@ #ifndef MMP_VITERBI_SEARCH_H_ #define MMP_VITERBI_SEARCH_H_ -#include #include #include #include diff --git a/valhalla/midgard/elevation_encoding.h b/valhalla/midgard/elevation_encoding.h new file mode 100644 index 0000000000..d50e4d952d --- /dev/null +++ b/valhalla/midgard/elevation_encoding.h @@ -0,0 +1,136 @@ +#ifndef VALHALLA_MIDGARD_ELEVATION_ENCODING_H_ +#define VALHALLA_MIDGARD_ELEVATION_ENCODING_H_ + +#include +#include +#include + +#include + +namespace valhalla { +namespace midgard { + +// Maximum sampling interval used when encoding elevation along an edge. +const uint32_t kMaxEdgeElevationSampling = 32; + +// NO_DATA elevation value (occurs where no elevation data exists, e.g. high latitudes) +constexpr int32_t ELEVATION_NO_DATA_VALUE = -32768; + +// Use fixed precision (0.25 degrees) +const double kElevationPrecision = 0.25f; +const double kInvElevationPrecision = 4.0; +inline int32_t to_fixed_precision(const double v) { + return static_cast((v * kInvElevationPrecision) + 0.5); +} +inline float from_fixed_precision(const int8_t v) { + return (v * kElevationPrecision); +} + +/** + * Get the sampling interval to use along an edge of a given length. To allow + * consistent encoding forward and reverse along an edge we use a sampling + * interval that breaks the edge into an integral number of samples. + * @return Returns the sampling interval in meters. + */ +inline double sampling_interval(const double length) { + uint32_t sample_count = length / kMaxEdgeElevationSampling; + return length / static_cast(sample_count + 1); +} + +/** + * Get the count of encoded elevations given the edge length. Computes the + * desired sampling interval (not to exceed kMaxEdgeElevationSampling) and + * computes the number of vertices. Excludes the first and last (these are + * not encoded). + * @param length Edge length. + * @return Returns the number of encoded elevations. + */ +inline uint32_t encoded_elevation_count(const uint32_t length) { + return static_cast(std::round(length / sampling_interval(length))) - 1; +} + +/** + * Encode elevation. Uses delta/offset encoding - storing the delta with fixed + * precision (0.25m). Does not encode the first or last shape point (these are + * stored in NodeInfo). + * @param elevation Elevation sampled along an edge. + * @param error Boolean value indicating if an error occurred (clamped a value + * that would have exceeded 1 byte). This is returned by this + * argument to the calling method. + * @return Returns encoding of elevation along the edge. Can be an empty + * vector if the elevation vector size is <= 2 (length of the edge + * is less than the max interval). + */ +inline std::vector encode_elevation(const std::vector& elevation, bool& error) { + // Return empty encoded vector if first or last value is NO_DATA + std::vector encoding; + if (elevation.front() == ELEVATION_NO_DATA_VALUE || elevation.back() == ELEVATION_NO_DATA_VALUE) { + return encoding; + } + + // First elevation is stored in NodeInfo...skip it but round it to nearest int + int prior = to_fixed_precision(elevation[0]); + + for (uint32_t i = 1; i < elevation.size() - 1; ++i) { + // Special case for NO_DATA values + if (elevation[i] == ELEVATION_NO_DATA_VALUE) { + encoding.push_back(0); + continue; + } + + // Convert to fixed precision offset. Clamp to 1 byte values. It should be + // very rare that the limit is exceeded, but if it is we try to make up for + // any shortfall on subsequent postings. + auto v = to_fixed_precision(elevation[i]); + auto delta = v - prior; + if (delta > 127) { + if (delta > 256) { + error = true; + } + delta = 127; + prior += 127; + } else if (delta < -128) { + if (delta < -256) { + error = true; + } + delta = -128; + prior -= 128; + } else { + prior = v; + } + encoding.push_back(static_cast(delta)); + } + return encoding; +} + +/** + * Decode elevation. The start and end elevation (at the end nodes of the edge) are + * provided. + * @param encoded Encoded elevation offsets. + * @param h1 Elevation at the start. + * @param h2 Elevation at the end. + * @return Returns elevation along the edge. + */ +inline std::vector +decode_elevation(std::vector& encoded, const float h1, const float h2, const bool forward) { + // Allocate elevation vector. Swap h1 and h2 if the edge is not forward + std::vector elevation(encoded.size() + 2); + elevation.front() = (forward) ? h1 : h2; + elevation.back() = forward ? h2 : h1; + + // Decode in forward direction. + for (uint32_t i = 0; i < encoded.size(); ++i) { + elevation[i + 1] = elevation[i] + from_fixed_precision(encoded[i]); + } + + // Reverse if the direction is not forward. + if (!forward) { + std::reverse(elevation.begin(), elevation.end()); + } + return elevation; +} + +} // namespace midgard +} // namespace valhalla + +#endif // VALHALLA_MIDGARD_ELEVATION_ENCODING_H_ diff --git a/valhalla/midgard/gridded_data.h b/valhalla/midgard/gridded_data.h index db77927053..11d4e0d55f 100644 --- a/valhalla/midgard/gridded_data.h +++ b/valhalla/midgard/gridded_data.h @@ -61,6 +61,14 @@ template class GriddedData : public Tiles { } } + float DataAt(size_t tileid, size_t metricid) const { + return data_[tileid][metricid]; + } + + float MaxValue(size_t metricidx) const { + return max_value_[metricidx]; + } + using contour_t = std::list; using feature_t = std::list; using contours_t = std::vector>; @@ -104,8 +112,13 @@ template class GriddedData : public Tiles { // Find the intersection along a tile edge auto intersect = [&tile_corners, &s](int p1, int p2) { auto ds = s[p2] - s[p1]; - return PointLL((s[p2] * tile_corners[p1].first - s[p1] * tile_corners[p2].first) / ds, - (s[p2] * tile_corners[p1].second - s[p1] * tile_corners[p2].second) / ds); + auto x = (s[p2] * tile_corners[p1].first - s[p1] * tile_corners[p2].first) / ds; + auto y = (s[p2] * tile_corners[p1].second - s[p1] * tile_corners[p2].second) / ds; + // we round here because connecting the cell line segments requires finding points via equality + // on some platforms the intersection arithmetic for adjacent cells results in floating point + // noise that differs for the intersection point on either side of the cell boundary, snapping + // to centimeter resolution lets us get usable results on those platforms (eg. aarch64) + return PointLL(std::round(x * 1e7) / 1e7, std::round(y * 1e7) / 1e7); }; // In the tight loop below, we need to decide where a contour intersects the triangles that make @@ -360,7 +373,6 @@ template class GriddedData : public Tiles { } // some info about the area the image covers - auto c = this->TileBounds().Center(); auto h = this->tilesize_ / 2; // for each contour for (auto& collection : contours) { @@ -409,6 +421,33 @@ template class GriddedData : public Tiles { return contours; } + /** + * Determine the smallest subgrid that contains all valid (i.e. non-max) values + + * @return array with 4 elements: minimum column, minimum row, maximum column, maximum row + */ + const std::array MinExtent() const { + // minx, miny, maxx, maxy + std::array box = {this->ncolumns_ / 2, this->nrows_ / 2, this->ncolumns_ / 2, + this->nrows_ / 2}; + + for (int32_t i = 0; i < this->nrows_; ++i) { + for (int32_t j = 0; j < this->ncolumns_; ++j) { + if (data_[this->TileId(j, i)][0] < max_value_[0] || + data_[this->TileId(j, i)][1] < max_value_[1]) { + // pad by 1 row/column as a sanity check + box[0] = std::min(std::max(j - 1, 0), box[0]); + box[1] = std::min(std::max(i - 1, 0), box[1]); + // +1 extra because range is exclusive + box[2] = std::max(std::min(j + 2, this->ncolumns_ - 1), box[2]); + box[3] = std::max(std::min(i + 2, this->ncolumns_ - 1), box[3]); + } + } + } + + return box; + } + protected: value_type max_value_; // Maximum value stored in the tile std::vector data_; // Data value within each tile diff --git a/valhalla/midgard/point2.h b/valhalla/midgard/point2.h index bee8138cc6..d54094faae 100644 --- a/valhalla/midgard/point2.h +++ b/valhalla/midgard/point2.h @@ -1,10 +1,7 @@ #pragma once #include -#include #include -#include -#include #include #include #include @@ -39,8 +36,6 @@ template class PointXY : public std::pair::second_type; using value_type = typename std::pair::first_type; - virtual ~PointXY() = default; - /** * Get the x component. * @return Returns the x component of the point. @@ -78,7 +73,7 @@ template class PointXY : public std::pair class PointXY : public std::pair& p1, const PointXY& p2) const { + PrecisionT IsLeft(const PointXY& p1, const PointXY& p2) const { return (p2.x() - p1.x()) * (y() - p1.y()) - (x() - p1.x()) * (p2.y() - p1.y()); } diff --git a/valhalla/midgard/pointll.h b/valhalla/midgard/pointll.h index 80ea08952d..d52e271c67 100644 --- a/valhalla/midgard/pointll.h +++ b/valhalla/midgard/pointll.h @@ -55,9 +55,6 @@ template class GeoPoint : public PointXY { GeoPoint(const PointXY& p) : PointXY(p) { } - virtual ~GeoPoint() { - } - /** * cast the lon lat to a 64bit value with 7 decimals places of precision the layout is: * bitfield { @@ -242,10 +239,9 @@ template class GeoPoint : public PointXY { * @param p2 End point of the segment. * @return Returns a positive value if this point is left of the segment. */ - virtual value_type IsLeft(const GeoPoint& p1, const GeoPoint& p2) const { + value_type IsLeft(const GeoPoint& p1, const GeoPoint& p2) const { return (p2.lng() - p1.lng()) * (lat() - p1.lat()) - (lng() - p1.lng()) * (p2.lat() - p1.lat()); } - using PointXY::IsLeft; /** * Tests whether this point is within a polygon. diff --git a/valhalla/midgard/polyline2.h b/valhalla/midgard/polyline2.h index 019b4093f7..814c0f2bea 100644 --- a/valhalla/midgard/polyline2.h +++ b/valhalla/midgard/polyline2.h @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -167,6 +166,17 @@ template class Polyline2 { std::equal(pts_.begin(), pts_.end(), other.pts_.begin()); } + /** + * Computes the hausdorff distance between the two linestring features + * @tparam container_t a container of point features comprising the linestring feature + * @param a linestring feature + * @param b linestring feature + * @return the hausdorff metric + */ + template + static typename container_t::value_type::first_type HausdorffDistance(const container_t& l1, + const container_t& l2); + protected: // Polyline points std::vector pts_; diff --git a/valhalla/midgard/sequence.h b/valhalla/midgard/sequence.h index 7d4a7c3488..e5b169db3c 100644 --- a/valhalla/midgard/sequence.h +++ b/valhalla/midgard/sequence.h @@ -8,10 +8,7 @@ #include #include #include -#include #include -#include -#include #include #include #include @@ -714,7 +711,7 @@ struct tar { if (!tried_index && from_index != nullptr) { tried_index = true; contents = from_index(name, position, mm.get(), size); - // if it was able to intialize from an index we bail + // if it was able to initialize from an index we bail if (!contents.empty()) { return; } diff --git a/valhalla/midgard/tiles.h b/valhalla/midgard/tiles.h index 38ed9102eb..6e339c8826 100644 --- a/valhalla/midgard/tiles.h +++ b/valhalla/midgard/tiles.h @@ -1,10 +1,8 @@ - #ifndef VALHALLA_MIDGARD_TILES_H_ #define VALHALLA_MIDGARD_TILES_H_ #include #include -#include #include #include @@ -123,7 +121,7 @@ template class Tiles { * @param y y coordinate * @return Returns the tile row. Returns -1 if outside the tile system bounds. */ - int32_t Row(const float y) const { + int32_t Row(const typename coord_t::value_type y) const { // Return -1 if outside the tile system bounds if (y < tilebounds_.miny() || y > tilebounds_.maxy()) { return -1; @@ -139,7 +137,7 @@ template class Tiles { * @param x x coordinate * @return Returns the tile column. Returns -1 if outside the tile system bounds. */ - int32_t Col(const float x) const { + int32_t Col(const typename coord_t::value_type x) const { // Return -1 if outside the tile system bounds if (x < tilebounds_.minx() || x > tilebounds_.maxx()) { return -1; @@ -169,7 +167,7 @@ template class Tiles { * @param y y (or lat) * @param x x (or lng) * @return Returns the tile Id. -1 (error is returned if the x,y is - * outside the bounding box of the tiling sytem). + * outside the bounding box of the tiling system). */ int32_t TileId(const typename coord_t::first_type y, const typename coord_t::first_type x) const { // Return -1 if totally outside the extent. @@ -230,8 +228,8 @@ template class Tiles { * @return The latitude, longitude extent of the specified tile. */ AABB2 TileBounds(const int32_t tileid) const { - Point2 base = Base(tileid); - return AABB2(base.x(), base.y(), base.x() + tilesize_, base.y() + tilesize_); + auto base = Base(tileid); + return {base.x(), base.y(), base.x() + tilesize_, base.y() + tilesize_}; } /** @@ -241,9 +239,9 @@ template class Tiles { * @return The latitude, longitude extent of the specified tile. */ AABB2 TileBounds(const int32_t col, const int32_t row) const { - float basex = tilebounds_.minx() + ((float)col * tilesize_); - float basey = tilebounds_.miny() + ((float)row * tilesize_); - return AABB2(basex, basey, basex + tilesize_, basey + tilesize_); + auto basex = tilebounds_.minx() + col * tilesize_; + auto basey = tilebounds_.miny() + row * tilesize_; + return {basex, basey, basex + tilesize_, basey + tilesize_}; } /** diff --git a/valhalla/midgard/util.h b/valhalla/midgard/util.h index a566515c42..6fba10bd87 100644 --- a/valhalla/midgard/util.h +++ b/valhalla/midgard/util.h @@ -2,11 +2,9 @@ #include #include -#include #include -#include +#include #include -#include #include #include #include @@ -299,7 +297,7 @@ float tangent_angle(size_t index, size_t first_segment_index = 0, size_t last_segment_index = std::numeric_limits::max()); -// useful in converting from one iteratable map to another +// useful in converting from one iterable map to another // for example: ToMap // >(some_ptree) /* @@ -403,7 +401,7 @@ resample_polyline(const std::vector& polyline, const float length, cons /** * Resample a polyline at uniform intervals using more accurate spherical interpolation between * points. The length and number of samples is specified. The interval is computed based on - * the number of samples and the algorithm guarantees that the secified number of samples + * the number of samples and the algorithm guarantees that the specified number of samples * is exactly produced. * @param polyline the list/vector of points in the line * @param length Length (meters) of the polyline @@ -468,6 +466,15 @@ template struct iterable_t { template bool intersect(const coord_t& u, const coord_t& v, const coord_t& a, const coord_t& b, coord_t& i); +/** + * Check whether a given point lies within a polygon. Uses the simplified winding number algorithm + * (http://www.graphicsgems.org/gemsiv/ptpoly_weiler/) + * + * @return true if the point lies within the given polygon + */ +template +bool point_in_poly(const coord_t& pt, const container_t& poly); + /** * Compute the area of a polygon. If your polygon is not twisted or self intersecting * this will return a positive value for counterclockwise wound polygons and negative otherwise. @@ -698,7 +705,7 @@ unaligned_read(const void* ptr) { } /** - * For some variables, an ivalid value needs to be set as: the maximum value it's type can get + * For some variables, an invalid value needs to be set as: the maximum value it's type can get * @returns the invalid value of the type */ template numeric_t invalid() { @@ -706,7 +713,7 @@ template numeric_t invalid() { } /** - * For some variables, an ivalid value needs to be set as: the maximum value it's type can get + * For some variables, an invalid value needs to be set as: the maximum value it's type can get * @returns true when the value is invalid */ template bool is_invalid(numeric_t value) { @@ -714,7 +721,7 @@ template bool is_invalid(numeric_t value) { } /** - * For some variables, an ivalid value needs to be set as: the maximum value it's type can get + * For some variables, an invalid value needs to be set as: the maximum value it's type can get * @returns true when the value is valid */ template bool is_valid(numeric_t value) { diff --git a/valhalla/mjolnir/admin.h b/valhalla/mjolnir/admin.h index a96eb1c5a8..725e3b0569 100644 --- a/valhalla/mjolnir/admin.h +++ b/valhalla/mjolnir/admin.h @@ -17,14 +17,19 @@ using namespace valhalla::baldr; using namespace valhalla::midgard; +namespace bg = boost::geometry; namespace valhalla { namespace mjolnir { // Geometry types for admin queries -typedef boost::geometry::model::d2::point_xy point_type; -typedef boost::geometry::model::polygon polygon_type; -typedef boost::geometry::model::multi_polygon multi_polygon_type; +typedef bg::model::d2::point_xy point_type; +typedef bg::model::polygon polygon_type; +typedef bg::model::multi_polygon multi_polygon_type; +typedef bg::index::rtree< + std::tuple, multi_polygon_type, std::vector, bool>, + bg::index::rstar<16>> + language_poly_index; /** * Get the dbhandle of a sqlite db. Used for timezones and admins DBs. @@ -51,6 +56,16 @@ uint32_t GetMultiPolyId(const std::multimap& polys */ uint32_t GetMultiPolyId(const std::multimap& polys, const PointLL& ll); +/** + * Get the vector of languages for this LL. Used by admin areas. Checks if the pointLL is covered_by + * the poly. + * @param language_polys tuple that contains a language, poly, is_default_language. + * @param ll point that needs to be checked. + * @return Returns the vector of pairs {language, is_default_language} + */ +std::vector> +GetMultiPolyIndexes(const language_poly_index& language_ploys, const PointLL& ll); + /** * Get the timezone polys from the db * @param db_handle sqlite3 db handle @@ -67,13 +82,20 @@ std::multimap GetTimeZones(sqlite3* db_handle, * @param polys unordered multimap of admin polys * @param drive_on_right unordered map that indicates if a country drives on right side of the * road + * @param default_languages ordered map that is used for lower admins that have an + * default language set + * @param language_polys ordered map that is used for lower admins that have an + * default language set + * @param languages_only should we only process the languages with this query */ void GetData(sqlite3* db_handle, sqlite3_stmt* stmt, const std::string& sql, GraphTileBuilder& tilebuilder, std::multimap& polys, - std::unordered_map& drive_on_right); + std::unordered_map& drive_on_right, + language_poly_index& language_polys, + bool languages_only); /** * Get the admin polys that intersect with the tile bounding box. @@ -82,13 +104,18 @@ void GetData(sqlite3* db_handle, * road * @param allow_intersection_names unordered map that indicates if we call out intersections * names for this country - * @param aabb bb of the tile - * @param tilebuilder Graph tile builder + * @param default_languages ordered map that is used for lower admins that have an + * default language set + * @param language_polys ordered map that is used for lower admins that have an + * default language set + * @param aabb bb of the tile + * @param tilebuilder Graph tile builder */ std::multimap GetAdminInfo(sqlite3* db_handle, std::unordered_map& drive_on_right, std::unordered_map& allow_intersection_names, + language_poly_index& language_polys, const AABB2& aabb, GraphTileBuilder& tilebuilder); @@ -100,4 +127,5 @@ std::unordered_map> GetCountryAccess(sqlite3* db_h } // namespace mjolnir } // namespace valhalla + #endif // VALHALLA_MJOLNIR_ADMIN_H_ diff --git a/valhalla/mjolnir/adminbuilder.h b/valhalla/mjolnir/adminbuilder.h index c63c179e5f..553107652d 100644 --- a/valhalla/mjolnir/adminbuilder.h +++ b/valhalla/mjolnir/adminbuilder.h @@ -2,9 +2,6 @@ #include -#include -#include -#include #include #include diff --git a/valhalla/mjolnir/adminconstants.h b/valhalla/mjolnir/adminconstants.h index add8004e0f..73e18a497b 100644 --- a/valhalla/mjolnir/adminconstants.h +++ b/valhalla/mjolnir/adminconstants.h @@ -30,10 +30,8 @@ enum class AccessTypes : uint16_t { // 0 indicates no access. const std::unordered_map> kCountryAccess{{"Australia", - {-1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), - (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), - (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), - (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, -1}}, + {-1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, -1, -1, + -1, -1}}, {"Austria", {(kAutoAccess | kTruckAccess | kBusAccess | kHOVAccess | kTaxiAccess | kMotorcycleAccess), @@ -49,7 +47,8 @@ const std::unordered_map> kMotorcycleAccess), (kAutoAccess | kTruckAccess | kBusAccess | kHOVAccess | kTaxiAccess | kMotorcycleAccess), - -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), + (kPedestrianAccess | kWheelchairAccess | kBicycleAccess | kMopedAccess), -1, + (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), (kPedestrianAccess | kWheelchairAccess), (kPedestrianAccess | kWheelchairAccess | kBicycleAccess | kMopedAccess), (kPedestrianAccess | kWheelchairAccess | kBicycleAccess | kMopedAccess), -1}}, @@ -88,14 +87,15 @@ const std::unordered_map> kMotorcycleAccess), (kAutoAccess | kTruckAccess | kBusAccess | kHOVAccess | kTaxiAccess | kMotorcycleAccess), - -1, -1, -1, -1, -1, -1, -1}}, + -1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, + -1}}, {"Iceland", {-1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, -1}}, {"Ireland", - {-1, -1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), - (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, -1}}, + {-1, -1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, + -1, -1}}, {"Italy", {-1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, -1, -1, -1}}, @@ -105,31 +105,29 @@ const std::unordered_map> {-1, -1, -1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), (kPedestrianAccess | kWheelchairAccess | kBicycleAccess | kMopedAccess), -1}}, {"Norway", - {-1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), + {-1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess | kMopedAccess), + (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, -1}}, {"Northern Ireland", - {-1, -1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), - (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, -1}}, + {-1, -1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, + -1, -1}}, {"Oman", {-1, -1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, -1}}, {"Philippines", {-1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), - (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, -1}}, + (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), + (kPedestrianAccess | kWheelchairAccess | kBicycleAccess | kMopedAccess), -1}}, {"Poland", - {(kAutoAccess | kTruckAccess | kBusAccess | kHOVAccess | kTaxiAccess | - kMotorcycleAccess), - (kAutoAccess | kTruckAccess | kBusAccess | kHOVAccess | kTaxiAccess | - kMotorcycleAccess), - -1, -1, -1, -1, -1, -1, -1}}, + {-1, -1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), + (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), + (kPedestrianAccess | kWheelchairAccess | kBicycleAccess | kMopedAccess), -1}}, {"Romania", - {(kAutoAccess | kTruckAccess | kBusAccess | kHOVAccess | kTaxiAccess | - kMotorcycleAccess | kBicycleAccess), - (kAutoAccess | kTruckAccess | kBusAccess | kHOVAccess | kTaxiAccess | - kMotorcycleAccess | kBicycleAccess), - -1, -1, -1, -1, -1, + {-1, -1, -1, -1, -1, + (kPedestrianAccess | kWheelchairAccess | kBicycleAccess | kMopedAccess), + (kBicycleAccess | kMopedAccess), (kPedestrianAccess | kWheelchairAccess | kBicycleAccess | kMopedAccess), -1}}, {"Russia", {-1, -1, -1, -1, -1, -1, (kMopedAccess | kBicycleAccess), -1, -1}}, {"Alba / Scotland", @@ -143,7 +141,7 @@ const std::unordered_map> -1, -1, -1, -1, -1, -1, -1}}, // Spain allows bicycle access on trunk and trunk link without motorroad = yes {"Spain", - {-1, -1, -1, -1, -1, -1, -1, + {-1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess | kMopedAccess), -1}}, {"Sweden", {-1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), @@ -155,9 +153,17 @@ const std::unordered_map> kMotorcycleAccess), (kAutoAccess | kTruckAccess | kBusAccess | kHOVAccess | kTaxiAccess | kMotorcycleAccess), - -1, -1, -1, -1, (kBicycleAccess | kMopedAccess), -1, -1}}, + -1, -1, -1, -1, + (kPedestrianAccess | kWheelchairAccess | kBicycleAccess | kMopedAccess), -1, + -1}}, + {"ประเทศไทย", + {-1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), + (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), + (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), + (kPedestrianAccess | kWheelchairAccess | kBicycleAccess | kMopedAccess), -1}}, {"Türkiye", - {-1, -1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess), -1, -1, -1}}, + {-1, -1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess), + (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, -1}}, // Ukraine - no overrides {"United States", {-1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), @@ -167,6 +173,21 @@ const std::unordered_map> {"Cymru / Wales", {-1, -1, -1, -1, -1, (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), (kPedestrianAccess | kWheelchairAccess | kBicycleAccess), -1, -1}}}; + +const std::unordered_map> + kSupportedLanguages{{"Cymru / Wales", {2, "cy;en"}}, + {"United Kingdom", {2, "en"}}, + {"Ireland", {4, "ga"}}, + {"Northern Ireland", {4, "ga"}}, + {"Japan", {2, "ja;en"}}, + {"Canada", {2, "en;fr"}}, + {"Belarus", {2, "ru;be"}}, + {"Singapore", {2, "en;zh;ms;ta"}}, + {"Saudi Arabia", {2, "ar;en"}}, + {"Catalonia", {4, "ca"}}, + {"Valencian Community", {4, "ca"}}, + {"Spain", {2, "es"}}}; + } // namespace mjolnir } // namespace valhalla #endif // VALHALLA_MJOLNIR_ADMINCONSTANTS_H_ diff --git a/valhalla/mjolnir/bssbuilder.h b/valhalla/mjolnir/bssbuilder.h index b26eaf6fc0..f34f37ca7b 100644 --- a/valhalla/mjolnir/bssbuilder.h +++ b/valhalla/mjolnir/bssbuilder.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_BSSBUILDER_H #include -#include #include namespace valhalla { diff --git a/valhalla/mjolnir/complexrestrictionbuilder.h b/valhalla/mjolnir/complexrestrictionbuilder.h index af46fca344..04bd8594ea 100644 --- a/valhalla/mjolnir/complexrestrictionbuilder.h +++ b/valhalla/mjolnir/complexrestrictionbuilder.h @@ -2,9 +2,6 @@ #define VALHALLA_MJOLNIR_COMPLEXRESTRICTIONBUILDER_H_ #include -#include -#include -#include #include #include diff --git a/valhalla/mjolnir/directededgebuilder.h b/valhalla/mjolnir/directededgebuilder.h index 9a2752ce20..5084744f9f 100644 --- a/valhalla/mjolnir/directededgebuilder.h +++ b/valhalla/mjolnir/directededgebuilder.h @@ -19,23 +19,22 @@ class DirectedEdgeBuilder : public baldr::DirectedEdge { public: /** * Constructor with arguments. - * @param way OSM way info generated from parsing OSM tags with Lua. - * @param endnode GraphId of the end node of this directed edge. - * @param length Length in meters. - * @param speed Average speed in kph. - * @param truck_speed Truck speed limit in kph. - * @param use Use of the edge. - * @param rc Road class / importance - * @param localidx Index of the edge (from the node) on the local level - * @param signal Traffic signal - * @param stop_sign Stop sign - * @param yield_sign Yield sign - * @param minor Does the stop or yield only apply to minor roads? - * @param restrictions Mask of simple turn restrictions at the end node - * of this directed edge. - * @param bike_network Mask of bike_networks from relations. - * @param reclass_ferry Reclassify ferry boolean; Allows us to drop destination only attribution - * or anything that would prevent seeing a ferry connection + * @param way OSM way info generated from parsing OSM tags with Lua. + * @param endnode GraphId of the end node of this directed edge. + * @param length Length in meters. + * @param speed Average speed in kph. + * @param truck_speed Truck speed limit in kph. + * @param use Use of the edge. + * @param rc Road class / importance + * @param localidx Index of the edge (from the node) on the local level + * @param signal Traffic signal + * @param stop_sign Stop sign + * @param yield_sign Yield sign + * @param minor Does the stop or yield only apply to minor roads? + * @param restrictions Mask of simple turn restrictions at the end node + * of this directed edge. + * @param bike_network Mask of bike_networks from relations. + * @param remove_destonly Drop dest_only attribution for reclassified ferry paths */ DirectedEdgeBuilder(const OSMWay& way, const baldr::GraphId& endnode, @@ -52,7 +51,7 @@ class DirectedEdgeBuilder : public baldr::DirectedEdge { const bool minor, const uint32_t restrictions, const uint32_t bike_network, - const bool reclass_ferry); + const bool remove_destonly); }; } // namespace mjolnir diff --git a/valhalla/mjolnir/edgeinfobuilder.h b/valhalla/mjolnir/edgeinfobuilder.h index 0b8c8a3101..47d1d76b3c 100644 --- a/valhalla/mjolnir/edgeinfobuilder.h +++ b/valhalla/mjolnir/edgeinfobuilder.h @@ -2,8 +2,6 @@ #define VALHALLA_MJOLNIR_EDGEINFOBUILDER_H_ #include -#include -#include #include #include @@ -50,6 +48,12 @@ class EdgeInfoBuilder { */ void set_speed_limit(const uint32_t speed_limit); + /** + * Sets the elevation flag. + * @param elevation Flag indicating whether the edge has elevation data. + */ + void set_has_elevation(const bool elevation); + /** * Get the bike network mask for this directed edge. * @return Returns the bike network mask for this directed edge. @@ -90,6 +94,12 @@ class EdgeInfoBuilder { */ void set_encoded_shape(const std::string& encoded_shape); + /** + * Set encoded elevation. + * @param encoded_elevation Encoded elevation + */ + void set_encoded_elevation(const std::vector& encoded_elevation); + /** * Get the size of this edge info (without padding). * @return Returns the size in bytes of this object. @@ -103,6 +113,20 @@ class EdgeInfoBuilder { */ std::size_t SizeOf() const; + /** + * Check if the given name_info exists in this edge info. + * @param offset The offset of the name info. + * @return Returns true if the name info exists, false otherwise. + */ + bool has_name_info(uint32_t offset) const { + for (const auto& name : name_info_list_) { + if (name.name_offset_ == offset) { + return true; + } + } + return false; + } + protected: // Fixed size information baldr::EdgeInfo::EdgeInfoInner ei_{}; @@ -117,6 +141,9 @@ class EdgeInfoBuilder { // Lat,lng shape of the edge std::string encoded_shape_; + // Encoded elevation + std::vector encoded_elevation_; + friend std::ostream& operator<<(std::ostream& os, const EdgeInfoBuilder& id); }; diff --git a/valhalla/mjolnir/ferry_connections.h b/valhalla/mjolnir/ferry_connections.h index 97bd2201f5..ccaa2515df 100644 --- a/valhalla/mjolnir/ferry_connections.h +++ b/valhalla/mjolnir/ferry_connections.h @@ -2,9 +2,7 @@ #define VALHALLA_MJOLNIR_FERRY_CONNECTIONS_H_ #include -#include #include -#include #include #include @@ -32,14 +30,18 @@ constexpr uint32_t kPermanent = 1; // be "adjacent" to an node that is permanently labeled. constexpr uint32_t kTemporary = 2; +constexpr uint32_t kFerryUpClass = static_cast(baldr::RoadClass::kPrimary); + // NodeLabel - for simple shortest path struct NodeLabel { float cost; uint32_t node_index; uint32_t pred_node_index; + uint32_t way_index; + bool dest_only; - NodeLabel(const float c, const uint32_t n, const uint32_t p) - : cost(c), node_index(n), pred_node_index(p) { + NodeLabel(const float c, const uint32_t n, const uint32_t p, const uint32_t w, const bool d) + : cost(c), node_index(n), pred_node_index(p), way_index(w), dest_only(d) { } }; @@ -53,39 +55,6 @@ struct NodeStatusInfo { } }; -/** - * Get the best classification for any driveable non-ferry and non-link - * edges from a node. Skip any reclassified ferry edges - * @param edges The file backed list of edges in the graph. - * @return Returns the best (most important) classification - */ -uint32_t GetBestNonFerryClass(const std::map& edges); - -/** - * Form the shortest path from the start node until a node that - * touches the specified road classification. - */ -uint32_t ShortestPath(const uint32_t start_node_idx, - const uint32_t node_idx, - sequence& ways, - sequence& way_nodes, - sequence& edges, - sequence& nodes, - const bool inbound, - const uint32_t rc); - -/** - * Check if the ferry included in this node bundle is short. Must be - * just one edge and length < 2 km. This prevents forming connections - * to what are most likely river crossing ferries. - */ -bool ShortFerry(const uint32_t node_index, - node_bundle& bundle, - sequence& edges, - sequence& nodes, - sequence& ways, - sequence& way_nodes); - /** * Reclassify edges from a ferry along the shortest path to the * specified road classification. @@ -93,8 +62,7 @@ bool ShortFerry(const uint32_t node_index, void ReclassifyFerryConnections(const std::string& ways_file, const std::string& way_nodes_file, const std::string& nodes_file, - const std::string& edges_file, - const uint32_t rc); + const std::string& edges_file); } // namespace mjolnir } // namespace valhalla diff --git a/valhalla/mjolnir/graphbuilder.h b/valhalla/mjolnir/graphbuilder.h index 609a415719..44860001fe 100644 --- a/valhalla/mjolnir/graphbuilder.h +++ b/valhalla/mjolnir/graphbuilder.h @@ -7,9 +7,11 @@ #include #include +#include #include #include +#include #include namespace valhalla { @@ -35,8 +37,9 @@ class GraphBuilder { * not in memory * @param complex_to_restriction_file where to store the to complex restrictions so they are not * in memory - * @param pronunciation_file where to store the to pronunciations so they are not - * in memory + * @param linguistic_node_file where to store the to linguistic info so they are not in + * memory + * */ static void Build(const boost::property_tree::ptree& pt, const OSMData& osmdata, @@ -46,7 +49,7 @@ class GraphBuilder { const std::string& edges_file, const std::string& complex_from_restriction_file, const std::string& complex_to_restriction_file, - const std::string& pronunciation_file, + const std::string& linguistic_node_file, const std::map& tiles); static std::map BuildEdges(const ptree& conf, @@ -57,44 +60,84 @@ class GraphBuilder { static std::string GetRef(const std::string& way_ref, const std::string& relation_ref); - static void GetPronunciationTokens(const OSMData& osmdata, - const uint32_t ipa_index, - const uint32_t nt_sampa_index, - const uint32_t katakana_index, - const uint32_t jeita_index, - std::vector& ipa_tokens, - std::vector& nt_sampa_tokens, - std::vector& katakana_tokens, - std::vector& jeita_tokens, - bool is_node_pronunciation = false); - - static void AddPronunciation(const baldr::PronunciationAlphabet alphabet, - const std::string& phoneme, - std::vector& pronunciations, - uint32_t& count); + static void + GetPronunciationTokens(const UniqueNames& uniquenames, + const std::vector>& default_languages, + const uint32_t ipa_index, + const uint32_t ipa_lang_index, + const uint32_t nt_sampa_index, + const uint32_t nt_sampa_lang_index, + const uint32_t katakana_index, + const uint32_t katakana_lang_index, + const uint32_t jeita_index, + const uint32_t jeita_lang_index, + std::vector& ipa_tokens, + std::vector& ipa_langs, + std::vector& nt_sampa_tokens, + std::vector& nt_sampa_langs, + std::vector& katakana_tokens, + std::vector& katakana_langs, + std::vector& jeita_tokens, + std::vector& jeita_langs); + + static void AddPronunciationsWithLang(std::vector& pronunciations, + std::map& lang_map, + const baldr::PronunciationAlphabet verbal_type, + const std::vector& pronunciation_tokens, + const std::vector& pronunciation_langs, + const std::vector& token_langs, + const size_t token_size, + const size_t key); + + static void + AddLanguage(const size_t index, const baldr::Language& lang, std::vector& linguistics); static void BuildPronunciations(const std::vector& ipa_tokens, + const std::vector& ipa_langs, const std::vector& nt_sampa_tokens, + const std::vector& nt_sampa_langs, const std::vector& katakana_tokens, + const std::vector& katakana_langs, const std::vector& jeita_tokens, - const size_t index, + const std::vector& jeita_langs, + const std::vector& token_langs, + const size_t token_size, + const size_t key, std::vector& pronunciations, + std::map& lang_map, bool add_ipa, bool add_nt_sampa, bool add_katakana, - bool add_jeita, - uint32_t& count); - - static bool CreateSignInfoList(const OSMNode& node, - const OSMWay& way, - const OSMPronunciation& pronunciation, - const OSMData& osmdata, - std::vector& exits, - std::vector& pronunciations, - bool fork, - bool forward, - bool ramp, - bool tc); + bool add_jeita); + + static void GetShieldTokens(const OSMData& osmdata, + const uint32_t shield_text_color_index, + const uint32_t shield_name_index, + const uint32_t shield_display_ref_index, + std::vector& shield_text_color_tokens, + std::vector& shield_name_tokens, + std::vector& display_ref_tokens); + + static void AddShields(const std::vector& shield_text_color_tokens, + const std::vector& shield_name_tokens, + const std::vector& display_ref_tokens, + const size_t index, + uint32_t& count); + + static bool + CreateSignInfoList(const OSMNode& node, + const OSMWay& way, + const std::map, uint32_t>& pronunciationMap, + const std::map, uint32_t>& langMap, + const OSMData& osmdata, + const std::vector>& default_languages, + midgard::sequence& linguistic_node, + std::vector& exits, + std::vector& linguistics, + bool fork, + bool forward, + bool ramp, + bool tc); }; } // namespace mjolnir } // namespace valhalla diff --git a/valhalla/mjolnir/graphenhancer.h b/valhalla/mjolnir/graphenhancer.h index 327d91d7b1..5ea50457aa 100644 --- a/valhalla/mjolnir/graphenhancer.h +++ b/valhalla/mjolnir/graphenhancer.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_GRAPHENHANCER_H #include -#include #include namespace valhalla { diff --git a/valhalla/mjolnir/graphfilter.h b/valhalla/mjolnir/graphfilter.h index 9c36e06d60..c992a2439b 100644 --- a/valhalla/mjolnir/graphfilter.h +++ b/valhalla/mjolnir/graphfilter.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_GRAPHFILTER_H #include -#include #include diff --git a/valhalla/mjolnir/graphtilebuilder.h b/valhalla/mjolnir/graphtilebuilder.h index 74bdeccd06..20a330fdea 100644 --- a/valhalla/mjolnir/graphtilebuilder.h +++ b/valhalla/mjolnir/graphtilebuilder.h @@ -4,13 +4,10 @@ #include #include #include -#include -#include #include #include #include #include -#include #include #include @@ -29,6 +26,7 @@ #include #include #include +#include namespace valhalla { namespace mjolnir { @@ -50,7 +48,7 @@ class GraphTileBuilder : public baldr::GraphTile { * @param graphid GraphId used to determine the tileid and level * @param deserialize If true the existing objects in the tile are * converted into builders so they can be added to. - * @param serialize_turn_lanes If true, the offsets are truely text offsets. + * @param serialize_turn_lanes If true, the offsets are truly text offsets. * If false, the offsets are indexes into unique name file */ GraphTileBuilder(const std::string& tile_dir, @@ -144,7 +142,7 @@ class GraphTileBuilder : public baldr::GraphTile { */ void AddSigns(const uint32_t idx, const std::vector& signs, - const std::vector& pronunciations); + const std::vector& linguistics); /** * Add sign information. @@ -192,6 +190,18 @@ class GraphTileBuilder : public baldr::GraphTile { const baldr::GraphId& nodeb, uint32_t& edge_info_offset); + /** + * Process Tagged values for the edge. + * @param edgeindex The edgeindex we are processing. + * @param names The names to add. + * @param name_count The current name count. + * @param name_info_list The list to add the name infos + */ + void ProcessTaggedValues(const uint32_t edgeindex, + const std::vector& names, + size_t& name_count, + std::vector& name_info_list); + /** * Add the edge info to the tile. * @@ -210,7 +220,7 @@ class GraphTileBuilder : public baldr::GraphTile { * @param spd Speed limit. [kph] * @param lls The shape of the target edge. * @param names The names of the target edge. - * @param pronunciations The pronunciations of the target edge. + * @param linguistics The pronunciations and languages of the target edge. * @param types Bits indicating if the name is a ref vs a name. * @param added Set to true if the target edge was newly added to the list, * set to false if the target edge was already in the list. @@ -220,8 +230,8 @@ class GraphTileBuilder : public baldr::GraphTile { */ template uint32_t AddEdgeInfo(const uint32_t edgeindex, - const baldr::GraphId& nodea, - const baldr::GraphId& nodeb, + baldr::GraphId nodea, + baldr::GraphId nodeb, const uint64_t wayid, const float elev, const uint32_t bn, @@ -229,7 +239,7 @@ class GraphTileBuilder : public baldr::GraphTile { const shape_container_t& lls, const std::vector& names, const std::vector& tagged_values, - const std::vector& pronunciations, + const std::vector& linguistics, const uint16_t types, bool& added, bool diff_names = false); @@ -251,8 +261,8 @@ class GraphTileBuilder : public baldr::GraphTile { * @param spd Speed limit. * @param llstr The shape of the target edge as an encoded string. * @param names The names of the target edge. - * @param tagged_values The tagged names of the target edge. - * @param pronunciations The pronunciations of the target edge. + * @param tagged_values The tagged names of the target edge. + * @param linguistics The pronunciations and languages of the target edge. * @param types Bits indicating if the name is a ref vs a name. * @param added Set to true if the target edge was newly added to the list, * set to false if the target edge was already in the list. @@ -261,8 +271,8 @@ class GraphTileBuilder : public baldr::GraphTile { * @return The edge info offset that will be stored in the directed edge. */ uint32_t AddEdgeInfo(const uint32_t edgeindex, - const baldr::GraphId& nodea, - const baldr::GraphId& nodeb, + baldr::GraphId nodea, + baldr::GraphId nodeb, const uint64_t wayid, const float elev, const uint32_t bn, @@ -270,7 +280,7 @@ class GraphTileBuilder : public baldr::GraphTile { const std::string& llstr, const std::vector& names, const std::vector& tagged_values, - const std::vector& pronunciations, + const std::vector& linguistics, const uint16_t types, bool& added, bool diff_names = false); @@ -282,12 +292,16 @@ class GraphTileBuilder : public baldr::GraphTile { void set_mean_elevation(const float elev); /** - * Set the mean elevation to the EdgeInfo given the edge info offset. This requires - * a serialized tile builder. + * Set mean elevation and encoded elevation within the EdgeInfo given the edge info offset. + * This requires a serialized tile builder. * @param offset Edge info offset. - * @param elev Mean elevation. + * @param mean_elevation Mean elevation. + * @param encoded_elevation Encoded elevation. + * @return Returns size of the updated EdgeInfo data. */ - void set_mean_elevation(const uint32_t offset, const float elev); + uint32_t set_elevation(const uint32_t offset, + const float mean_elevation, + const std::vector& encoded_elevation); /** * Add a name to the text list. @@ -485,6 +499,23 @@ class GraphTileBuilder : public baldr::GraphTile { */ void UpdatePredictedSpeeds(const std::vector& directededges); + /** + * Adds a landmark to the given edge id by modifying its edgeinfo to add a name and tagged value + * + * @param edge_id the edge id to modify + * @param landmark the landmark to associate to the edge + */ + void AddLandmark(const baldr::GraphId& edge_id, const Landmark& landmark); + + /** + * Is there an opposing edge with matching edgeinfo offset. The end node of the directed edge + * must be in the same tile as the directed edge. This is called during the building of the + * tiles; therefore, we can't use GetOpposingEdgeId as it has not been set yet. + * @param tile Graph tile of the edge + * @param directededge Directed edge to check. + */ + bool OpposingEdgeInfoDiffers(const graph_tile_ptr& tile, const DirectedEdge* edge); + protected: struct EdgeTupleHasher { std::size_t operator()(const edge_tuple& k) const { diff --git a/valhalla/mjolnir/graphvalidator.h b/valhalla/mjolnir/graphvalidator.h index d2646ad0c8..70d75d6f8d 100644 --- a/valhalla/mjolnir/graphvalidator.h +++ b/valhalla/mjolnir/graphvalidator.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_GRAPHOPTIMIZER_H #include -#include namespace valhalla { namespace mjolnir { diff --git a/valhalla/mjolnir/hierarchybuilder.h b/valhalla/mjolnir/hierarchybuilder.h index 424fe4d712..25425c8e4e 100644 --- a/valhalla/mjolnir/hierarchybuilder.h +++ b/valhalla/mjolnir/hierarchybuilder.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_HIERARCHYBUILDER_H #include -#include namespace valhalla { namespace mjolnir { diff --git a/valhalla/mjolnir/ingest_transit.h b/valhalla/mjolnir/ingest_transit.h index 6fa3f984dc..dbb597a3b7 100644 --- a/valhalla/mjolnir/ingest_transit.h +++ b/valhalla/mjolnir/ingest_transit.h @@ -2,11 +2,7 @@ #include #include -#include #include -#include -#include -#include #include #include diff --git a/valhalla/mjolnir/landmarks.h b/valhalla/mjolnir/landmarks.h new file mode 100644 index 0000000000..02a993dd30 --- /dev/null +++ b/valhalla/mjolnir/landmarks.h @@ -0,0 +1,100 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +namespace valhalla { +namespace mjolnir { + +struct LandmarkDatabase { +public: + /** + * LandmarkDatabase constructor. + * Create a new LandmarkDatabase object that connects to the SQLite landmark database + * with the given `db_name`. The `read_only` parameter determines whether the + * database connection is read-only or read-write. + * + * @param db_name The file path of the SQLite database to connect to. + * @param read_only Set to true to open the database in read-only mode, false for read-write. + */ + LandmarkDatabase(const std::string& db_name, bool read_only); + + /** + * Insert a new landmark into the database. + * This function inserts a new landmark into the database with the given `name`, `type`, + * `lng`, and `lat`. The landmark's primary key will be automatically assigned by the database. + * + * @param name The name of the landmark. + * @param type The type of the landmark (LandmarkType enum). + * @param lng The longitude of the landmark. + * @param lat The latitude of the landmark. + */ + void insert_landmark(const std::string& name, + const baldr::LandmarkType& type, + const double lng, + const double lat); + + /** + * Retrieve a vector of landmarks from the database within the specified bounding box. + * Bounding box is a rectangle area defined by min latitude, max latitude, min longitude, and max + * longitude. + * + * @param minlng The minimum longitude of the bounding box (min x). + * @param minlat The minimum latitude of the bounding box (min y). + * @param maxlng The maximum longitude of the bounding box (max x). + * @param maxlat The maximum latitude of the bounding box (max y). + * @return A vector of Landmark objects within the specified bounding box. + */ + std::vector get_landmarks_by_bbox(const double minlng, + const double minlat, + const double maxlng, + const double maxlat); + + /** + * Retrieve a vector of landmarks that match the provided primary keys. + * NOTE: The return size may be less than the size of the input if some of the provided + * primary keys do not exist in the database. In such cases, the function will skip the missing IDs + * without throwing an exception. + * + * @param pkeys A vector of int64_t representing the primary keys of the landmarks to retrieve. + * @return A vector of Landmark objects matching the given primary keys. + */ + std::vector get_landmarks_by_ids(const std::vector& pkeys); + +protected: + struct db_pimpl; + std::shared_ptr pimpl; +}; + +/** + * Build a SQLite landmark database and insert landmarks data from the PBF files to the database. + * This function reads landmark data from PBF files specified in the `input_files` vector. + * It then parses the data to extract landmark nodes and stores the landmarks in the database. + * The database is automatically built based on the configuration settings provided in `pt`. + * + * @param pt The configuration settings for the landmark building process. + * @param input_files A vector of file paths to the PBF input files. + * @return True if landmarks were successfully built and inserted into the database, false otherwise. + */ +bool BuildLandmarkFromPBF(const boost::property_tree::ptree& pt, + const std::vector& input_files); + +/** + * Add landmarks from the landmark database to graph tiles. + * This function finds landmarks in each graph tile, retrieves them from the landmark database, + * associate them with nearby edges, and update the graph tiles with these associations of landmark - + * edge pairs. + * + * @param pt The configuration settings + * @return + */ +bool AddLandmarks(const boost::property_tree::ptree& pt); + +} // namespace mjolnir + +} // namespace valhalla diff --git a/valhalla/mjolnir/linkclassification.h b/valhalla/mjolnir/linkclassification.h index ac3b01dd4a..8e03a153d5 100644 --- a/valhalla/mjolnir/linkclassification.h +++ b/valhalla/mjolnir/linkclassification.h @@ -1,9 +1,7 @@ #ifndef VALHALLA_MJOLNIR_LINK_CLASSIFICATION_H_ #define VALHALLA_MJOLNIR_LINK_CLASSIFICATION_H_ -#include #include -#include namespace valhalla { namespace mjolnir { @@ -16,6 +14,7 @@ void ReclassifyLinks(const std::string& ways_file, const std::string& edges_file, const std::string& way_nodes_file, const OSMData& osmdata, + bool reclassify_links, bool infer_turn_channels); } // namespace mjolnir } // namespace valhalla diff --git a/valhalla/mjolnir/node_expander.h b/valhalla/mjolnir/node_expander.h index 00fbc7f716..29d08bceef 100644 --- a/valhalla/mjolnir/node_expander.h +++ b/valhalla/mjolnir/node_expander.h @@ -2,8 +2,6 @@ #define VALHALLA_MJOLNIR_NODE_EXPANDER_H_ #include -#include -#include #include #include @@ -53,9 +51,9 @@ struct Edge { uint64_t shortlink : 1; // true if this is a link edge and is // short enough it may be internal to // an intersection - uint64_t driveable_ferry : 1; + uint64_t drivable_ferry : 1; uint64_t reclass_ferry : 1; // Has edge been reclassified due to - // ferry connection + // ferry connection, will remove dest_only tag from DE uint64_t turn_channel : 1; // Link edge should be a turn channel uint64_t way_begin : 1; // True if first edge of way uint64_t way_end : 1; // True if last edge of way @@ -103,12 +101,12 @@ struct Edge { e.attributes.llcount = 1; e.attributes.importance = static_cast(way.road_class()); e.attributes.link = way.link(); - e.attributes.driveable_ferry = (way.ferry() || way.rail()) && (drive_fwd || drive_rev); + e.attributes.drivable_ferry = (way.ferry() || way.rail()) && (drive_fwd || drive_rev); e.attributes.reclass_link = false; e.attributes.reclass_ferry = false; e.attributes.has_names = - (way.name_index_ != 0 || way.name_en_index_ != 0 || way.alt_name_index_ != 0 || - way.official_name_index_ != 0 || way.ref_index_ != 0 || way.int_ref_index_ != 0); + (way.name_index_ != 0 || way.alt_name_index_ != 0 || way.official_name_index_ != 0 || + way.ref_index_ != 0 || way.int_ref_index_ != 0); // If this data has turn_channels set and we are not inferring turn channels then we need to // use the flag. Otherwise the turn_channel is set in the reclassify links. Also, an edge can't @@ -217,6 +215,8 @@ struct Node { uint32_t end_of; // the graphid of the node baldr::GraphId graph_id; + // grid Id within the tile (used for spatial node sorting) + uint32_t grid_id; bool is_start() const { return start_of != static_cast(-1); diff --git a/valhalla/mjolnir/osmaccess.h b/valhalla/mjolnir/osmaccess.h index 2aac686229..1ab85c0622 100644 --- a/valhalla/mjolnir/osmaccess.h +++ b/valhalla/mjolnir/osmaccess.h @@ -2,8 +2,6 @@ #define VALHALLA_MJOLNIR_PBFGRAPHBUILDER_OSMACCESS_H #include -#include -#include #include diff --git a/valhalla/mjolnir/osmaccessrestriction.h b/valhalla/mjolnir/osmaccessrestriction.h index da9f31ed81..9f7826c7b8 100644 --- a/valhalla/mjolnir/osmaccessrestriction.h +++ b/valhalla/mjolnir/osmaccessrestriction.h @@ -8,6 +8,9 @@ namespace valhalla { namespace mjolnir { +// Used for access restrictions. Conveys the direction in which the access restriction applies +enum class AccessRestrictionDirection : uint8_t { kBoth = 0, kForward = 1, kBackward = 2 }; + /** * OSM Access restriction information. Access Restrictions are stored in a * multimap keyed by the Id of the "from" way of the restriction. @@ -55,6 +58,16 @@ class OSMAccessRestriction { */ void set_modes(uint16_t modes); + /** + * Get the direction the access restriction applies to. + */ + AccessRestrictionDirection direction() const; + + /** + * Set the direction the access restriction applies to. + */ + void set_direction(AccessRestrictionDirection direction); + protected: uint64_t value_; @@ -63,7 +76,9 @@ class OSMAccessRestriction { uint16_t modes_ : 12; }; Attributes attributes_; - uint16_t spare_[3]; + AccessRestrictionDirection direction_; + uint16_t spare_[2]; + uint8_t spare2_; }; } // namespace mjolnir diff --git a/valhalla/mjolnir/osmadmindata.h b/valhalla/mjolnir/osmadmindata.h index 41f218985f..ab0d9e95d2 100644 --- a/valhalla/mjolnir/osmadmindata.h +++ b/valhalla/mjolnir/osmadmindata.h @@ -1,8 +1,6 @@ #pragma once #include -#include -#include #include #include @@ -37,6 +35,9 @@ struct OSMAdmin { // do we call out intersection names at intersections? bool allow_intersection_names; + + // Default language + uint32_t default_language_index; }; /** @@ -63,4 +64,4 @@ struct OSMAdminData { }; } // namespace mjolnir -} // namespace valhalla \ No newline at end of file +} // namespace valhalla diff --git a/valhalla/mjolnir/osmdata.h b/valhalla/mjolnir/osmdata.h index 70fc8d954e..a99b47f9f9 100644 --- a/valhalla/mjolnir/osmdata.h +++ b/valhalla/mjolnir/osmdata.h @@ -2,14 +2,13 @@ #define VALHALLA_MJOLNIR_OSMDATA_H #include -#include -#include #include #include -#include #include +#include #include +#include #include #include #include @@ -23,8 +22,8 @@ enum class OSMType : uint8_t { kNode, kWay, kRelation }; // Structure to store OSM node information and associate it to an OSM way struct OSMWayNode { OSMNode node; - uint32_t way_index; - uint32_t way_shape_node_index; + uint32_t way_index = 0; + uint32_t way_shape_node_index = 0; }; // OSM bicycle data (stored within OSMData) @@ -48,6 +47,7 @@ using ViaSet = std::unordered_set; using AccessRestrictionsMultiMap = std::unordered_multimap; using BikeMultiMap = std::unordered_multimap; using OSMLaneConnectivityMultiMap = std::unordered_multimap; +using LinguisticMultiMap = std::unordered_multimap; // OSMString map uses the way Id as the key and the name index into UniqueNames as the value using OSMStringMap = std::unordered_map; @@ -88,15 +88,16 @@ struct OSMData { */ static void cleanup_temp_files(const std::string& tile_dir); - uint64_t max_changeset_id_; // The largest/newest changeset id encountered when parsing OSM data - uint64_t osm_node_count; // Count of osm nodes - uint64_t osm_way_count; // Count of osm ways - uint64_t osm_way_node_count; // Count of osm nodes on osm ways - uint64_t node_count; // Count of all nodes in the graph - uint64_t edge_count; // Estimated count of edges in the graph - uint64_t node_ref_count; // Number of node with ref - uint64_t node_name_count; // Number of nodes with names - uint64_t node_exit_to_count; // Number of nodes with exit_to + uint64_t max_changeset_id_; // The largest/newest changeset id encountered when parsing OSM data + uint64_t osm_node_count; // Count of osm nodes + uint64_t osm_way_count; // Count of osm ways + uint64_t osm_way_node_count; // Count of osm nodes on osm ways + uint64_t node_count; // Count of all nodes in the graph + uint64_t edge_count; // Estimated count of edges in the graph + uint64_t node_ref_count; // Number of node with ref + uint64_t node_name_count; // Number of nodes with names + uint64_t node_exit_to_count; // Number of nodes with exit_to + uint64_t node_linguistic_count; // Number of nodes with linguistic info // Stores simple restrictions. Indexed by the from way Id RestrictionsMultiMap restrictions; @@ -128,6 +129,12 @@ struct OSMData { // Lane connectivity, index by the to way Id OSMLaneConnectivityMultiMap lane_connectivity_map; + // Stores the pronunciations. Indexed by the way Id. + LinguisticMultiMap pronunciations; + + // Stores the pronunciation languages. Indexed by the way Id. + LinguisticMultiMap langs; + bool initialized = false; }; diff --git a/valhalla/mjolnir/osmlinguistic.h b/valhalla/mjolnir/osmlinguistic.h new file mode 100644 index 0000000000..c3edd00fdd --- /dev/null +++ b/valhalla/mjolnir/osmlinguistic.h @@ -0,0 +1,95 @@ +#ifndef VALHALLA_MJOLNIR_OSMLINGUISTIC_H +#define VALHALLA_MJOLNIR_OSMLINGUISTIC_H + +#include + +#include + +namespace valhalla { +namespace mjolnir { + +// OSM Linguistic. IPA/Nt-sampa/Katakana/Jeita phonems/pronunciations/languages +struct OSMLinguistic { + + enum class Type : uint8_t { + kName, + kNameLeft, + kNameRight, + kNameForward, + kNameBackward, + kNodeName, + kAltName, + kAltNameLeft, + kAltNameRight, + kOfficialName, + kOfficialNameLeft, + kOfficialNameRight, + kTunnelName, + kTunnelNameLeft, + kTunnelNameRight, + kRef, + kRefLeft, + kRefRight, + kNodeRef, + kIntRef, + kIntRefLeft, + kIntRefRight, + kDestination, + kDestinationForward, + kDestinationBackward, + kDestinationRef, + kDestinationRefTo, + kDestinationStreet, + kDestinationStreetTo, + kJunctionRef, + kJunctionName + }; + enum class DiffType : uint8_t { kLeft, kRight, kForward, kBackward }; + + /** + * Constructor + */ + OSMLinguistic() { + memset(this, 0, sizeof(OSMLinguistic)); + } + + /** + * Set way id. + * @param id way id + */ + void set_name_offset(const uint32_t id) { + name_offset_ = id; + } + + /** + * Get the name offset + * @return Returns name_offset_. + */ + uint64_t name_offset() const { + return name_offset_; + } + + /** + * Set the key + * @param type Type of pronunciation/language + * @param alpha Type of alphabet + */ + void set_key(const uint8_t type, const uint8_t alpha) { + key_.type_ = type; + key_.alpha_ = alpha; + } + + // OSM way Id + uint32_t name_offset_; + struct Key { + uint16_t type_ : 8; + uint16_t alpha_ : 8; + }; + Key key_; + uint16_t spare_; +}; + +} // namespace mjolnir +} // namespace valhalla + +#endif // VALHALLA_MJOLNIR_OSMLINGUISTIC_H diff --git a/valhalla/mjolnir/osmnode.h b/valhalla/mjolnir/osmnode.h index 48a47661e1..a2e00912f4 100644 --- a/valhalla/mjolnir/osmnode.h +++ b/valhalla/mjolnir/osmnode.h @@ -3,8 +3,6 @@ #include #include -#include -#include #include #include @@ -57,19 +55,12 @@ struct OSMNode { uint32_t cash_only_toll_ : 1; uint32_t spare1_ : 5; - // pronunciations - uint32_t name_pronunciation_ipa_index_; - uint32_t name_pronunciation_nt_sampa_index_; - uint32_t name_pronunciation_katakana_index_; - uint32_t name_pronunciation_jeita_index_; - uint32_t ref_pronunciation_ipa_index_; - uint32_t ref_pronunciation_nt_sampa_index_; - uint32_t ref_pronunciation_katakana_index_; - uint32_t ref_pronunciation_jeita_index_; - // bss information uint32_t bss_info_; + // linguistic information + uint32_t linguistic_info_index_; + // Lat,lng of the node at fixed 7digit precision uint32_t lng7_; uint32_t lat7_; @@ -90,8 +81,8 @@ struct OSMNode { } /** - * Sets the OSM node Id. Ensures the Id does not exceed the maximum allowed based on - * ths OSMNode structure. + * Sets the OSM node Id. + * * @param id Node Id. */ void set_id(const uint64_t id) { @@ -436,134 +427,6 @@ struct OSMNode { return urban_; } - /** - * Sets the index for the ref ipa pronunciation - * @param idx Index for the reference ipa pronunciation. - */ - void set_ref_pronunciation_ipa_index(const uint32_t idx) { - ref_pronunciation_ipa_index_ = idx; - } - - /** - * Get the ref ipa pronunciation index. - * @return Returns the index for the ref ipa pronunciation. - */ - uint32_t ref_pronunciation_ipa_index() const { - return ref_pronunciation_ipa_index_; - } - - /** - * Sets the index for the ref nt-sampa pronunciation - * @param idx Index for the reference nt-sampa pronunciation. - */ - void set_ref_pronunciation_nt_sampa_index(const uint32_t idx) { - ref_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the ref nt-sampa pronunciation index. - * @return Returns the index for the ref nt-sampa pronunciation. - */ - uint32_t ref_pronunciation_nt_sampa_index() const { - return ref_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for the ref katakana pronunciation - * @param idx Index for the reference katakana pronunciation. - */ - void set_ref_pronunciation_katakana_index(const uint32_t idx) { - ref_pronunciation_katakana_index_ = idx; - } - - /** - * Get the ref katakana pronunciation index. - * @return Returns the index for the ref katakana pronunciation. - */ - uint32_t ref_pronunciation_katakana_index() const { - return ref_pronunciation_katakana_index_; - } - - /** - * Sets the index for the ref jeita pronunciation - * @param idx Index for the reference jeita pronunciation. - */ - void set_ref_pronunciation_jeita_index(const uint32_t idx) { - ref_pronunciation_jeita_index_ = idx; - } - - /** - * Get the ref jeita pronunciation index. - * @return Returns the index for the ref jeita pronunciation. - */ - uint32_t ref_pronunciation_jeita_index() const { - return ref_pronunciation_jeita_index_; - } - - /** - * Sets the index for name ipa pronunciation - * @param idx Index for the name ipa pronunciation. - */ - void set_name_pronunciation_ipa_index(const uint32_t idx) { - name_pronunciation_ipa_index_ = idx; - } - - /** - * Get the name ipa pronunciation index. - * @return Returns the index for the name ipa pronunciation. - */ - uint32_t name_pronunciation_ipa_index() const { - return name_pronunciation_ipa_index_; - } - - /** - * Sets the index for name nt-sampa pronunciation - * @param idx Index for the name nt-sampa pronunciation. - */ - void set_name_pronunciation_nt_sampa_index(const uint32_t idx) { - name_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the name nt-sampa pronunciation index. - * @return Returns the index for the name nt-sampa pronunciation. - */ - uint32_t name_pronunciation_nt_sampa_index() const { - return name_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for name katakana pronunciation - * @param idx Index for the name katakana pronunciation. - */ - void set_name_pronunciation_katakana_index(const uint32_t idx) { - name_pronunciation_katakana_index_ = idx; - } - - /** - * Get the name katakana pronunciation index. - * @return Returns the index for the name katakana pronunciation. - */ - uint32_t name_pronunciation_katakana_index() const { - return name_pronunciation_katakana_index_; - } - - /** - * Sets the index for name jeita pronunciation - * @param idx Index for the name jeita pronunciation. - */ - void set_name_pronunciation_jeita_index(const uint32_t idx) { - name_pronunciation_jeita_index_ = idx; - } - - /** - * Get the name jeita pronunciation index. - * @return Returns the index for the name jeita pronunciation. - */ - uint32_t name_pronunciation_jeita_index() const { - return name_pronunciation_jeita_index_; - } - /** * Set the country iso code index * @param country iso code Index into the 2 char Country ISO Code. @@ -684,6 +547,22 @@ struct OSMNode { uint32_t bss_info_index() const { return bss_info_; } + + /** + * Sets the index for the linguistic info + * @param idx Index for the linguistic info. + */ + void set_linguistic_info_index(const uint32_t idx) { + linguistic_info_index_ = idx; + } + + /** + * Get the linguistic info index. + * @return Returns the index for the linguistic info. + */ + uint32_t linguistic_info_index() const { + return linguistic_info_index_; + } }; } // namespace mjolnir diff --git a/valhalla/mjolnir/osmnodelinguistic.h b/valhalla/mjolnir/osmnodelinguistic.h new file mode 100644 index 0000000000..4c85a543e7 --- /dev/null +++ b/valhalla/mjolnir/osmnodelinguistic.h @@ -0,0 +1,345 @@ +#ifndef VALHALLA_MJOLNIR_OSMNODELINGUISTIC_H_ +#define VALHALLA_MJOLNIR_OSMNODELINGUISTIC_H_ + +#include + +namespace valhalla { +namespace mjolnir { + +/** + * OSM node linguistic information. + */ +struct OSMNodeLinguistic { + + OSMNodeLinguistic() { + memset(this, 0, sizeof(OSMNodeLinguistic)); + } + + /** + * Sets the index for name: + * @param idx Index for the languages. + */ + void set_name_lang_index(const uint32_t idx) { + name_lang_index_ = idx; + } + + /** + * Get the name: index. + * @return Returns the index for the languages. + */ + uint32_t name_lang_index() const { + return name_lang_index_; + } + + /** + * Sets the index for the name ipa pronunciation + * @param idx Index for the nameerence ipa pronunciation. + */ + void set_name_pronunciation_ipa_index(const uint32_t idx) { + name_pronunciation_ipa_index_ = idx; + } + + /** + * Get the name ipa pronunciation index. + * @return Returns the index for the name ipa pronunciation. + */ + uint32_t name_pronunciation_ipa_index() const { + return name_pronunciation_ipa_index_; + } + + /** + * Sets the index for the name ipa lang pronunciation + * @param idx Index for the nameerence ipa lang pronunciation. + */ + void set_name_pronunciation_ipa_lang_index(const uint32_t idx) { + name_pronunciation_ipa_lang_index_ = idx; + } + + /** + * Get the name ipa pronunciation lang index. + * @return Returns the index for the name ipa lang pronunciation. + */ + uint32_t name_pronunciation_ipa_lang_index() const { + return name_pronunciation_ipa_lang_index_; + } + + /** + * Sets the index for the name nt-sampa pronunciation + * @param idx Index for the nameerence nt-sampa pronunciation. + */ + void set_name_pronunciation_nt_sampa_index(const uint32_t idx) { + name_pronunciation_nt_sampa_index_ = idx; + } + + /** + * Get the name nt-sampa pronunciation index. + * @return Returns the index for the name nt-sampa pronunciation. + */ + uint32_t name_pronunciation_nt_sampa_index() const { + return name_pronunciation_nt_sampa_index_; + } + + /** + * Sets the index for the name nt-sampa lang pronunciation + * @param idx Index for the nameerence nt-sampa lang pronunciation. + */ + void set_name_pronunciation_nt_sampa_lang_index(const uint32_t idx) { + name_pronunciation_nt_sampa_lang_index_ = idx; + } + + /** + * Get the name nt-sampa pronunciation lang index. + * @return Returns the index for the name nt-sampa lang pronunciation. + */ + uint32_t name_pronunciation_nt_sampa_lang_index() const { + return name_pronunciation_nt_sampa_lang_index_; + } + + /** + * Sets the index for the name katakana pronunciation + * @param idx Index for the nameerence katakana pronunciation. + */ + void set_name_pronunciation_katakana_index(const uint32_t idx) { + name_pronunciation_katakana_index_ = idx; + } + + /** + * Get the name katakana pronunciation index. + * @return Returns the index for the name katakana pronunciation. + */ + uint32_t name_pronunciation_katakana_index() const { + return name_pronunciation_katakana_index_; + } + + /** + * Sets the index for the name katakana lang pronunciation + * @param idx Index for the nameerence katakana lang pronunciation. + */ + void set_name_pronunciation_katakana_lang_index(const uint32_t idx) { + name_pronunciation_katakana_lang_index_ = idx; + } + + /** + * Get the name katakana pronunciation lang index. + * @return Returns the index for the name katakana lang pronunciation. + */ + uint32_t name_pronunciation_katakana_lang_index() const { + return name_pronunciation_katakana_lang_index_; + } + + /** + * Sets the index for the name jeita pronunciation + * @param idx Index for the nameerence jeita pronunciation. + */ + void set_name_pronunciation_jeita_index(const uint32_t idx) { + name_pronunciation_jeita_index_ = idx; + } + + /** + * Get the name jeita pronunciation index. + * @return Returns the index for the name jeita pronunciation. + */ + uint32_t name_pronunciation_jeita_index() const { + return name_pronunciation_jeita_index_; + } + + /** + * Sets the index for the name jeita lang pronunciation + * @param idx Index for the nameerence jeita lang pronunciation. + */ + void set_name_pronunciation_jeita_lang_index(const uint32_t idx) { + name_pronunciation_jeita_lang_index_ = idx; + } + + /** + * Get the name jeita pronunciation lang index. + * @return Returns the index for the name jeita lang pronunciation. + */ + uint32_t name_pronunciation_jeita_lang_index() const { + return name_pronunciation_jeita_lang_index_; + } + + /** + * Sets the index for ref: + * @param idx Index for the languages. + */ + void set_ref_lang_index(const uint32_t idx) { + ref_lang_index_ = idx; + } + + /** + * Get the ref: index. + * @return Returns the index for the languages. + */ + uint32_t ref_lang_index() const { + return ref_lang_index_; + } + + /** + * Sets the index for the ref ipa pronunciation + * @param idx Index for the reference ipa pronunciation. + */ + void set_ref_pronunciation_ipa_index(const uint32_t idx) { + ref_pronunciation_ipa_index_ = idx; + } + + /** + * Get the ref ipa pronunciation index. + * @return Returns the index for the ref ipa pronunciation. + */ + uint32_t ref_pronunciation_ipa_index() const { + return ref_pronunciation_ipa_index_; + } + + /** + * Sets the index for the ref ipa lang pronunciation + * @param idx Index for the reference ipa lang pronunciation. + */ + void set_ref_pronunciation_ipa_lang_index(const uint32_t idx) { + ref_pronunciation_ipa_lang_index_ = idx; + } + + /** + * Get the ref ipa pronunciation lang index. + * @return Returns the index for the ref ipa lang pronunciation. + */ + uint32_t ref_pronunciation_ipa_lang_index() const { + return ref_pronunciation_ipa_lang_index_; + } + + /** + * Sets the index for the ref nt-sampa pronunciation + * @param idx Index for the reference nt-sampa pronunciation. + */ + void set_ref_pronunciation_nt_sampa_index(const uint32_t idx) { + ref_pronunciation_nt_sampa_index_ = idx; + } + + /** + * Get the ref nt-sampa pronunciation index. + * @return Returns the index for the ref nt-sampa pronunciation. + */ + uint32_t ref_pronunciation_nt_sampa_index() const { + return ref_pronunciation_nt_sampa_index_; + } + + /** + * Sets the index for the ref nt-sampa lang pronunciation + * @param idx Index for the reference nt-sampa lang pronunciation. + */ + void set_ref_pronunciation_nt_sampa_lang_index(const uint32_t idx) { + ref_pronunciation_nt_sampa_lang_index_ = idx; + } + + /** + * Get the ref nt-sampa pronunciation lang index. + * @return Returns the index for the ref nt-sampa lang pronunciation. + */ + uint32_t ref_pronunciation_nt_sampa_lang_index() const { + return ref_pronunciation_nt_sampa_lang_index_; + } + + /** + * Sets the index for the ref katakana pronunciation + * @param idx Index for the reference katakana pronunciation. + */ + void set_ref_pronunciation_katakana_index(const uint32_t idx) { + ref_pronunciation_katakana_index_ = idx; + } + + /** + * Get the ref katakana pronunciation index. + * @return Returns the index for the ref katakana pronunciation. + */ + uint32_t ref_pronunciation_katakana_index() const { + return ref_pronunciation_katakana_index_; + } + + /** + * Sets the index for the ref katakana lang pronunciation + * @param idx Index for the reference katakana lang pronunciation. + */ + void set_ref_pronunciation_katakana_lang_index(const uint32_t idx) { + ref_pronunciation_katakana_lang_index_ = idx; + } + + /** + * Get the ref katakana pronunciation lang index. + * @return Returns the index for the ref katakana lang pronunciation. + */ + uint32_t ref_pronunciation_katakana_lang_index() const { + return ref_pronunciation_katakana_lang_index_; + } + + /** + * Sets the index for the ref jeita pronunciation + * @param idx Index for the reference jeita pronunciation. + */ + void set_ref_pronunciation_jeita_index(const uint32_t idx) { + ref_pronunciation_jeita_index_ = idx; + } + + /** + * Get the ref jeita pronunciation index. + * @return Returns the index for the ref jeita pronunciation. + */ + uint32_t ref_pronunciation_jeita_index() const { + return ref_pronunciation_jeita_index_; + } + + /** + * Sets the index for the ref jeita lang pronunciation + * @param idx Index for the reference jeita lang pronunciation. + */ + void set_ref_pronunciation_jeita_lang_index(const uint32_t idx) { + ref_pronunciation_jeita_lang_index_ = idx; + } + + /** + * Get the ref jeita pronunciation lang index. + * @return Returns the index for the ref jeita lang pronunciation. + */ + uint32_t ref_pronunciation_jeita_lang_index() const { + return ref_pronunciation_jeita_lang_index_; + } + + /** + * Has any index been set? + * @return True is nothing is set + */ + bool isEmpty() const { + return (name_lang_index_ == 0 && name_pronunciation_ipa_index_ == 0 && + name_pronunciation_ipa_lang_index_ == 0 && name_pronunciation_nt_sampa_index_ == 0 && + name_pronunciation_nt_sampa_lang_index_ == 0 && name_pronunciation_katakana_index_ == 0 && + name_pronunciation_katakana_lang_index_ == 0 && name_pronunciation_jeita_index_ == 0 && + name_pronunciation_jeita_lang_index_ == 0 && ref_lang_index_ == 0 && + ref_pronunciation_ipa_index_ == 0 && ref_pronunciation_ipa_lang_index_ == 0 && + ref_pronunciation_nt_sampa_index_ == 0 && ref_pronunciation_nt_sampa_lang_index_ == 0 && + ref_pronunciation_katakana_index_ == 0 && ref_pronunciation_katakana_lang_index_ == 0 && + ref_pronunciation_jeita_index_ == 0 && ref_pronunciation_jeita_lang_index_ == 0); + } + + // pronunciations / langs + uint32_t name_lang_index_; + uint32_t name_pronunciation_ipa_index_; + uint32_t name_pronunciation_ipa_lang_index_; + uint32_t name_pronunciation_nt_sampa_index_; + uint32_t name_pronunciation_nt_sampa_lang_index_; + uint32_t name_pronunciation_katakana_index_; + uint32_t name_pronunciation_katakana_lang_index_; + uint32_t name_pronunciation_jeita_index_; + uint32_t name_pronunciation_jeita_lang_index_; + uint32_t ref_lang_index_; + uint32_t ref_pronunciation_ipa_index_; + uint32_t ref_pronunciation_ipa_lang_index_; + uint32_t ref_pronunciation_nt_sampa_index_; + uint32_t ref_pronunciation_nt_sampa_lang_index_; + uint32_t ref_pronunciation_katakana_index_; + uint32_t ref_pronunciation_katakana_lang_index_; + uint32_t ref_pronunciation_jeita_index_; + uint32_t ref_pronunciation_jeita_lang_index_; +}; +} // namespace mjolnir +} // namespace valhalla + +#endif // VALHALLA_MJOLNIR_OSMLINGUISTIC_H diff --git a/valhalla/mjolnir/osmpronunciation.h b/valhalla/mjolnir/osmpronunciation.h deleted file mode 100644 index 490e488d73..0000000000 --- a/valhalla/mjolnir/osmpronunciation.h +++ /dev/null @@ -1,1099 +0,0 @@ -#ifndef VALHALLA_MJOLNIR_PBFGRAPHBUILDER_OSMPRONUNCIATION_H -#define VALHALLA_MJOLNIR_PBFGRAPHBUILDER_OSMPRONUNCIATION_H - -#include -#include -#include - -#include - -namespace valhalla { -namespace mjolnir { - -// OSM Pronunciation. IPA/Nt-sampa/Katakana/Jeita phonems/pronunciations -struct OSMPronunciation { - - /** - * Constructor - */ - OSMPronunciation() { - memset(this, 0, sizeof(OSMPronunciation)); - } - - /** - * Constructor with way id arg. - * @param id way id - */ - OSMPronunciation(const uint64_t id) { - memset(this, 0, sizeof(OSMPronunciation)); - set_way_id(id); - } - - /** - * Set way id. - * @param id way id - */ - void set_way_id(const uint64_t id) { - osmwayid_ = id; - } - - /** - * Get the way id - * @return Returns way id. - */ - uint64_t way_id() const { - return osmwayid_; - } - - /** - * Sets the index for the ref ipa pronunciation - * @param idx Index for the reference ipa pronunciation. - */ - void set_ref_pronunciation_ipa_index(const uint32_t idx) { - ref_pronunciation_ipa_index_ = idx; - } - - /** - * Get the ref ipa pronunciation index. - * @return Returns the index for the ref ipa pronunciation. - */ - uint32_t ref_pronunciation_ipa_index() const { - return ref_pronunciation_ipa_index_; - } - - /** - * Sets the index for int ret ipa pronunciation - * @param idx Index for the international reference ipa pronunciation. - */ - void set_int_ref_pronunciation_ipa_index(const uint32_t idx) { - int_ref_pronunciation_ipa_index_ = idx; - } - - /** - * Get the int ref ipa pronunciation index. - * @return Returns the index for the int ref ipa pronunciation. - */ - uint32_t int_ref_pronunciation_ipa_index() const { - return int_ref_pronunciation_ipa_index_; - } - - /** - * Sets the index for name ipa pronunciation - * @param idx Index for the name ipa pronunciation. - */ - void set_name_pronunciation_ipa_index(const uint32_t idx) { - name_pronunciation_ipa_index_ = idx; - } - - /** - * Get the name ipa pronunciation index. - * @return Returns the index for the name ipa pronunciation. - */ - uint32_t name_pronunciation_ipa_index() const { - return name_pronunciation_ipa_index_; - } - - /** - * Sets the index for name:en ipa pronunciation - * @param idx Index for the English name ipa pronunciation. - */ - void set_name_en_pronunciation_ipa_index(const uint32_t idx) { - name_en_pronunciation_ipa_index_ = idx; - } - - /** - * Get the name:en ipa pronunciation index. - * @return Returns the index for the English name ipa pronunciation. - */ - uint32_t name_en_pronunciation_ipa_index() const { - return name_en_pronunciation_ipa_index_; - } - - /** - * Sets the index for alt name ipa pronunciation - * @param idx Index for the alt name ipa pronunciation. - */ - void set_alt_name_pronunciation_ipa_index(const uint32_t idx) { - alt_name_pronunciation_ipa_index_ = idx; - } - - /** - * Get the alt name ipa pronunciation index. - * @return Returns the index for the alt name ipa pronunciation. - */ - uint32_t alt_name_pronunciation_ipa_index() const { - return alt_name_pronunciation_ipa_index_; - } - - /** - * Sets the index for official name ipa pronunciation - * @param idx Index for the official name ipa pronunciation. - */ - void set_official_name_pronunciation_ipa_index(const uint32_t idx) { - official_name_pronunciation_ipa_index_ = idx; - } - - /** - * Get the official name ipa pronunciation index. - * @return Returns the index for the official name ipa pronunciation. - */ - uint32_t official_name_pronunciation_ipa_index() const { - return official_name_pronunciation_ipa_index_; - } - - /** - * Sets the index for tunnel name ipa pronunciation - * @param idx Index for the tunnel name ipa pronunciation. - */ - void set_tunnel_name_pronunciation_ipa_index(const uint32_t idx) { - tunnel_name_pronunciation_ipa_index_ = idx; - } - - /** - * Get the tunnel name ipa pronunciation index. - * @return Returns the index for the tunnel name ipa pronunciation. - */ - uint32_t tunnel_name_pronunciation_ipa_index() const { - return tunnel_name_pronunciation_ipa_index_; - } - - /** - * Sets the index for destination ipa pronunciation. - * @param idx Index for the destination ipa pronunciation. - */ - void set_destination_pronunciation_ipa_index(const uint32_t idx) { - destination_pronunciation_ipa_index_ = idx; - } - - /** - * Get the get_destination ipa pronunciation index. - * @return Returns the index for the destination ipa pronunciation. - */ - uint32_t destination_pronunciation_ipa_index() const { - return destination_pronunciation_ipa_index_; - } - - /** - * Sets the index for destination in forward direction ipa pronunciation. - * @param idx Index for the destination ipa pronunciation. - */ - void set_destination_forward_pronunciation_ipa_index(const uint32_t idx) { - destination_forward_pronunciation_ipa_index_ = idx; - } - - /** - * Get the forward direction ipa pronunciation index. - * @return Returns the index for the forward direction ipa pronunciation. - */ - uint32_t destination_forward_pronunciation_ipa_index() const { - return destination_forward_pronunciation_ipa_index_; - } - - /** - * Sets the index for destination in backward direction ipa pronunciation. - * @param idx Index for the backward direction ipa pronunciation. - */ - void set_destination_backward_pronunciation_ipa_index(const uint32_t idx) { - destination_backward_pronunciation_ipa_index_ = idx; - } - - /** - * Get the backward direction ipa pronunciation index. - * @return Returns the index for the backward direction ipa pronunciation. - */ - uint32_t destination_backward_pronunciation_ipa_index() const { - return destination_backward_pronunciation_ipa_index_; - } - - /** - * Sets the index for destination ref ipa pronunciation. - * @param idx Index for the destination ref ipa pronunciation. - */ - void set_destination_ref_pronunciation_ipa_index(const uint32_t idx) { - destination_ref_pronunciation_ipa_index_ = idx; - } - - /** - * Get the destination ref ipa pronunciation index. - * @return Returns the index for the destination ref ipa pronunciation. - */ - uint32_t destination_ref_pronunciation_ipa_index() const { - return destination_ref_pronunciation_ipa_index_; - } - - /** - * Sets the index for destination ref to ipa pronunciation. - * @param idx Index for the destination ref to ipa pronunciation. - */ - void set_destination_ref_to_pronunciation_ipa_index(const uint32_t idx) { - destination_ref_to_pronunciation_ipa_index_ = idx; - } - - /** - * Get the destination ref to ipa pronunciation index. - * @return Returns the index for the destination ref to ipa pronunciation. - */ - uint32_t destination_ref_to_pronunciation_ipa_index() const { - return destination_ref_to_pronunciation_ipa_index_; - } - - /** - * Sets the index for destination street ipa pronunciation. - * @param idx Index for the destination street ipa pronunciation. - */ - void set_destination_street_pronunciation_ipa_index(const uint32_t idx) { - destination_street_pronunciation_ipa_index_ = idx; - } - - /** - * Get the destination_street ipa pronunciation index. - * @return Returns the index for the destination street ipa pronunciation. - */ - uint32_t destination_street_pronunciation_ipa_index() const { - return destination_street_pronunciation_ipa_index_; - } - - /** - * Sets the index for destination street to ipa pronunciation. - * @param idx Index for the destination street to ipa pronunciation. - */ - void set_destination_street_to_pronunciation_ipa_index(const uint32_t idx) { - destination_street_to_pronunciation_ipa_index_ = idx; - } - - /** - * Get the destination street to ipa pronunciation index. - * @return Returns the index for the destination street to ipa pronunciation. - */ - uint32_t destination_street_to_pronunciation_ipa_index() const { - return destination_street_to_pronunciation_ipa_index_; - } - - /** - * Sets the index for junction ref ipa pronunciation. - * @param idx Index for the junction ref ipa pronunciation. - */ - void set_junction_ref_pronunciation_ipa_index(const uint32_t idx) { - junction_ref_pronunciation_ipa_index_ = idx; - } - - /** - * Get the junction ref ipa pronunciation index. - * @return Returns the ipa pronunciation index for the junction ref. - */ - uint32_t junction_ref_pronunciation_ipa_index() const { - return junction_ref_pronunciation_ipa_index_; - } - - /** - * Sets the index for the ref nt-sampa pronunciation - * @param idx Index for the reference nt-sampa pronunciation. - */ - void set_ref_pronunciation_nt_sampa_index(const uint32_t idx) { - ref_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the ref nt-sampa pronunciation index. - * @return Returns the index for the ref nt-sampa pronunciation. - */ - uint32_t ref_pronunciation_nt_sampa_index() const { - return ref_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for int ret nt-sampa pronunciation - * @param idx Index for the international reference nt-sampa pronunciation. - */ - void set_int_ref_pronunciation_nt_sampa_index(const uint32_t idx) { - int_ref_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the int ref nt-sampa pronunciation index. - * @return Returns the index for the int ref nt-sampa pronunciation. - */ - uint32_t int_ref_pronunciation_nt_sampa_index() const { - return int_ref_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for name nt-sampa pronunciation - * @param idx Index for the name nt-sampa pronunciation. - */ - void set_name_pronunciation_nt_sampa_index(const uint32_t idx) { - name_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the name nt-sampa pronunciation index. - * @return Returns the index for the name nt-sampa pronunciation. - */ - uint32_t name_pronunciation_nt_sampa_index() const { - return name_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for name:en nt-sampa pronunciation - * @param idx Index for the English name nt-sampa pronunciation. - */ - void set_name_en_pronunciation_nt_sampa_index(const uint32_t idx) { - name_en_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the name:en nt-sampa pronunciation index. - * @return Returns the index for the English name nt-sampa pronunciation. - */ - uint32_t name_en_pronunciation_nt_sampa_index() const { - return name_en_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for alt name nt-sampa pronunciation - * @param idx Index for the alt name nt-sampa pronunciation. - */ - void set_alt_name_pronunciation_nt_sampa_index(const uint32_t idx) { - alt_name_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the alt name nt-sampa pronunciation index. - * @return Returns the index for the alt name nt-sampa pronunciation. - */ - uint32_t alt_name_pronunciation_nt_sampa_index() const { - return alt_name_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for official name nt-sampa pronunciation - * @param idx Index for the official name nt-sampa pronunciation. - */ - void set_official_name_pronunciation_nt_sampa_index(const uint32_t idx) { - official_name_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the official name nt-sampa pronunciation index. - * @return Returns the index for the official name nt-sampa pronunciation. - */ - uint32_t official_name_pronunciation_nt_sampa_index() const { - return official_name_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for tunnel name nt-sampa pronunciation - * @param idx Index for the tunnel name nt-sampa pronunciation. - */ - void set_tunnel_name_pronunciation_nt_sampa_index(const uint32_t idx) { - tunnel_name_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the tunnel name nt-sampa pronunciation index. - * @return Returns the index for the tunnel name nt-sampa pronunciation. - */ - uint32_t tunnel_name_pronunciation_nt_sampa_index() const { - return tunnel_name_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for destination nt-sampa pronunciation. - * @param idx Index for the destination nt-sampa pronunciation. - */ - void set_destination_pronunciation_nt_sampa_index(const uint32_t idx) { - destination_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the get_destination nt-sampa pronunciation index. - * @return Returns the index for the destination nt-sampa pronunciation. - */ - uint32_t destination_pronunciation_nt_sampa_index() const { - return destination_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for destination in forward direction nt-sampa pronunciation. - * @param idx Index for the destination nt-sampa pronunciation. - */ - void set_destination_forward_pronunciation_nt_sampa_index(const uint32_t idx) { - destination_forward_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the forward direction nt-sampa pronunciation index. - * @return Returns the index for the forward direction nt-sampa pronunciation. - */ - uint32_t destination_forward_pronunciation_nt_sampa_index() const { - return destination_forward_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for destination in backward direction nt-sampa pronunciation. - * @param idx Index for the backward direction nt-sampa pronunciation. - */ - void set_destination_backward_pronunciation_nt_sampa_index(const uint32_t idx) { - destination_backward_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the backward direction nt-sampa pronunciation index. - * @return Returns the index for the backward direction nt-sampa pronunciation. - */ - uint32_t destination_backward_pronunciation_nt_sampa_index() const { - return destination_backward_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for destination ref nt-sampa pronunciation. - * @param idx Index for the destination ref nt-sampa pronunciation. - */ - void set_destination_ref_pronunciation_nt_sampa_index(const uint32_t idx) { - destination_ref_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the destination ref nt-sampa pronunciation index. - * @return Returns the index for the destination ref nt-sampa pronunciation. - */ - uint32_t destination_ref_pronunciation_nt_sampa_index() const { - return destination_ref_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for destination ref to nt-sampa pronunciation. - * @param idx Index for the destination ref to nt-sampa pronunciation. - */ - void set_destination_ref_to_pronunciation_nt_sampa_index(const uint32_t idx) { - destination_ref_to_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the destination ref to nt-sampa pronunciation index. - * @return Returns the index for the destination ref to nt-sampa pronunciation. - */ - uint32_t destination_ref_to_pronunciation_nt_sampa_index() const { - return destination_ref_to_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for destination street nt-sampa pronunciation. - * @param idx Index for the destination street nt-sampa pronunciation. - */ - void set_destination_street_pronunciation_nt_sampa_index(const uint32_t idx) { - destination_street_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the destination_street nt-sampa pronunciation index. - * @return Returns the index for the destination street nt-sampa pronunciation. - */ - uint32_t destination_street_pronunciation_nt_sampa_index() const { - return destination_street_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for destination street to nt-sampa pronunciation. - * @param idx Index for the destination street to nt-sampa pronunciation. - */ - void set_destination_street_to_pronunciation_nt_sampa_index(const uint32_t idx) { - destination_street_to_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the destination street to nt-sampa pronunciation index. - * @return Returns the index for the destination street to nt-sampa pronunciation. - */ - uint32_t destination_street_to_pronunciation_nt_sampa_index() const { - return destination_street_to_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for junction ref nt-sampa pronunciation. - * @param idx Index for the junction ref nt-sampa pronunciation. - */ - void set_junction_ref_pronunciation_nt_sampa_index(const uint32_t idx) { - junction_ref_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the junction ref nt-sampa pronunciation index. - * @return Returns the nt-sampa pronunciation index for the junction ref. - */ - uint32_t junction_ref_pronunciation_nt_sampa_index() const { - return junction_ref_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for the ref katakana pronunciation - * @param idx Index for the reference katakana pronunciation. - */ - void set_ref_pronunciation_katakana_index(const uint32_t idx) { - ref_pronunciation_katakana_index_ = idx; - } - - /** - * Get the ref katakana pronunciation index. - * @return Returns the index for the ref katakana pronunciation. - */ - uint32_t ref_pronunciation_katakana_index() const { - return ref_pronunciation_katakana_index_; - } - - /** - * Sets the index for int ret katakana pronunciation - * @param idx Index for the international reference katakana pronunciation. - */ - void set_int_ref_pronunciation_katakana_index(const uint32_t idx) { - int_ref_pronunciation_katakana_index_ = idx; - } - - /** - * Get the int ref katakana pronunciation index. - * @return Returns the index for the int ref katakana pronunciation. - */ - uint32_t int_ref_pronunciation_katakana_index() const { - return int_ref_pronunciation_katakana_index_; - } - - /** - * Sets the index for name katakana pronunciation - * @param idx Index for the name katakana pronunciation. - */ - void set_name_pronunciation_katakana_index(const uint32_t idx) { - name_pronunciation_katakana_index_ = idx; - } - - /** - * Get the name katakana pronunciation index. - * @return Returns the index for the name katakana pronunciation. - */ - uint32_t name_pronunciation_katakana_index() const { - return name_pronunciation_katakana_index_; - } - - /** - * Sets the index for name:en katakana pronunciation - * @param idx Index for the English name katakana pronunciation. - */ - void set_name_en_pronunciation_katakana_index(const uint32_t idx) { - name_en_pronunciation_katakana_index_ = idx; - } - - /** - * Get the name:en katakana pronunciation index. - * @return Returns the index for the English name katakana pronunciation. - */ - uint32_t name_en_pronunciation_katakana_index() const { - return name_en_pronunciation_katakana_index_; - } - - /** - * Sets the index for alt name katakana pronunciation - * @param idx Index for the alt name katakana pronunciation. - */ - void set_alt_name_pronunciation_katakana_index(const uint32_t idx) { - alt_name_pronunciation_katakana_index_ = idx; - } - - /** - * Get the alt name katakana pronunciation index. - * @return Returns the index for the alt name katakana pronunciation. - */ - uint32_t alt_name_pronunciation_katakana_index() const { - return alt_name_pronunciation_katakana_index_; - } - - /** - * Sets the index for official name katakana pronunciation - * @param idx Index for the official name katakana pronunciation. - */ - void set_official_name_pronunciation_katakana_index(const uint32_t idx) { - official_name_pronunciation_katakana_index_ = idx; - } - - /** - * Get the official name katakana pronunciation index. - * @return Returns the index for the official name katakana pronunciation. - */ - uint32_t official_name_pronunciation_katakana_index() const { - return official_name_pronunciation_katakana_index_; - } - - /** - * Sets the index for tunnel name katakana pronunciation - * @param idx Index for the tunnel name katakana pronunciation. - */ - void set_tunnel_name_pronunciation_katakana_index(const uint32_t idx) { - tunnel_name_pronunciation_katakana_index_ = idx; - } - - /** - * Get the tunnel name katakana pronunciation index. - * @return Returns the index for the tunnel name katakana pronunciation. - */ - uint32_t tunnel_name_pronunciation_katakana_index() const { - return tunnel_name_pronunciation_katakana_index_; - } - - /** - * Sets the index for destination katakana pronunciation. - * @param idx Index for the destination katakana pronunciation. - */ - void set_destination_pronunciation_katakana_index(const uint32_t idx) { - destination_pronunciation_katakana_index_ = idx; - } - - /** - * Get the get_destination katakana pronunciation index. - * @return Returns the index for the destination katakana pronunciation. - */ - uint32_t destination_pronunciation_katakana_index() const { - return destination_pronunciation_katakana_index_; - } - - /** - * Sets the index for destination in forward direction katakana pronunciation. - * @param idx Index for the destination katakana pronunciation. - */ - void set_destination_forward_pronunciation_katakana_index(const uint32_t idx) { - destination_forward_pronunciation_katakana_index_ = idx; - } - - /** - * Get the forward direction katakana pronunciation index. - * @return Returns the index for the forward direction katakana pronunciation. - */ - uint32_t destination_forward_pronunciation_katakana_index() const { - return destination_forward_pronunciation_katakana_index_; - } - - /** - * Sets the index for destination in backward direction katakana pronunciation. - * @param idx Index for the backward direction katakana pronunciation. - */ - void set_destination_backward_pronunciation_katakana_index(const uint32_t idx) { - destination_backward_pronunciation_katakana_index_ = idx; - } - - /** - * Get the backward direction katakana pronunciation index. - * @return Returns the index for the backward direction katakana pronunciation. - */ - uint32_t destination_backward_pronunciation_katakana_index() const { - return destination_backward_pronunciation_katakana_index_; - } - - /** - * Sets the index for destination ref katakana pronunciation. - * @param idx Index for the destination ref katakana pronunciation. - */ - void set_destination_ref_pronunciation_katakana_index(const uint32_t idx) { - destination_ref_pronunciation_katakana_index_ = idx; - } - - /** - * Get the destination ref katakana pronunciation index. - * @return Returns the index for the destination ref katakana pronunciation. - */ - uint32_t destination_ref_pronunciation_katakana_index() const { - return destination_ref_pronunciation_katakana_index_; - } - - /** - * Sets the index for destination ref to katakana pronunciation. - * @param idx Index for the destination ref to katakana pronunciation. - */ - void set_destination_ref_to_pronunciation_katakana_index(const uint32_t idx) { - destination_ref_to_pronunciation_katakana_index_ = idx; - } - - /** - * Get the destination ref to katakana pronunciation index. - * @return Returns the index for the destination ref to katakana pronunciation. - */ - uint32_t destination_ref_to_pronunciation_katakana_index() const { - return destination_ref_to_pronunciation_katakana_index_; - } - - /** - * Sets the index for destination street katakana pronunciation. - * @param idx Index for the destination street katakana pronunciation. - */ - void set_destination_street_pronunciation_katakana_index(const uint32_t idx) { - destination_street_pronunciation_katakana_index_ = idx; - } - - /** - * Get the destination_street katakana pronunciation index. - * @return Returns the index for the destination street katakana pronunciation. - */ - uint32_t destination_street_pronunciation_katakana_index() const { - return destination_street_pronunciation_katakana_index_; - } - - /** - * Sets the index for destination street to katakana pronunciation. - * @param idx Index for the destination street to katakana pronunciation. - */ - void set_destination_street_to_pronunciation_katakana_index(const uint32_t idx) { - destination_street_to_pronunciation_katakana_index_ = idx; - } - - /** - * Get the destination street to katakana pronunciation index. - * @return Returns the index for the destination street to katakana pronunciation. - */ - uint32_t destination_street_to_pronunciation_katakana_index() const { - return destination_street_to_pronunciation_katakana_index_; - } - - /** - * Sets the index for junction ref katakana pronunciation. - * @param idx Index for the junction ref katakana pronunciation. - */ - void set_junction_ref_pronunciation_katakana_index(const uint32_t idx) { - junction_ref_pronunciation_katakana_index_ = idx; - } - - /** - * Get the junction ref katakana pronunciation index. - * @return Returns the katakana pronunciation index for the junction ref. - */ - uint32_t junction_ref_pronunciation_katakana_index() const { - return junction_ref_pronunciation_katakana_index_; - } - - /** - * Sets the index for the ref jeita pronunciation - * @param idx Index for the reference jeita pronunciation. - */ - void set_ref_pronunciation_jeita_index(const uint32_t idx) { - ref_pronunciation_jeita_index_ = idx; - } - - /** - * Get the ref jeita pronunciation index. - * @return Returns the index for the ref jeita pronunciation. - */ - uint32_t ref_pronunciation_jeita_index() const { - return ref_pronunciation_jeita_index_; - } - - /** - * Sets the index for int ret jeita pronunciation - * @param idx Index for the international reference jeita pronunciation. - */ - void set_int_ref_pronunciation_jeita_index(const uint32_t idx) { - int_ref_pronunciation_jeita_index_ = idx; - } - - /** - * Get the int ref jeita pronunciation index. - * @return Returns the index for the int ref jeita pronunciation. - */ - uint32_t int_ref_pronunciation_jeita_index() const { - return int_ref_pronunciation_jeita_index_; - } - - /** - * Sets the index for name jeita pronunciation - * @param idx Index for the name jeita pronunciation. - */ - void set_name_pronunciation_jeita_index(const uint32_t idx) { - name_pronunciation_jeita_index_ = idx; - } - - /** - * Get the name jeita pronunciation index. - * @return Returns the index for the name jeita pronunciation. - */ - uint32_t name_pronunciation_jeita_index() const { - return name_pronunciation_jeita_index_; - } - - /** - * Sets the index for name:en jeita pronunciation - * @param idx Index for the English name jeita pronunciation. - */ - void set_name_en_pronunciation_jeita_index(const uint32_t idx) { - name_en_pronunciation_jeita_index_ = idx; - } - - /** - * Get the name:en jeita pronunciation index. - * @return Returns the index for the English name jeita pronunciation. - */ - uint32_t name_en_pronunciation_jeita_index() const { - return name_en_pronunciation_jeita_index_; - } - - /** - * Sets the index for alt name jeita pronunciation - * @param idx Index for the alt name jeita pronunciation. - */ - void set_alt_name_pronunciation_jeita_index(const uint32_t idx) { - alt_name_pronunciation_jeita_index_ = idx; - } - - /** - * Get the alt name jeita pronunciation index. - * @return Returns the index for the alt name jeita pronunciation. - */ - uint32_t alt_name_pronunciation_jeita_index() const { - return alt_name_pronunciation_jeita_index_; - } - - /** - * Sets the index for official name jeita pronunciation - * @param idx Index for the official name jeita pronunciation. - */ - void set_official_name_pronunciation_jeita_index(const uint32_t idx) { - official_name_pronunciation_jeita_index_ = idx; - } - - /** - * Get the official name jeita pronunciation index. - * @return Returns the index for the official name jeita pronunciation. - */ - uint32_t official_name_pronunciation_jeita_index() const { - return official_name_pronunciation_jeita_index_; - } - - /** - * Sets the index for tunnel name jeita pronunciation - * @param idx Index for the tunnel name jeita pronunciation. - */ - void set_tunnel_name_pronunciation_jeita_index(const uint32_t idx) { - tunnel_name_pronunciation_jeita_index_ = idx; - } - - /** - * Get the tunnel name jeita pronunciation index. - * @return Returns the index for the tunnel name jeita pronunciation. - */ - uint32_t tunnel_name_pronunciation_jeita_index() const { - return tunnel_name_pronunciation_jeita_index_; - } - - /** - * Sets the index for destination jeita pronunciation. - * @param idx Index for the destination jeita pronunciation. - */ - void set_destination_pronunciation_jeita_index(const uint32_t idx) { - destination_pronunciation_jeita_index_ = idx; - } - - /** - * Get the get_destination jeita pronunciation index. - * @return Returns the index for the destination jeita pronunciation. - */ - uint32_t destination_pronunciation_jeita_index() const { - return destination_pronunciation_jeita_index_; - } - - /** - * Sets the index for destination in forward direction jeita pronunciation. - * @param idx Index for the destination jeita pronunciation. - */ - void set_destination_forward_pronunciation_jeita_index(const uint32_t idx) { - destination_forward_pronunciation_jeita_index_ = idx; - } - - /** - * Get the forward direction jeita pronunciation index. - * @return Returns the index for the forward direction jeita pronunciation. - */ - uint32_t destination_forward_pronunciation_jeita_index() const { - return destination_forward_pronunciation_jeita_index_; - } - - /** - * Sets the index for destination in backward direction jeita pronunciation. - * @param idx Index for the backward direction jeita pronunciation. - */ - void set_destination_backward_pronunciation_jeita_index(const uint32_t idx) { - destination_backward_pronunciation_jeita_index_ = idx; - } - - /** - * Get the backward direction jeita pronunciation index. - * @return Returns the index for the backward direction jeita pronunciation. - */ - uint32_t destination_backward_pronunciation_jeita_index() const { - return destination_backward_pronunciation_jeita_index_; - } - - /** - * Sets the index for destination ref jeita pronunciation. - * @param idx Index for the destination ref jeita pronunciation. - */ - void set_destination_ref_pronunciation_jeita_index(const uint32_t idx) { - destination_ref_pronunciation_jeita_index_ = idx; - } - - /** - * Get the destination ref jeita pronunciation index. - * @return Returns the index for the destination ref jeita pronunciation. - */ - uint32_t destination_ref_pronunciation_jeita_index() const { - return destination_ref_pronunciation_jeita_index_; - } - - /** - * Sets the index for destination ref to jeita pronunciation. - * @param idx Index for the destination ref to jeita pronunciation. - */ - void set_destination_ref_to_pronunciation_jeita_index(const uint32_t idx) { - destination_ref_to_pronunciation_jeita_index_ = idx; - } - - /** - * Get the destination ref to jeita pronunciation index. - * @return Returns the index for the destination ref to jeita pronunciation. - */ - uint32_t destination_ref_to_pronunciation_jeita_index() const { - return destination_ref_to_pronunciation_jeita_index_; - } - - /** - * Sets the index for destination street jeita pronunciation. - * @param idx Index for the destination street jeita pronunciation. - */ - void set_destination_street_pronunciation_jeita_index(const uint32_t idx) { - destination_street_pronunciation_jeita_index_ = idx; - } - - /** - * Get the destination_street jeita pronunciation index. - * @return Returns the index for the destination street jeita pronunciation. - */ - uint32_t destination_street_pronunciation_jeita_index() const { - return destination_street_pronunciation_jeita_index_; - } - - /** - * Sets the index for destination street to jeita pronunciation. - * @param idx Index for the destination street to jeita pronunciation. - */ - void set_destination_street_to_pronunciation_jeita_index(const uint32_t idx) { - destination_street_to_pronunciation_jeita_index_ = idx; - } - - /** - * Get the destination street to jeita pronunciation index. - * @return Returns the index for the destination street to jeita pronunciation. - */ - uint32_t destination_street_to_pronunciation_jeita_index() const { - return destination_street_to_pronunciation_jeita_index_; - } - - /** - * Sets the index for junction ref jeita pronunciation. - * @param idx Index for the junction ref jeita pronunciation. - */ - void set_junction_ref_pronunciation_jeita_index(const uint32_t idx) { - junction_ref_pronunciation_jeita_index_ = idx; - } - - /** - * Get the junction ref jeita pronunciation index. - * @return Returns the jeita pronunciation index for the junction ref. - */ - uint32_t junction_ref_pronunciation_jeita_index() const { - return junction_ref_pronunciation_jeita_index_; - } - - // OSM way Id - uint64_t osmwayid_; - - // name and ref ipa pronunciations - uint32_t ref_pronunciation_ipa_index_; - uint32_t int_ref_pronunciation_ipa_index_; - uint32_t name_pronunciation_ipa_index_; - uint32_t name_en_pronunciation_ipa_index_; - uint32_t alt_name_pronunciation_ipa_index_; - uint32_t official_name_pronunciation_ipa_index_; - uint32_t tunnel_name_pronunciation_ipa_index_; - uint32_t direction_pronunciation_ipa_index_; - uint32_t int_direction_pronunciation_ipa_index_; - - // Sign Destination ipa pronunciations - uint32_t destination_pronunciation_ipa_index_; - uint32_t destination_forward_pronunciation_ipa_index_; - uint32_t destination_backward_pronunciation_ipa_index_; - uint32_t destination_ref_pronunciation_ipa_index_; - uint32_t destination_ref_to_pronunciation_ipa_index_; - uint32_t destination_street_pronunciation_ipa_index_; - uint32_t destination_street_to_pronunciation_ipa_index_; - uint32_t junction_ref_pronunciation_ipa_index_; - - // name and ref nt-sampa pronunciations - uint32_t ref_pronunciation_nt_sampa_index_; - uint32_t int_ref_pronunciation_nt_sampa_index_; - uint32_t name_pronunciation_nt_sampa_index_; - uint32_t name_en_pronunciation_nt_sampa_index_; - uint32_t alt_name_pronunciation_nt_sampa_index_; - uint32_t official_name_pronunciation_nt_sampa_index_; - uint32_t tunnel_name_pronunciation_nt_sampa_index_; - uint32_t direction_pronunciation_nt_sampa_index_; - uint32_t int_direction_pronunciation_nt_sampa_index_; - - // Sign Destination nt-sampa pronunciations - uint32_t destination_pronunciation_nt_sampa_index_; - uint32_t destination_forward_pronunciation_nt_sampa_index_; - uint32_t destination_backward_pronunciation_nt_sampa_index_; - uint32_t destination_ref_pronunciation_nt_sampa_index_; - uint32_t destination_ref_to_pronunciation_nt_sampa_index_; - uint32_t destination_street_pronunciation_nt_sampa_index_; - uint32_t destination_street_to_pronunciation_nt_sampa_index_; - uint32_t junction_ref_pronunciation_nt_sampa_index_; - - // name and ref katakana pronunciations - uint32_t ref_pronunciation_katakana_index_; - uint32_t int_ref_pronunciation_katakana_index_; - uint32_t name_pronunciation_katakana_index_; - uint32_t name_en_pronunciation_katakana_index_; - uint32_t alt_name_pronunciation_katakana_index_; - uint32_t official_name_pronunciation_katakana_index_; - uint32_t tunnel_name_pronunciation_katakana_index_; - uint32_t direction_pronunciation_katakana_index_; - uint32_t int_direction_pronunciation_katakana_index_; - - // Sign Destination katakana pronunciations - uint32_t destination_pronunciation_katakana_index_; - uint32_t destination_forward_pronunciation_katakana_index_; - uint32_t destination_backward_pronunciation_katakana_index_; - uint32_t destination_ref_pronunciation_katakana_index_; - uint32_t destination_ref_to_pronunciation_katakana_index_; - uint32_t destination_street_pronunciation_katakana_index_; - uint32_t destination_street_to_pronunciation_katakana_index_; - uint32_t junction_ref_pronunciation_katakana_index_; - - // name and ref jeita pronunciations - uint32_t ref_pronunciation_jeita_index_; - uint32_t int_ref_pronunciation_jeita_index_; - uint32_t name_pronunciation_jeita_index_; - uint32_t name_en_pronunciation_jeita_index_; - uint32_t alt_name_pronunciation_jeita_index_; - uint32_t official_name_pronunciation_jeita_index_; - uint32_t tunnel_name_pronunciation_jeita_index_; - uint32_t direction_pronunciation_jeita_index_; - uint32_t int_direction_pronunciation_jeita_index_; - - // Sign Destination jeita pronunciations - uint32_t destination_pronunciation_jeita_index_; - uint32_t destination_forward_pronunciation_jeita_index_; - uint32_t destination_backward_pronunciation_jeita_index_; - uint32_t destination_ref_pronunciation_jeita_index_; - uint32_t destination_ref_to_pronunciation_jeita_index_; - uint32_t destination_street_pronunciation_jeita_index_; - uint32_t destination_street_to_pronunciation_jeita_index_; - uint32_t junction_ref_pronunciation_jeita_index_; -}; - -} // namespace mjolnir -} // namespace valhalla - -#endif // VALHALLA_MJOLNIR_PBFGRAPHBUILDER_OSMPRONUNCIATION_H diff --git a/valhalla/mjolnir/osmway.h b/valhalla/mjolnir/osmway.h index ab60575ed1..a34d9a93b7 100644 --- a/valhalla/mjolnir/osmway.h +++ b/valhalla/mjolnir/osmway.h @@ -3,11 +3,12 @@ #include #include +#include #include #include #include -#include +#include #include namespace valhalla { @@ -122,6 +123,34 @@ struct OSMWay { return truck_speed_; } + /** + * Sets the forward truck speed + * @param truck_speed_forward Forward truck speed in KPH. + */ + void set_truck_speed_forward(const float truck_speed_forward); + + /** + * Gets the forward truck speed in KPH. + * @return Returns forward truck speed. + */ + uint8_t truck_speed_forward() const { + return truck_speed_forward_; + } + + /** + * Sets the backward truck speed + * @param truck_speed_backward Backward truck speed in KPH. + */ + void set_truck_speed_backward(const float truck_speed_backward); + + /** + * Gets the backward truck speed in KPH. + * @return Returns backward truck speed. + */ + uint8_t truck_speed_backward() const { + return truck_speed_backward_; + } + /** * Sets the index for the ref * @param idx Index for the reference. @@ -138,6 +167,86 @@ struct OSMWay { return ref_index_; } + /** + * Sets the index for ref: + * @param idx Index for the languages. + */ + void set_ref_lang_index(const uint32_t idx) { + ref_lang_index_ = idx; + } + + /** + * Get the ref: index. + * @return Returns the index for the languages. + */ + uint32_t ref_lang_index() const { + return ref_lang_index_; + } + + /** + * Sets the index for left ref + * @param idx Index for the left ref. + */ + void set_ref_left_index(const uint32_t idx) { + ref_left_index_ = idx; + } + + /** + * Get the left ref index. + * @return Returns the index for the left ref. + */ + uint32_t ref_left_index() const { + return ref_left_index_; + } + + /** + * Sets the index for ref:left: + * @param idx Index for the left languages. + */ + void set_ref_left_lang_index(const uint32_t idx) { + ref_left_lang_index_ = idx; + } + + /** + * Get the ref:left: index. + * @return Returns the index for the left languages. + */ + uint32_t ref_left_lang_index() const { + return ref_left_lang_index_; + } + + /** + * Sets the index for right ref + * @param idx Index for the right ref. + */ + void set_ref_right_index(const uint32_t idx) { + ref_right_index_ = idx; + } + + /** + * Get the right ref index. + * @return Returns the index for the right ref. + */ + uint32_t ref_right_index() const { + return ref_right_index_; + } + + /** + * Sets the index for ref:right: + * @param idx Index for the right languages. + */ + void set_ref_right_lang_index(const uint32_t idx) { + ref_right_lang_index_ = idx; + } + + /** + * Get the ref:right: index. + * @return Returns the index for the right languages. + */ + uint32_t ref_right_lang_index() const { + return ref_right_lang_index_; + } + /** * Sets the index for int ret * @param idx Index for the international reference. @@ -154,6 +263,86 @@ struct OSMWay { return int_ref_index_; } + /** + * Sets the index for int_ref: + * @param idx Index for the languages. + */ + void set_int_ref_lang_index(const uint32_t idx) { + int_ref_lang_index_ = idx; + } + + /** + * Get the int_ref: index. + * @return Returns the index for the languages. + */ + uint32_t int_ref_lang_index() const { + return int_ref_lang_index_; + } + + /** + * Sets the index for left int_ref + * @param idx Index for the left int_ref. + */ + void set_int_ref_left_index(const uint32_t idx) { + int_ref_left_index_ = idx; + } + + /** + * Get the left int_ref index. + * @return Returns the index for the left int_ref. + */ + uint32_t int_ref_left_index() const { + return int_ref_left_index_; + } + + /** + * Sets the index for int_ref:left: + * @param idx Index for the left languages. + */ + void set_int_ref_left_lang_index(const uint32_t idx) { + int_ref_left_lang_index_ = idx; + } + + /** + * Get the int_ref:left: index. + * @return Returns the index for the left languages. + */ + uint32_t int_ref_left_lang_index() const { + return int_ref_left_lang_index_; + } + + /** + * Sets the index for right int_ref + * @param idx Index for the right int_ref. + */ + void set_int_ref_right_index(const uint32_t idx) { + int_ref_right_index_ = idx; + } + + /** + * Get the right int_ref index. + * @return Returns the index for the right int_ref. + */ + uint32_t int_ref_right_index() const { + return int_ref_right_index_; + } + + /** + * Sets the index for int_ref:right: + * @param idx Index for the right languages. + */ + void set_int_ref_right_lang_index(const uint32_t idx) { + int_ref_right_lang_index_ = idx; + } + + /** + * Get the int_ref:right: index. + * @return Returns the index for the right languages. + */ + uint32_t int_ref_right_lang_index() const { + return int_ref_right_lang_index_; + } + /** * Sets the index for name * @param idx Index for the name. @@ -171,19 +360,147 @@ struct OSMWay { } /** - * Sets the index for name:en - * @param idx Index for the English name. + * Sets the index for name: + * @param idx Index for the languages. + */ + void set_name_lang_index(const uint32_t idx) { + name_lang_index_ = idx; + } + + /** + * Get the name: index. + * @return Returns the index for the languages. */ - void set_name_en_index(const uint32_t idx) { - name_en_index_ = idx; + uint32_t name_lang_index() const { + return name_lang_index_; } /** - * Get the name:en index. - * @return Returns the index for the English name. + * Sets the index for left name + * @param idx Index for the left name. */ - uint32_t name_en_index() const { - return name_en_index_; + void set_name_left_index(const uint32_t idx) { + name_left_index_ = idx; + } + + /** + * Get the left name index. + * @return Returns the index for the left name. + */ + uint32_t name_left_index() const { + return name_left_index_; + } + + /** + * Sets the index for name:left: + * @param idx Index for the left languages. + */ + void set_name_left_lang_index(const uint32_t idx) { + name_left_lang_index_ = idx; + } + + /** + * Get the name:left: index. + * @return Returns the index for the left languages. + */ + uint32_t name_left_lang_index() const { + return name_left_lang_index_; + } + + /** + * Sets the index for right name + * @param idx Index for the right name. + */ + void set_name_right_index(const uint32_t idx) { + name_right_index_ = idx; + } + + /** + * Get the right name index. + * @return Returns the index for the right name. + */ + uint32_t name_right_index() const { + return name_right_index_; + } + + /** + * Sets the index for name:right: + * @param idx Index for the right languages. + */ + void set_name_right_lang_index(const uint32_t idx) { + name_right_lang_index_ = idx; + } + + /** + * Get the name:right: index. + * @return Returns the index for the right languages. + */ + uint32_t name_right_lang_index() const { + return name_right_lang_index_; + } + + /** + * Sets the index for name forward + * @param idx Index for name forward. + */ + void set_name_forward_index(const uint32_t idx) { + name_forward_index_ = idx; + } + + /** + * Get the name forward index. + * @return Returns the index for name forward. + */ + uint32_t name_forward_index() const { + return name_forward_index_; + } + + /** + * Sets the index for name backward + * @param idx Index for name backward. + */ + void set_name_backward_index(const uint32_t idx) { + name_backward_index_ = idx; + } + + /** + * Get the name backward index. + * @return Returns the index for name backward. + */ + uint32_t name_backward_index() const { + return name_backward_index_; + } + + /** + * Sets the index for name forward lang + * @param idx Index for name forward lang. + */ + void set_name_forward_lang_index(const uint32_t idx) { + name_forward_lang_index_ = idx; + } + + /** + * Get the name forward lang index. + * @return Returns the index for name forward lang. + */ + uint32_t name_forward_lang_index() const { + return name_forward_lang_index_; + } + + /** + * Sets the index for name backward lang + * @param idx Index for name backward lang. + */ + void set_name_backward_lang_index(const uint32_t idx) { + name_backward_lang_index_ = idx; + } + + /** + * Get the name backward lang index. + * @return Returns the index for name backward lang. + */ + uint32_t name_backward_lang_index() const { + return name_backward_lang_index_; } /** @@ -202,6 +519,86 @@ struct OSMWay { return alt_name_index_; } + /** + * Sets the index for alt_name: + * @param idx Index for the languages. + */ + void set_alt_name_lang_index(const uint32_t idx) { + alt_name_lang_index_ = idx; + } + + /** + * Get the alt_name: index. + * @return Returns the index for the languages. + */ + uint32_t alt_name_lang_index() const { + return alt_name_lang_index_; + } + + /** + * Sets the index for left alt_name + * @param idx Index for the left alt_name. + */ + void set_alt_name_left_index(const uint32_t idx) { + alt_name_left_index_ = idx; + } + + /** + * Get the left alt_name index. + * @return Returns the index for the left alt_name. + */ + uint32_t alt_name_left_index() const { + return alt_name_left_index_; + } + + /** + * Sets the index for alt_name:left: + * @param idx Index for the left languages. + */ + void set_alt_name_left_lang_index(const uint32_t idx) { + alt_name_left_lang_index_ = idx; + } + + /** + * Get the alt_name:left: index. + * @return Returns the index for the left languages. + */ + uint32_t alt_name_left_lang_index() const { + return alt_name_left_lang_index_; + } + + /** + * Sets the index for right alt_name + * @param idx Index for the right alt_name. + */ + void set_alt_name_right_index(const uint32_t idx) { + alt_name_right_index_ = idx; + } + + /** + * Get the right alt_name index. + * @return Returns the index for the right alt_name. + */ + uint32_t alt_name_right_index() const { + return alt_name_right_index_; + } + + /** + * Sets the index for alt_name:right: + * @param idx Index for the right languages. + */ + void set_alt_name_right_lang_index(const uint32_t idx) { + alt_name_right_lang_index_ = idx; + } + + /** + * Get the alt_name:right: index. + * @return Returns the index for the right languages. + */ + uint32_t alt_name_right_lang_index() const { + return alt_name_right_lang_index_; + } + /** * Sets the index for official name * @param idx Index for the official name. @@ -218,6 +615,86 @@ struct OSMWay { return official_name_index_; } + /** + * Sets the index for official_name: + * @param idx Index for the languages. + */ + void set_official_name_lang_index(const uint32_t idx) { + official_name_lang_index_ = idx; + } + + /** + * Get the official_name: index. + * @return Returns the index for the languages. + */ + uint32_t official_name_lang_index() const { + return official_name_lang_index_; + } + + /** + * Sets the index for left official_name + * @param idx Index for the left official_name. + */ + void set_official_name_left_index(const uint32_t idx) { + official_name_left_index_ = idx; + } + + /** + * Get the left official_name index. + * @return Returns the index for the left official_name. + */ + uint32_t official_name_left_index() const { + return official_name_left_index_; + } + + /** + * Sets the index for official_name:left: + * @param idx Index for the left languages. + */ + void set_official_name_left_lang_index(const uint32_t idx) { + official_name_left_lang_index_ = idx; + } + + /** + * Get the official_name:left: index. + * @return Returns the index for the left languages. + */ + uint32_t official_name_left_lang_index() const { + return official_name_left_lang_index_; + } + + /** + * Sets the index for right official_name + * @param idx Index for the right official_name. + */ + void set_official_name_right_index(const uint32_t idx) { + official_name_right_index_ = idx; + } + + /** + * Get the right official_name index. + * @return Returns the index for the right official_name. + */ + uint32_t official_name_right_index() const { + return official_name_right_index_; + } + + /** + * Sets the index for official_name:right: + * @param idx Index for the right languages. + */ + void set_official_name_right_lang_index(const uint32_t idx) { + official_name_right_lang_index_ = idx; + } + + /** + * Get the official_name:right: index. + * @return Returns the index for the right languages. + */ + uint32_t official_name_right_lang_index() const { + return official_name_right_lang_index_; + } + /** * Sets the index for tunnel name * @param idx Index for the tunnel name. @@ -234,6 +711,86 @@ struct OSMWay { return tunnel_name_index_; } + /** + * Sets the index for tunnel_name: + * @param idx Index for the languages. + */ + void set_tunnel_name_lang_index(const uint32_t idx) { + tunnel_name_lang_index_ = idx; + } + + /** + * Get the tunnel_name: index. + * @return Returns the index for the languages. + */ + uint32_t tunnel_name_lang_index() const { + return tunnel_name_lang_index_; + } + + /** + * Sets the index for left tunnel_name + * @param idx Index for the left tunnel_name. + */ + void set_tunnel_name_left_index(const uint32_t idx) { + tunnel_name_left_index_ = idx; + } + + /** + * Get the left tunnel_name index. + * @return Returns the index for the left tunnel_name. + */ + uint32_t tunnel_name_left_index() const { + return tunnel_name_left_index_; + } + + /** + * Sets the index for tunnel_name:left: + * @param idx Index for the left languages. + */ + void set_tunnel_name_left_lang_index(const uint32_t idx) { + tunnel_name_left_lang_index_ = idx; + } + + /** + * Get the tunnel_name:left: index. + * @return Returns the index for the left languages. + */ + uint32_t tunnel_name_left_lang_index() const { + return tunnel_name_left_lang_index_; + } + + /** + * Sets the index for right tunnel_name + * @param idx Index for the right tunnel_name. + */ + void set_tunnel_name_right_index(const uint32_t idx) { + tunnel_name_right_index_ = idx; + } + + /** + * Get the right tunnel_name index. + * @return Returns the index for the right tunnel_name. + */ + uint32_t tunnel_name_right_index() const { + return tunnel_name_right_index_; + } + + /** + * Sets the index for tunnel_name:right: + * @param idx Index for the right languages. + */ + void set_tunnel_name_right_lang_index(const uint32_t idx) { + tunnel_name_right_lang_index_ = idx; + } + + /** + * Get the tunnel_name:right: index. + * @return Returns the index for the right languages. + */ + uint32_t tunnel_name_right_lang_index() const { + return tunnel_name_right_lang_index_; + } + /** * Sets the index for forward turn lanes string. * @param idx Index for the forward turn lanes string. @@ -377,6 +934,22 @@ struct OSMWay { return destination_index_; } + /** + * Sets the index for destination: + * @param idx Index for the destination with languages. + */ + void set_destination_lang_index(const uint32_t idx) { + destination_lang_index_ = idx; + } + + /** + * Get the destination: + * @return Returns the destination with languages. + */ + uint32_t destination_lang_index() const { + return destination_lang_index_; + } + /** * Sets the index for destination in forward direction. * @param idx Index for the destination. @@ -393,6 +966,22 @@ struct OSMWay { return destination_forward_index_; } + /** + * Sets the index for destination lang in forward direction. + * @param idx Index for the destination. + */ + void set_destination_forward_lang_index(const uint32_t idx) { + destination_forward_lang_index_ = idx; + } + + /** + * Get the destination lang in forward direction index. + * @return Returns the index for the destination lang in forward direction. + */ + uint32_t destination_forward_lang_index() const { + return destination_forward_lang_index_; + } + /** * Sets the index for destination in backward direction. * @param idx Index for the destination. @@ -409,6 +998,22 @@ struct OSMWay { return destination_backward_index_; } + /** + * Sets the index for destination lang in backward direction. + * @param idx Index for the destination. + */ + void set_destination_backward_lang_index(const uint32_t idx) { + destination_backward_lang_index_ = idx; + } + + /** + * Get the destination lang in backward direction index. + * @return Returns the index for the destination lang in backward direction. + */ + uint32_t destination_backward_lang_index() const { + return destination_backward_lang_index_; + } + /** * Sets the index for destination ref. * @param idx Index for the destination ref. @@ -425,6 +1030,22 @@ struct OSMWay { return destination_ref_index_; } + /** + * Sets the index for destination ref lang. + * @param idx Index for the destination:ref lang. + */ + void set_destination_ref_lang_index(const uint32_t idx) { + destination_ref_lang_index_ = idx; + } + + /** + * Get the destination_ref_lang index. + * @return Returns the index for the destination:ref lang. + */ + uint32_t destination_ref_lang_index() const { + return destination_ref_lang_index_; + } + /** * Sets the index for destination ref to. * @param idx Index for the destination ref to. @@ -441,6 +1062,54 @@ struct OSMWay { return destination_ref_to_index_; } + /** + * Sets the index for destination:ref:to lang + * @param idx Index for the destination:ref:to lang + */ + void set_destination_ref_to_lang_index(const uint32_t idx) { + destination_ref_to_lang_index_ = idx; + } + + /** + * Get the destination:ref:to lang index. + * @return Returns the index for destination:ref:to lang. + */ + uint32_t destination_ref_to_lang_index() const { + return destination_ref_to_lang_index_; + } + + /** + * Sets the index for destination international ref. + * @param idx Index for the destination international ref. + */ + void set_destination_int_ref_index(const uint32_t idx) { + destination_int_ref_index_ = idx; + } + + /** + * Get the destination international ref index. + * @return Returns the index for the destination international ref. + */ + uint32_t destination_int_ref_index() const { + return destination_int_ref_index_; + } + + /** + * Sets the index for destination international ref to. + * @param idx Index for the destination international ref to. + */ + void set_destination_int_ref_to_index(const uint32_t idx) { + destination_int_ref_to_index_ = idx; + } + + /** + * Get the destination international ref to index. + * @return Returns the index for the destination ref to. + */ + uint32_t destination_int_ref_to_index() const { + return destination_int_ref_to_index_; + } + /** * Sets the index for destination street. * @param idx Index for the destination street. @@ -457,6 +1126,22 @@ struct OSMWay { return destination_street_index_; } + /** + * Sets the index for destination:street lang + * @param idx Index for the destination:street lang + */ + void set_destination_street_lang_index(const uint32_t idx) { + destination_street_lang_index_ = idx; + } + + /** + * Get the destination:street lang index. + * @return Returns the index for destination:street lang. + */ + uint32_t destination_street_lang_index() const { + return destination_street_lang_index_; + } + /** * Sets the index for destination street to. * @param idx Index for the destination street to. @@ -473,6 +1158,54 @@ struct OSMWay { return destination_street_to_index_; } + /** + * Sets the index for destination:street:to lang + * @param idx Index for the destination:street:to lang + */ + void set_destination_street_to_lang_index(const uint32_t idx) { + destination_street_to_lang_index_ = idx; + } + + /** + * Get the destination:street:to lang index. + * @return Returns the index for destination:street:to lang. + */ + uint32_t destination_street_to_lang_index() const { + return destination_street_to_lang_index_; + } + + /** + * Sets the index for junction name pronunciation. + * @param idx Index for the junction name pronunciation. + */ + void set_junction_name_index(const uint32_t idx) { + junction_name_index_ = idx; + } + + /** + * Get the junction name pronunciation index. + * @return Returns the index for the junction name pronunciation. + */ + uint32_t junction_name_index() const { + return junction_name_index_; + } + + /** + * Sets the index for junction name lang. + * @param idx Index for the junction:name:lang. + */ + void set_junction_name_lang_index(const uint32_t idx) { + junction_name_lang_index_ = idx; + } + + /** + * Get the junction name pronunciation index. + * @return Returns the index for the junction name pronunciation. + */ + uint32_t junction_name_lang_index() const { + return junction_name_lang_index_; + } + /** * Sets the index for junction ref pronunciation. * @param idx Index for the junction ref pronunciation. @@ -489,6 +1222,22 @@ struct OSMWay { return junction_ref_index_; } + /** + * Sets the index for junction ref lang. + * @param idx Index for the junction:ref lang. + */ + void set_junction_ref_lang_index(const uint32_t idx) { + junction_ref_lang_index_ = idx; + } + + /** + * Get the junction_ref_lang index. + * @return Returns the index for the junction:ref lang. + */ + uint32_t junction_ref_lang_index() const { + return junction_ref_lang_index_; + } + /** * Sets the index for bike national ref. * @param idx Index for the name of the national bike network. @@ -878,7 +1627,7 @@ struct OSMWay { } /** - * Sets the destination_only flag. + * Sets the destination_only flag for cars or motor vehicles. * @param destination_only Is private? */ void set_destination_only(const bool destination_only) { @@ -886,13 +1635,29 @@ struct OSMWay { } /** - * Get the destination only/private flag. + * Get the destination only/private flag for cars or motor vehicles. * @return Returns private flag. */ bool destination_only() const { return destination_only_; } + /** + * Sets the destination_only flag specifically for HGV. + * @param destination_only Is private for HGV? + */ + void set_destination_only_hgv(const bool destination_only) { + destination_only_hgv_ = destination_only; + } + + /** + * Get the destination only/private flag for HGV. + * @return Returns private hgv flag. + */ + bool destination_only_hgv() const { + return destination_only_hgv_; + } + /** * Sets the has_user_tags flag. * @param has_user_tags Did a user enter the access tags? @@ -1193,7 +1958,7 @@ struct OSMWay { /* * Gets whether a pedestrian or cyclist should have preference to use a different * path to the side (A separate OSMWay completely) - * @return Returns if using a sidepath is preffered + * @return Returns if using a sidepath is preferred */ bool use_sidepath() const { return use_sidepath_; @@ -1628,13 +2393,34 @@ struct OSMWay { } void AddPronunciations(std::vector& pronunciations, + std::map& lang_map, const UniqueNames& name_offset_map, + const std::vector>& default_languages, + const std::vector& token_langs, const uint32_t ipa_index, + const uint32_t ipa_lang_index, const uint32_t nt_sampa_index, + const uint32_t nt_sampa_lang_index, const uint32_t katakana_index, + const uint32_t katakana_lang_index, const uint32_t jeita_index, - const size_t name_tokens_size, - const size_t key) const; + const uint32_t jeita_lang_index, + const size_t token_size, + const size_t key, + bool diff_names) const; + + void AddPronunciationsWithLang(std::vector& pronunciations, + std::map& lang_map, + const baldr::PronunciationAlphabet verbal_type, + const std::vector& pronunciation_tokens, + const std::vector& pronunciation_langs, + const std::vector& token_langs, + const size_t token_size, + const size_t key) const; + + void AddLanguage(std::vector& linguistics, + const size_t index, + const baldr::Language& lang) const; /** * Sets layer index(Z-level) of the way. @@ -1700,6 +2486,16 @@ struct OSMWay { return lit_; } + static void + ProcessNamesPronunciations(const UniqueNames& name_offset_map, + const std::vector>& default_languages, + const uint32_t name_index, + const uint32_t name_lang_index, + std::vector& tokens, + std::vector& token_langs, + bool diff_names, + bool allow_empty_tokens = false); + /** * Get the names for the edge info based on the road class. * @param ref updated refs from relations. @@ -1708,30 +2504,86 @@ struct OSMWay { */ void GetNames(const std::string& ref, const UniqueNames& name_offset_map, - const OSMPronunciation& pronunciation, + const std::map, uint32_t>& pronunciationMap, + const std::map, uint32_t>& langMap, + const std::vector>& default_languages, + const uint32_t ref_index, + const uint32_t ref_lang_index, + const uint32_t name_index, + const uint32_t name_lang_index, + const uint32_t official_name_index, + const uint32_t official_name_lang_index, + const uint32_t alt_name_index, + const uint32_t alt_name_lang_index, uint16_t& types, std::vector& names, - std::vector& pronunciations) const; + std::vector& linguistics, + OSMLinguistic::DiffType type = OSMLinguistic::DiffType::kRight, + bool diff_names = false) const; void GetTaggedValues(const UniqueNames& name_offset_map, - const OSMPronunciation& pronunciation, + const std::map, uint32_t>& pronunciationMap, + const std::map, uint32_t>& langMap, + const std::vector>& default_languages, + const uint32_t tunnel_name_index, + const uint32_t tunnel_name_lang_index, const size_t& names_size, std::vector& names, - std::vector& pronunciations) const; + std::vector& linguistics, + OSMLinguistic::DiffType type = OSMLinguistic::DiffType::kRight, + bool diff_names = false) const; // OSM way Id uint64_t osmwayid_; // Reference name (highway numbers) uint32_t ref_index_; + uint32_t ref_lang_index_; + uint32_t ref_left_index_; + uint32_t ref_left_lang_index_; + uint32_t ref_right_index_; + uint32_t ref_right_lang_index_; + uint32_t int_ref_index_; + uint32_t int_ref_lang_index_; + uint32_t int_ref_left_index_; + uint32_t int_ref_left_lang_index_; + uint32_t int_ref_right_index_; + uint32_t int_ref_right_lang_index_; // Names uint32_t name_index_; - uint32_t name_en_index_; + uint32_t name_lang_index_; + uint32_t name_left_index_; + uint32_t name_left_lang_index_; + uint32_t name_right_index_; + uint32_t name_right_lang_index_; + + uint32_t name_forward_index_; + uint32_t name_forward_lang_index_; + uint32_t name_backward_index_; + uint32_t name_backward_lang_index_; + uint32_t alt_name_index_; + uint32_t alt_name_lang_index_; + uint32_t alt_name_left_index_; + uint32_t alt_name_left_lang_index_; + uint32_t alt_name_right_index_; + uint32_t alt_name_right_lang_index_; + uint32_t official_name_index_; + uint32_t official_name_lang_index_; + uint32_t official_name_left_index_; + uint32_t official_name_left_lang_index_; + uint32_t official_name_right_index_; + uint32_t official_name_right_lang_index_; + uint32_t tunnel_name_index_; + uint32_t tunnel_name_lang_index_; + uint32_t tunnel_name_left_index_; + uint32_t tunnel_name_left_lang_index_; + uint32_t tunnel_name_right_index_; + uint32_t tunnel_name_right_lang_index_; // Turn lanes uint32_t fwd_turn_lanes_index_; @@ -1749,13 +2601,25 @@ struct OSMWay { // Sign Destination information uint32_t destination_index_; + uint32_t destination_lang_index_; uint32_t destination_forward_index_; uint32_t destination_backward_index_; + uint32_t destination_forward_lang_index_; + uint32_t destination_backward_lang_index_; uint32_t destination_ref_index_; + uint32_t destination_ref_lang_index_; uint32_t destination_ref_to_index_; + uint32_t destination_ref_to_lang_index_; + uint32_t destination_int_ref_index_; + uint32_t destination_int_ref_to_index_; uint32_t destination_street_index_; + uint32_t destination_street_lang_index_; uint32_t destination_street_to_index_; + uint32_t destination_street_to_lang_index_; + uint32_t junction_name_index_; + uint32_t junction_name_lang_index_; uint32_t junction_ref_index_; + uint32_t junction_ref_lang_index_; // level and level:ref of the way uint32_t level_index_; @@ -1843,8 +2707,9 @@ struct OSMWay { uint16_t use_sidepath_ : 1; uint16_t bike_forward_ : 1; uint16_t bike_backward_ : 1; - bool lit_ : 1; - uint16_t spare2_ : 3; + uint16_t lit_ : 1; + uint16_t destination_only_hgv_ : 1; + uint16_t spare2_ : 2; uint16_t nodecount_; @@ -1863,6 +2728,8 @@ struct OSMWay { // Truck speed in kilometers per hour uint8_t truck_speed_; + uint8_t truck_speed_forward_; + uint8_t truck_speed_backward_; // layer index(Z-level) of the way relatively to other levels int8_t layer_; diff --git a/valhalla/mjolnir/pbfadminparser.h b/valhalla/mjolnir/pbfadminparser.h index 292c976433..100a51e7ac 100644 --- a/valhalla/mjolnir/pbfadminparser.h +++ b/valhalla/mjolnir/pbfadminparser.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_PBFADMINPARSER_H #include -#include #include #include diff --git a/valhalla/mjolnir/pbfgraphparser.h b/valhalla/mjolnir/pbfgraphparser.h index 4d71e0df24..daa1f9fdeb 100644 --- a/valhalla/mjolnir/pbfgraphparser.h +++ b/valhalla/mjolnir/pbfgraphparser.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_PBFGRAPHPARSER_H #include -#include #include #include @@ -30,8 +29,7 @@ class PBFGraphParser { const std::vector& input_files, const std::string& ways_file, const std::string& way_nodes_file, - const std::string& access_file, - const std::string& pronunciation_file); + const std::string& access_file); /** * Loads given input files @@ -56,12 +54,16 @@ class PBFGraphParser { * @param input_files the protobuf files to parse * @param way_nodes_file where to store the nodes so they are not in memory * @param bss_nodes_file where to store the bss nodes so they are not in memory + * @param linguistic_node_file where to store the linguistic information for nodes so + * they are not in memory + * * @param osmdata OSM data */ static void ParseNodes(const boost::property_tree::ptree& pt, const std::vector& input_files, const std::string& way_nodes_file, const std::string& bss_nodes_file, + const std::string& linguistic_node_file, OSMData& osmdata); }; diff --git a/valhalla/mjolnir/restrictionbuilder.h b/valhalla/mjolnir/restrictionbuilder.h index eada9ba9df..da6a758a2e 100644 --- a/valhalla/mjolnir/restrictionbuilder.h +++ b/valhalla/mjolnir/restrictionbuilder.h @@ -2,9 +2,6 @@ #define VALHALLA_MJOLNIR_RESTRICTIONBUILDER_H #include -#include -#include -#include namespace valhalla { namespace mjolnir { diff --git a/valhalla/mjolnir/servicedays.h b/valhalla/mjolnir/servicedays.h index de09ca7ab1..d6f9835ff3 100644 --- a/valhalla/mjolnir/servicedays.h +++ b/valhalla/mjolnir/servicedays.h @@ -4,10 +4,6 @@ #include #include #include -#include -#include -#include -#include #include #include diff --git a/valhalla/mjolnir/shortcutbuilder.h b/valhalla/mjolnir/shortcutbuilder.h index 3fbebe1191..0e8bcdd8d6 100644 --- a/valhalla/mjolnir/shortcutbuilder.h +++ b/valhalla/mjolnir/shortcutbuilder.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_SHORTCUTBUILDER_H #include -#include namespace valhalla { namespace mjolnir { diff --git a/valhalla/mjolnir/timeparsing.h b/valhalla/mjolnir/timeparsing.h index 91c9827d72..f2946bab99 100644 --- a/valhalla/mjolnir/timeparsing.h +++ b/valhalla/mjolnir/timeparsing.h @@ -10,14 +10,14 @@ namespace valhalla { namespace mjolnir { /** - * get the dow mask from user inputed string. try to handle most inputs + * get the dow mask from the provided string. try to handle most inputs * @param dow entered by a user * @return dow mask */ uint8_t get_dow_mask(const std::string& dow); /** - * get the dow from user inputed string. try to handle most inputs + * get the dow from the provided string. try to handle most inputs * @param dow entered by a user * @return DOW */ diff --git a/valhalla/mjolnir/transitbuilder.h b/valhalla/mjolnir/transitbuilder.h index ec15958545..aff172e7a7 100644 --- a/valhalla/mjolnir/transitbuilder.h +++ b/valhalla/mjolnir/transitbuilder.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_TRANSITBUILDER_H #include -#include namespace valhalla { namespace mjolnir { diff --git a/valhalla/mjolnir/util.h b/valhalla/mjolnir/util.h index 5601efc156..39d4947577 100644 --- a/valhalla/mjolnir/util.h +++ b/valhalla/mjolnir/util.h @@ -12,7 +12,9 @@ // needs to be after sqlite include #include +#include #include +#include #include #include #include @@ -103,6 +105,14 @@ inline std::string to_string(BuildStage stg) { */ std::vector GetTagTokens(const std::string& tag_value, char delim = ';'); +/** + * Splits a tag into a vector of strings. + * @param tag_value tag to split + * @param delim delimiter + * @return the vector of strings + */ +std::vector GetTagTokens(const std::string& tag_value, const std::string& delim_str); + /** * Remove double quotes. * @param s @@ -119,6 +129,21 @@ std::string remove_double_quotes(const std::string& s); bool shapes_match(const std::vector& shape1, const std::vector& shape2); +/** + * Get the index of the opposing edge at the end node. This is on the local hierarchy, + * before adding transition and shortcut edges. Make sure that even if the end nodes + * and lengths match that the correct edge is selected (match shape) since some loops + * can have the same length and end node. + * @param endnodetile Graph tile at the end node. + * @param startnode Start node of the directed edge. + * @param tile Graph tile of the edge + * @param directededge Directed edge to match. + */ +uint32_t GetOpposingEdgeIndex(const baldr::graph_tile_ptr& endnodetile, + const baldr::GraphId& startnode, + const graph_tile_ptr& tile, + const baldr::DirectedEdge& edge); + /** * Compute a curvature metric given an edge shape. * @param shape Shape of an edge (list of lat,lon vertices). diff --git a/valhalla/mjolnir/valhalla_add_elevation_utils.h b/valhalla/mjolnir/valhalla_add_elevation_utils.h deleted file mode 100644 index 4810dcfc11..0000000000 --- a/valhalla/mjolnir/valhalla_add_elevation_utils.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#include "baldr/graphid.h" -#include "baldr/graphreader.h" -#include "baldr/graphtile.h" -#include "filesystem.h" -#include "midgard/logging.h" - -namespace valhalla { -namespace mjolnir { - -std::deque get_tile_ids(const boost::property_tree::ptree& pt, - const std::unordered_set& tiles) { - if (tiles.empty()) - return {}; - - auto tile_dir = pt.get_optional("mjolnir.tile_dir"); - if (!tile_dir || !filesystem::exists(*tile_dir)) { - LOG_WARN("Tile storage directory does not exist"); - return {}; - } - - std::deque tilequeue; - baldr::GraphReader reader(pt.get_child("mjolnir")); - std::for_each(std::begin(tiles), std::end(tiles), [&](const auto& tile) { - auto tile_id = baldr::GraphTile::GetTileId(*tile_dir + tile); - baldr::GraphId local_tile_id(tile_id.tileid(), tile_id.level(), tile_id.id()); - if (!reader.DoesTileExist(local_tile_id)) { - LOG_WARN("Provided tile doesn't belong to the tile directory from config file"); - return; - } - - tilequeue.push_back(tile_id); - }); - - return tilequeue; -} - -} // namespace mjolnir -} // namespace valhalla diff --git a/valhalla/mjolnir/validatetransit.h b/valhalla/mjolnir/validatetransit.h index 453df5f3ef..099ba8b2ea 100644 --- a/valhalla/mjolnir/validatetransit.h +++ b/valhalla/mjolnir/validatetransit.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_VALIDATETRANSIT_H #include -#include #include #include diff --git a/valhalla/odin/enhancedtrippath.h b/valhalla/odin/enhancedtrippath.h index 9be27de951..343c015a1f 100644 --- a/valhalla/odin/enhancedtrippath.h +++ b/valhalla/odin/enhancedtrippath.h @@ -88,6 +88,10 @@ class EnhancedTripLeg { return trip_path_.bbox(); } + const ::valhalla::Summary& summary() const { + return trip_path_.summary(); + } + std::unique_ptr GetEnhancedNode(const int node_index); std::unique_ptr GetPrevEdge(const int node_index, int delta = 1); @@ -140,6 +144,10 @@ class EnhancedTripLeg_Edge { return mutable_edge_->tagged_value(); } + const ::google::protobuf::RepeatedPtrField<::valhalla::RouteLandmark>& landmarks() const { + return mutable_edge_->landmarks(); + } + float length_km() const { return mutable_edge_->length_km(); } @@ -463,6 +471,18 @@ class EnhancedTripLeg_IntersectingEdge { public: EnhancedTripLeg_IntersectingEdge(TripLeg_IntersectingEdge* mutable_intersecting_edge); + int name_size() const { + return mutable_intersecting_edge_->name_size(); + } + + const ::valhalla::StreetName& name(int index) const { + return mutable_intersecting_edge_->name(index); + } + + const ::google::protobuf::RepeatedPtrField<::valhalla::StreetName>& name() const { + return mutable_intersecting_edge_->name(); + } + uint32_t begin_heading() const { return mutable_intersecting_edge_->begin_heading(); } @@ -598,6 +618,9 @@ class EnhancedTripLeg_Node { return mutable_node_->type(); } + bool traffic_signal() const { + return mutable_node_->traffic_signal(); + } double elapsed_time() const { return mutable_node_->cost().elapsed_cost().seconds(); } @@ -657,7 +680,7 @@ class EnhancedTripLeg_Node { const TravelMode travel_mode, IntersectingEdgeCounts& xedge_counts); - bool HasFowardIntersectingEdge(uint32_t from_heading); + bool HasForwardIntersectingEdge(uint32_t from_heading); bool HasForwardTraversableIntersectingEdge(uint32_t from_heading, const TravelMode travel_mode); @@ -790,7 +813,7 @@ const std::unordered_map TripLeg_VehicleType_Strings{ {static_cast(VehicleType::kCar), "car"}, {static_cast(VehicleType::kMotorcycle), "motorcycle"}, {static_cast(VehicleType::kAutoBus), "bus"}, - {static_cast(VehicleType::kTractorTrailer), "tractor_trailer"}, + {static_cast(VehicleType::kTruck), "truck"}, }; inline std::string to_string(VehicleType vehicle_type) { auto i = TripLeg_VehicleType_Strings.find(static_cast(vehicle_type)); @@ -803,7 +826,6 @@ inline std::string to_string(VehicleType vehicle_type) { const std::unordered_map TripLeg_PedestrianType_Strings{ {static_cast(PedestrianType::kFoot), "foot"}, {static_cast(PedestrianType::kWheelchair), "wheelchair"}, - {static_cast(PedestrianType::kSegway), "segway"}, }; inline std::string to_string(PedestrianType pedestrian_type) { auto i = TripLeg_PedestrianType_Strings.find(static_cast(pedestrian_type)); diff --git a/valhalla/odin/maneuver.h b/valhalla/odin/maneuver.h index c80ca493d7..22bfd08c69 100644 --- a/valhalla/odin/maneuver.h +++ b/valhalla/odin/maneuver.h @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -59,6 +58,18 @@ class Maneuver { bool IsRightType() const; bool IsLeftType() const; + void set_node_type(TripLeg_Node_Type type); + TripLeg_Node_Type node_type() const; + bool has_node_type() const; + bool traffic_signal() const; + void set_traffic_signal(bool traffic_signal); + bool is_steps() const; + void set_steps(bool steps); + bool is_bridge() const; + void set_bridge(bool bridge); + bool is_tunnel() const; + void set_tunnel(bool tunnel); + const StreetNames& street_names() const; void set_street_names(const std::vector>& names); void set_street_names(std::unique_ptr&& street_names); @@ -394,7 +405,10 @@ class Maneuver { void set_building_exit(bool building_exit); std::string end_level_ref() const; - void set_end_level_ref(std::string end_level_ref); + void set_end_level_ref(const std::string& end_level_ref); + + const std::vector& landmarks() const; + void set_landmarks(const std::vector& landmarks); #ifdef LOGGING_LEVEL_TRACE std::string ToString() const; @@ -404,6 +418,12 @@ class Maneuver { protected: DirectionsLeg_Maneuver_Type type_; + TripLeg_Node_Type node_type_; + bool has_node_type_; + bool traffic_signal_; + bool is_steps_; + bool is_bridge_; + bool is_tunnel_; std::unique_ptr street_names_; std::unique_ptr begin_street_names_; std::unique_ptr cross_street_names_; @@ -479,6 +499,9 @@ class Maneuver { bool building_exit_; std::string end_level_ref_; + // Landmarks correlated to the maneuver + std::vector landmarks_; + //////////////////////////////////////////////////////////////////////////// // Transit support diff --git a/valhalla/odin/maneuversbuilder.h b/valhalla/odin/maneuversbuilder.h index db0edfef64..9ecad2cb7c 100644 --- a/valhalla/odin/maneuversbuilder.h +++ b/valhalla/odin/maneuversbuilder.h @@ -319,6 +319,14 @@ class ManeuversBuilder { */ void CollapseMergeManeuvers(std::list& maneuvers); + /** + * Add relevant landmarks to maneuvers as direction support. + * Each maneuver should get the landmarks accosiated with edges that make up the previous maneuver. + * + * @param maneuvers The list of maneuvers to add landmarks. + */ + void AddLandmarksFromTripLegToManeuvers(std::list& maneuvers); + const Options& options_; EnhancedTripLeg* trip_path_; }; diff --git a/valhalla/odin/narrative_builder_factory.h b/valhalla/odin/narrative_builder_factory.h index c628bd730a..48433d0eae 100644 --- a/valhalla/odin/narrative_builder_factory.h +++ b/valhalla/odin/narrative_builder_factory.h @@ -2,7 +2,6 @@ #define VALHALLA_ODIN_NARRATIVE_BUILDER_FACTORY_H_ #include -#include #include #include diff --git a/valhalla/odin/narrative_dictionary.h b/valhalla/odin/narrative_dictionary.h index ac7b58e538..e4d8b705ef 100644 --- a/valhalla/odin/narrative_dictionary.h +++ b/valhalla/odin/narrative_dictionary.h @@ -76,6 +76,7 @@ constexpr auto kPostTransitionVerbalKey = "instructions.post_transition_verbal"; constexpr auto kPostTransitTransitionVerbalKey = "instructions.post_transition_transit_verbal"; constexpr auto kVerbalMultiCueKey = "instructions.verbal_multi_cue"; constexpr auto kApproachVerbalAlertKey = "instructions.approach_verbal_alert"; +constexpr auto kPassKey = "instructions.pass"; constexpr auto kElevatorKey = "instructions.elevator"; constexpr auto kStepsKey = "instructions.steps"; constexpr auto kEscalatorKey = "instructions.escalator"; @@ -95,6 +96,7 @@ constexpr auto kFerryLabelKey = "ferry_label"; constexpr auto kStationLabelKey = "station_label"; constexpr auto kEmptyTransitNameLabelsKey = "empty_transit_name_labels"; constexpr auto kTransitStopCountLabelsKey = "transit_stop_count_labels"; +constexpr auto kObjectLabelsKey = "object_labels"; constexpr auto kPluralCategoryZeroKey = "zero"; constexpr auto kPluralCategoryOneKey = "one"; @@ -108,6 +110,14 @@ constexpr auto kWalkwayIndex = 0; constexpr auto kCyclewayIndex = 1; constexpr auto kMountainBikeTrailIndex = 2; constexpr auto kPedestrianCrossingIndex = 3; +constexpr auto kStepsIndex = 4; +constexpr auto kBridgeIndex = 5; +constexpr auto kTunnelIndex = 6; + +// object label indexes +constexpr auto kGateIndex = 0; +constexpr auto kBollardIndex = 1; +constexpr auto kStreetIntersectionIndex = 2; // Metric length indexes constexpr auto kKilometersIndex = 0; @@ -134,6 +144,7 @@ constexpr auto kCrossStreetNamesTag = ""; constexpr auto kRoundaboutExitStreetNamesTag = ""; constexpr auto kRoundaboutExitBeginStreetNamesTag = ""; constexpr auto kRampExitNumbersVisualTag = ""; +constexpr auto kObjectLabelTag = ""; constexpr auto kLengthTag = ""; constexpr auto kDestinationTag = ""; constexpr auto kCurrentVerbalCueTag = ""; @@ -245,6 +256,10 @@ struct ApproachVerbalAlertSubset : PhraseSet { std::vector us_customary_lengths; }; +struct PassSubset : PhraseSet { + std::vector object_labels; +}; + struct EnterBuildingSubset : PhraseSet { std::vector empty_street_name_labels; }; @@ -376,6 +391,8 @@ class NarrativeDictionary { // Approach verbal alert ApproachVerbalAlertSubset approach_verbal_alert_subset; + // Pass + PassSubset pass_subset; // Elevator PhraseSet elevator_subset; @@ -594,6 +611,15 @@ class NarrativeDictionary { void Load(ApproachVerbalAlertSubset& approach_verbal_alert_handle, const boost::property_tree::ptree& approach_verbal_alert_subset_pt); + /** + * Loads the specified 'pass' instruction subset with the localized + * narrative instructions contained in the specified property tree. + * + * @param pass_handle The 'pass' structure to populate. + * @param pass_subset_pt The 'pass' property tree. + */ + void Load(PassSubset& pass_handle, const boost::property_tree::ptree& pass_subset_pt); + /** * Loads the specified 'enter_building' instruction subset with the localized * narrative instructions contained in the specified property tree. diff --git a/valhalla/odin/narrativebuilder.h b/valhalla/odin/narrativebuilder.h index ce7b8810ad..9ed444e3dd 100644 --- a/valhalla/odin/narrativebuilder.h +++ b/valhalla/odin/narrativebuilder.h @@ -436,6 +436,8 @@ class NarrativeBuilder { size_t stop_count, const std::unordered_map& transit_platform_count_labels); + std::string FormPassInstruction(Maneuver& maneuver); + /** * Returns the plural category based on the value of the specified * count and the language rules. diff --git a/valhalla/odin/util.h b/valhalla/odin/util.h index 13c428e8aa..d1b848b8c7 100644 --- a/valhalla/odin/util.h +++ b/valhalla/odin/util.h @@ -5,9 +5,6 @@ #include #include #include -#include - -#include #include #include @@ -57,7 +54,7 @@ bool IsSimilarTurnDegree(uint32_t path_turn_degree, uint32_t turn_degree_threshold = 40); /** - * Get the time from the inputed date. + * Get the time from the provided date. * date_time is in the format of 2015-05-06T08:00 * @param date_time in the format of 2015-05-06T08:00 * @param locale locale @@ -66,7 +63,7 @@ bool IsSimilarTurnDegree(uint32_t path_turn_degree, std::string get_localized_time(const std::string& date_time, const std::locale& locale); /** - * Get the date from the inputed date. + * Get the date from the provided date. * date_time is in the format of 2015-05-06T08:00 * @param date_time in the format of 2015-05-06T08:00 * @param locale locale diff --git a/valhalla/odin/worker.h b/valhalla/odin/worker.h index e8c3439773..7a326a9b5a 100644 --- a/valhalla/odin/worker.h +++ b/valhalla/odin/worker.h @@ -8,7 +8,7 @@ namespace valhalla { namespace odin { -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES void run_service(const boost::property_tree::ptree& config); #endif @@ -16,10 +16,10 @@ class odin_worker_t : public service_worker_t { public: odin_worker_t(const boost::property_tree::ptree& config); virtual ~odin_worker_t(); -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES virtual prime_server::worker_t::result_t work(const std::list& job, void* request_info, - const std::function& interupt) override; + const std::function& interrupt) override; #endif /** diff --git a/valhalla/proto_conversions.h b/valhalla/proto_conversions.h index 65cecb4d01..768bdc5d14 100644 --- a/valhalla/proto_conversions.h +++ b/valhalla/proto_conversions.h @@ -3,6 +3,7 @@ #include #include #include +#include #include namespace valhalla { @@ -31,9 +32,8 @@ inline TripLeg_Surface GetTripLegSurface(const baldr::Surface surface) { // Associate vehicle types to TripLeg proto // TODO - why doesn't these use an enum input? constexpr VehicleType kTripLegVehicleType[] = { - VehicleType::kCar, VehicleType::kMotorcycle, - VehicleType::kAutoBus, VehicleType::kTractorTrailer, - VehicleType::kMotorScooter, + VehicleType::kCar, VehicleType::kMotorcycle, VehicleType::kAutoBus, + VehicleType::kTruck, VehicleType::kMotorScooter, }; inline VehicleType GetTripLegVehicleType(const uint8_t type) { return (type <= static_cast(sif::VehicleType::kMotorScooter)) ? kTripLegVehicleType[type] @@ -41,14 +41,12 @@ inline VehicleType GetTripLegVehicleType(const uint8_t type) { } // Associate pedestrian types to TripLeg proto -constexpr PedestrianType kTripLegPedestrianType[] = { - PedestrianType::kFoot, - PedestrianType::kWheelchair, - PedestrianType::kSegway, -}; +constexpr PedestrianType kTripLegPedestrianType[] = {PedestrianType::kFoot, + PedestrianType::kWheelchair, + PedestrianType::kBlind}; inline PedestrianType GetTripLegPedestrianType(const uint8_t type) { - return (type <= static_cast(sif::PedestrianType::kSegway)) ? kTripLegPedestrianType[type] - : kTripLegPedestrianType[0]; + return (type <= static_cast(sif::PedestrianType::kBlind)) ? kTripLegPedestrianType[type] + : kTripLegPedestrianType[0]; } // Associate bicycle types to TripLeg proto @@ -132,18 +130,288 @@ inline Pronunciation_Alphabet GetTripPronunciationAlphabet(const valhalla::baldr::PronunciationAlphabet pronunciation_alphabet) { switch (pronunciation_alphabet) { case baldr::PronunciationAlphabet::kNone: + return Pronunciation_Alphabet_kNone; case baldr::PronunciationAlphabet::kIpa: return Pronunciation_Alphabet_kIpa; - case baldr::PronunciationAlphabet::kXKatakana: - return Pronunciation_Alphabet_kXKatakana; - case baldr::PronunciationAlphabet::kXJeita: - return Pronunciation_Alphabet_kXJeita; + case baldr::PronunciationAlphabet::kKatakana: + return Pronunciation_Alphabet_kKatakana; + case baldr::PronunciationAlphabet::kJeita: + return Pronunciation_Alphabet_kJeita; case baldr::PronunciationAlphabet::kNtSampa: return Pronunciation_Alphabet_kNtSampa; } - auto num = static_cast(pronunciation_alphabet); - throw std::runtime_error(std::string(__FILE__) + ":" + std::to_string(__LINE__) + - " Unhandled PronunciationAlphabet: " + std::to_string(num)); + return Pronunciation_Alphabet_kNone; +} + +inline LanguageTag GetTripLanguageTag(valhalla::baldr::Language l) { + static const std::unordered_map language_tag_map = + {{valhalla::baldr::Language::kAb, LanguageTag::kAb}, + {valhalla::baldr::Language::kAm, LanguageTag::kAm}, + {valhalla::baldr::Language::kAr, LanguageTag::kAr}, + {valhalla::baldr::Language::kAz, LanguageTag::kAz}, + {valhalla::baldr::Language::kBe, LanguageTag::kBe}, + {valhalla::baldr::Language::kBg, LanguageTag::kBg}, + {valhalla::baldr::Language::kBn, LanguageTag::kBn}, + {valhalla::baldr::Language::kBs, LanguageTag::kBs}, + {valhalla::baldr::Language::kCa, LanguageTag::kCa}, + {valhalla::baldr::Language::kCkb, LanguageTag::kCkb}, + {valhalla::baldr::Language::kCs, LanguageTag::kCs}, + {valhalla::baldr::Language::kDa, LanguageTag::kDa}, + {valhalla::baldr::Language::kDe, LanguageTag::kDe}, + {valhalla::baldr::Language::kDv, LanguageTag::kDv}, + {valhalla::baldr::Language::kDz, LanguageTag::kDz}, + {valhalla::baldr::Language::kEl, LanguageTag::kEl}, + {valhalla::baldr::Language::kEn, LanguageTag::kEn}, + {valhalla::baldr::Language::kEs, LanguageTag::kEs}, + {valhalla::baldr::Language::kEt, LanguageTag::kEt}, + {valhalla::baldr::Language::kFa, LanguageTag::kFa}, + {valhalla::baldr::Language::kFi, LanguageTag::kFi}, + {valhalla::baldr::Language::kFr, LanguageTag::kFr}, + {valhalla::baldr::Language::kFy, LanguageTag::kFy}, + {valhalla::baldr::Language::kGl, LanguageTag::kGl}, + {valhalla::baldr::Language::kHe, LanguageTag::kHe}, + {valhalla::baldr::Language::kHr, LanguageTag::kHr}, + {valhalla::baldr::Language::kHu, LanguageTag::kHu}, + {valhalla::baldr::Language::kHy, LanguageTag::kHy}, + {valhalla::baldr::Language::kId, LanguageTag::kId}, + {valhalla::baldr::Language::kIs, LanguageTag::kIs}, + {valhalla::baldr::Language::kIt, LanguageTag::kIt}, + {valhalla::baldr::Language::kJa, LanguageTag::kJa}, + {valhalla::baldr::Language::kKa, LanguageTag::kKa}, + {valhalla::baldr::Language::kKl, LanguageTag::kKl}, + {valhalla::baldr::Language::kKm, LanguageTag::kKm}, + {valhalla::baldr::Language::kKo, LanguageTag::kKo}, + {valhalla::baldr::Language::kLo, LanguageTag::kLo}, + {valhalla::baldr::Language::kLt, LanguageTag::kLt}, + {valhalla::baldr::Language::kLv, LanguageTag::kLv}, + {valhalla::baldr::Language::kMg, LanguageTag::kMg}, + {valhalla::baldr::Language::kMk, LanguageTag::kMk}, + {valhalla::baldr::Language::kMn, LanguageTag::kMn}, + {valhalla::baldr::Language::kMo, LanguageTag::kMo}, + {valhalla::baldr::Language::kMt, LanguageTag::kMt}, + {valhalla::baldr::Language::kMy, LanguageTag::kMy}, + {valhalla::baldr::Language::kNe, LanguageTag::kNe}, + {valhalla::baldr::Language::kNl, LanguageTag::kNl}, + {valhalla::baldr::Language::kNo, LanguageTag::kNo}, + {valhalla::baldr::Language::kOc, LanguageTag::kOc}, + {valhalla::baldr::Language::kPap, LanguageTag::kPap}, + {valhalla::baldr::Language::kPl, LanguageTag::kPl}, + {valhalla::baldr::Language::kPs, LanguageTag::kPs}, + {valhalla::baldr::Language::kPt, LanguageTag::kPt}, + {valhalla::baldr::Language::kRm, LanguageTag::kRm}, + {valhalla::baldr::Language::kRo, LanguageTag::kRo}, + {valhalla::baldr::Language::kRu, LanguageTag::kRu}, + {valhalla::baldr::Language::kSk, LanguageTag::kSk}, + {valhalla::baldr::Language::kSl, LanguageTag::kSl}, + {valhalla::baldr::Language::kSq, LanguageTag::kSq}, + {valhalla::baldr::Language::kSr, LanguageTag::kSr}, + {valhalla::baldr::Language::kSrLatn, LanguageTag::kSrLatn}, + {valhalla::baldr::Language::kSv, LanguageTag::kSv}, + {valhalla::baldr::Language::kTg, LanguageTag::kTg}, + {valhalla::baldr::Language::kTh, LanguageTag::kTh}, + {valhalla::baldr::Language::kTk, LanguageTag::kTk}, + {valhalla::baldr::Language::kTr, LanguageTag::kTr}, + {valhalla::baldr::Language::kUk, LanguageTag::kUk}, + {valhalla::baldr::Language::kUr, LanguageTag::kUr}, + {valhalla::baldr::Language::kUz, LanguageTag::kUz}, + {valhalla::baldr::Language::kVi, LanguageTag::kVi}, + {valhalla::baldr::Language::kZh, LanguageTag::kZh}, + {valhalla::baldr::Language::kCy, LanguageTag::kCy}, + {valhalla::baldr::Language::kNone, LanguageTag::kUnspecified}}; + + auto i = language_tag_map.find(l); + if (i == language_tag_map.cend()) { + return LanguageTag::kUnspecified; + } + return i->second; +} + +inline LanguageTag GetTripLanguageTag(const std::string& str_language) { + static const std::unordered_map str_language_tag_map = + {{"ab", LanguageTag::kAb}, {"am", LanguageTag::kAm}, {"ar", LanguageTag::kAr}, + {"az", LanguageTag::kAz}, {"be", LanguageTag::kBe}, {"bg", LanguageTag::kBg}, + {"bn", LanguageTag::kBn}, {"bs", LanguageTag::kBs}, {"ca", LanguageTag::kCa}, + {"ckb", LanguageTag::kCkb}, {"cs", LanguageTag::kCs}, {"da", LanguageTag::kDa}, + {"de", LanguageTag::kDe}, {"dv", LanguageTag::kDv}, {"dz", LanguageTag::kDz}, + {"el", LanguageTag::kEl}, {"en", LanguageTag::kEn}, {"es", LanguageTag::kEs}, + {"et", LanguageTag::kEt}, {"fa", LanguageTag::kFa}, {"fi", LanguageTag::kFi}, + {"fr", LanguageTag::kFr}, {"fy", LanguageTag::kFy}, {"gl", LanguageTag::kGl}, + {"he", LanguageTag::kHe}, {"hr", LanguageTag::kHr}, {"hu", LanguageTag::kHu}, + {"hy", LanguageTag::kHy}, {"id", LanguageTag::kId}, {"is", LanguageTag::kIs}, + {"it", LanguageTag::kIt}, {"ja", LanguageTag::kJa}, {"ka", LanguageTag::kKa}, + {"kl", LanguageTag::kKl}, {"km", LanguageTag::kKm}, {"ko", LanguageTag::kKo}, + {"lo", LanguageTag::kLo}, {"lt", LanguageTag::kLt}, {"lv", LanguageTag::kLv}, + {"mg", LanguageTag::kMg}, {"mk", LanguageTag::kMk}, {"mn", LanguageTag::kMn}, + {"mo", LanguageTag::kMo}, {"mt", LanguageTag::kMt}, {"my", LanguageTag::kMy}, + {"ne", LanguageTag::kNe}, {"nl", LanguageTag::kNl}, {"no", LanguageTag::kNo}, + {"oc", LanguageTag::kOc}, {"pap", LanguageTag::kPap}, {"pl", LanguageTag::kPl}, + {"ps", LanguageTag::kPs}, {"pt", LanguageTag::kPt}, {"rm", LanguageTag::kRm}, + {"ro", LanguageTag::kRo}, {"ru", LanguageTag::kRu}, {"sk", LanguageTag::kSk}, + {"sl", LanguageTag::kSl}, {"sq", LanguageTag::kSq}, {"sr", LanguageTag::kSr}, + {"sr-latn", LanguageTag::kSrLatn}, {"sv", LanguageTag::kSv}, {"tg", LanguageTag::kTg}, + {"th", LanguageTag::kTh}, {"tk", LanguageTag::kTk}, {"tr", LanguageTag::kTr}, + {"uk", LanguageTag::kUk}, {"ur", LanguageTag::kUr}, {"uz", LanguageTag::kUz}, + {"vi", LanguageTag::kVi}, {"zh", LanguageTag::kZh}, {"cy", LanguageTag::kCy}}; + + auto i = str_language_tag_map.find(str_language); + if (i == str_language_tag_map.cend()) { + return LanguageTag::kUnspecified; + } + return i->second; +} + +inline std::string to_string(LanguageTag lang_tag) { + switch (lang_tag) { + case LanguageTag::kAb: + return "ab"; + case LanguageTag::kAm: + return "am"; + case LanguageTag::kAr: + return "ar"; + case LanguageTag::kAz: + return "az"; + case LanguageTag::kBe: + return "be"; + case LanguageTag::kBg: + return "bg"; + case LanguageTag::kBn: + return "bn"; + case LanguageTag::kBs: + return "bs"; + case LanguageTag::kCa: + return "ca"; + case LanguageTag::kCkb: + return "ckb"; + case LanguageTag::kCs: + return "cs"; + case LanguageTag::kDa: + return "da"; + case LanguageTag::kDe: + return "de"; + case LanguageTag::kDv: + return "dv"; + case LanguageTag::kDz: + return "dz"; + case LanguageTag::kEl: + return "el"; + case LanguageTag::kEn: + return "en"; + case LanguageTag::kEs: + return "es"; + case LanguageTag::kEt: + return "et"; + case LanguageTag::kFa: + return "fa"; + case LanguageTag::kFi: + return "fi"; + case LanguageTag::kFr: + return "fr"; + case LanguageTag::kFy: + return "fy"; + case LanguageTag::kGl: + return "gl"; + case LanguageTag::kHe: + return "he"; + case LanguageTag::kHr: + return "hr"; + case LanguageTag::kHu: + return "hu"; + case LanguageTag::kHy: + return "hy"; + case LanguageTag::kId: + return "id"; + case LanguageTag::kIs: + return "is"; + case LanguageTag::kIt: + return "it"; + case LanguageTag::kJa: + return "ja"; + case LanguageTag::kKa: + return "ka"; + case LanguageTag::kKl: + return "kl"; + case LanguageTag::kKm: + return "km"; + case LanguageTag::kKo: + return "ko"; + case LanguageTag::kLo: + return "lo"; + case LanguageTag::kLt: + return "lt"; + case LanguageTag::kLv: + return "lv"; + case LanguageTag::kMg: + return "mg"; + case LanguageTag::kMk: + return "mk"; + case LanguageTag::kMn: + return "mn"; + case LanguageTag::kMo: + return "mo"; + case LanguageTag::kMt: + return "mt"; + case LanguageTag::kMy: + return "my"; + case LanguageTag::kNe: + return "ne"; + case LanguageTag::kNl: + return "nl"; + case LanguageTag::kNo: + return "no"; + case LanguageTag::kOc: + return "oc"; + case LanguageTag::kPap: + return "pap"; + case LanguageTag::kPl: + return "pl"; + case LanguageTag::kPs: + return "ps"; + case LanguageTag::kPt: + return "pt"; + case LanguageTag::kRm: + return "rm"; + case LanguageTag::kRo: + return "ro"; + case LanguageTag::kRu: + return "ru"; + case LanguageTag::kSk: + return "sk"; + case LanguageTag::kSl: + return "sl"; + case LanguageTag::kSq: + return "sq"; + case LanguageTag::kSr: + return "sr"; + case LanguageTag::kSrLatn: + return "sr-Latn"; + case LanguageTag::kSv: + return "sv"; + case LanguageTag::kTg: + return "tg"; + case LanguageTag::kTh: + return "th"; + case LanguageTag::kTk: + return "tk"; + case LanguageTag::kTr: + return "tr"; + case LanguageTag::kUk: + return "uk"; + case LanguageTag::kUr: + return "ur"; + case LanguageTag::kUz: + return "uz"; + case LanguageTag::kVi: + return "vi"; + case LanguageTag::kZh: + return "zh"; + case LanguageTag::kCy: + return "cy"; + case LanguageTag::kUnspecified: + return "unspecified"; + default: + // should never come here + return "unknown"; + } } // Associate cycle lane values to TripLeg proto @@ -246,6 +514,8 @@ inline TripLeg_Use GetTripLegUse(const baldr::Use use) { } } +// matrix algo to string +const std::string& MatrixAlgoToString(const valhalla::Matrix::Algorithm algo); // Get the string representing the incident-type std::string incidentTypeToString(const valhalla::IncidentsTile::Metadata::Type& incident_type); // Get the string representing the incident-Impact @@ -276,6 +546,7 @@ const std::string& Location_Type_Enum_Name(const Location::Type t); const std::string& Location_SideOfStreet_Enum_Name(const Location::SideOfStreet s); bool Options_ExpansionProperties_Enum_Parse(const std::string& prop, Options::ExpansionProperties* a); bool Options_ExpansionAction_Enum_Parse(const std::string& action, Options::Action* a); +const std::string& Expansion_EdgeStatus_Enum_Name(const Expansion_EdgeStatus status); std::pair travel_mode_type(const valhalla::DirectionsLeg_Maneuver& maneuver); diff --git a/valhalla/sif/autocost.h b/valhalla/sif/autocost.h index 7654520d13..be0095284a 100644 --- a/valhalla/sif/autocost.h +++ b/valhalla/sif/autocost.h @@ -1,9 +1,6 @@ #ifndef VALHALLA_SIF_AUTOCOST_H_ #define VALHALLA_SIF_AUTOCOST_H_ -#include - -#include #include #include #include diff --git a/valhalla/sif/bicyclecost.h b/valhalla/sif/bicyclecost.h index 1448a49c98..f4c4496aa9 100644 --- a/valhalla/sif/bicyclecost.h +++ b/valhalla/sif/bicyclecost.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_SIF_BICYCLECOST_H_ #define VALHALLA_SIF_BICYCLECOST_H_ -#include #include #include #include diff --git a/valhalla/sif/costconstants.h b/valhalla/sif/costconstants.h index 8870952b5e..0251d8e941 100644 --- a/valhalla/sif/costconstants.h +++ b/valhalla/sif/costconstants.h @@ -28,13 +28,13 @@ enum class VehicleType : uint8_t { kCar = 0, kMotorcycle = 1, kBus = 2, - kTractorTrailer = 3, + kTruck = 3, kMotorScooter = 4, kFourWheelDrive = 5 }; // Pedestrian travel type -enum class PedestrianType : uint8_t { kFoot = 0, kWheelchair = 1, kSegway = 2 }; +enum class PedestrianType : uint8_t { kFoot = 0, kWheelchair = 1, kBlind = 2 }; // Bicycle travel type enum class BicycleType : uint8_t { diff --git a/valhalla/sif/costfactory.h b/valhalla/sif/costfactory.h index 3702d78edd..fca04df4a2 100644 --- a/valhalla/sif/costfactory.h +++ b/valhalla/sif/costfactory.h @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -43,6 +42,7 @@ class CostFactory { Register(Costing::pedestrian, CreatePedestrianCost); Register(Costing::truck, CreateTruckCost); Register(Costing::transit, CreateTransitCost); + Register(Costing::multimodal, CreateNoCost); // dummy so it behaves like the rest Register(Costing::none_, CreateNoCost); Register(Costing::bikeshare, CreateBikeShareCost); } @@ -100,21 +100,24 @@ class CostFactory { mode_costing_t CreateModeCosting(const Options& options, TravelMode& mode) { mode_costing_t mode_costing; - // Set travel mode and construct costing + mode = TravelMode::kMaxTravelMode; + // Set travel mode and construct costing(s) for this type + for (const auto& costing : kCostingTypeMapping.at(options.costing_type())) { + valhalla::sif::cost_ptr_t cost = Create(options.costings().find(costing)->second); + mode = cost->travel_mode(); + mode_costing[static_cast(mode)] = cost; + } if (options.costing_type() == Costing::multimodal || options.costing_type() == Costing::transit || options.costing_type() == Costing::bikeshare) { - // For multi-modal we construct costing for all modes and set the - // initial mode to pedestrian. (TODO - allow other initial modes) - mode_costing[0] = Create(options.costings().find(Costing::auto_)->second); - mode_costing[1] = Create(options.costings().find(Costing::pedestrian)->second); - mode_costing[2] = Create(options.costings().find(Costing::bicycle)->second); - mode_costing[3] = Create(options.costings().find(Costing::transit)->second); + // For multi-modal we set the initial mode to pedestrian. (TODO - allow other initial modes) mode = valhalla::sif::TravelMode::kPedestrian; - } else { - valhalla::sif::cost_ptr_t cost = Create(options); - mode = cost->travel_mode(); - mode_costing[static_cast(mode)] = cost; } + // this should never happen + if (mode == TravelMode::kMaxTravelMode) { + throw std::runtime_error("sif::CostFactory couldn't find a valid TravelMode for " + + Costing_Enum_Name(options.costing_type())); + } + return mode_costing; } diff --git a/valhalla/sif/dynamiccost.h b/valhalla/sif/dynamiccost.h index d93fb1ce87..bf89d9a3de 100644 --- a/valhalla/sif/dynamiccost.h +++ b/valhalla/sif/dynamiccost.h @@ -87,11 +87,46 @@ : def)); \ } +/** + * same as above, but for costing options without pbf's awful oneof + * + * @param costing_options pointer to protobuf costing options object + * @param def the default value which is used when neither json nor pbf is provided + * @param json rapidjson value object which should contain user provided costing options + * @param json_key the json key to use to pull a user provided value out of the json + * @param option_name the name of the option will be set on the costing options object + */ + +#define JSON_PBF_DEFAULT_V2(costing_options, def, json, json_key, option_name) \ + { \ + costing_options->set_##option_name( \ + rapidjson::get::type>::type>(json, json_key, \ + costing_options->option_name() \ + ? costing_options->option_name() \ + : def)); \ + } + using namespace valhalla::midgard; namespace valhalla { namespace sif { +const std::unordered_map> kCostingTypeMapping{ + {Costing::none_, {Costing::none_}}, + {Costing::bicycle, {Costing::bicycle}}, + {Costing::bus, {Costing::bus}}, + {Costing::motor_scooter, {Costing::motor_scooter}}, + {Costing::multimodal, {Costing::multimodal, Costing::transit, Costing::pedestrian}}, + {Costing::pedestrian, {Costing::pedestrian}}, + {Costing::transit, {Costing::transit, Costing::pedestrian}}, + {Costing::truck, {Costing::truck}}, + {Costing::motorcycle, {Costing::motorcycle}}, + {Costing::taxi, {Costing::taxi}}, + {Costing::auto_, {Costing::auto_}}, + {Costing::bikeshare, {Costing::bikeshare, Costing::pedestrian, Costing::bicycle}}, +}; + const sif::Cost kNoCost(0.0f, 0.0f); // Default unit size (seconds) for cost sorting. @@ -412,6 +447,9 @@ class DynamicCost { thor::EdgeStatus* edgestatus = nullptr, const uint64_t current_time = 0, const uint32_t tz_index = 0) const { + if (ignore_turn_restrictions_) + return false; + // Lambda to get the next predecessor EdgeLabel (that is not a transition) auto next_predecessor = [&edge_labels](const EdgeLabel* label) { // Get the next predecessor - make sure it is valid. Continue to get @@ -425,7 +463,7 @@ class DynamicCost { // A complex restriction spans multiple edges, e.g. from A to C via B. // // At the point of triggering a complex restriction, all edges leading up to C - // hav already been evaluated. I.e. B is now marked as kPermanent since + // have already been evaluated. I.e. B is now marked as kPermanent since // we didn't know at the time of B's evaluation that A to B would eventually // form a restricted path // @@ -459,10 +497,10 @@ class DynamicCost { // If forward, check if the edge marks the end of a restriction, else check // if the edge marks the start of a complex restriction. - if ((forward && (edge->end_restriction() & access_mode())) || - (!forward && (edge->start_restriction() & access_mode()))) { + if ((forward && (edge->end_restriction() & access_mask_)) || + (!forward && (edge->start_restriction() & access_mask_))) { // Get complex restrictions. Return false if no restrictions are found - auto restrictions = tile->GetRestrictions(forward, edgeid, access_mode()); + auto restrictions = tile->GetRestrictions(forward, edgeid, access_mask_); if (restrictions.size() == 0) { return false; } @@ -525,7 +563,7 @@ class DynamicCost { } continue; } - // TODO: If a user runs a non-time dependent route, we need to provide Manuever Notes for + // TODO: If a user runs a non-time dependent route, we need to provide Maneuver Notes for // the timed restriction. else if (!current_time && cr->has_dt()) { return false; @@ -561,6 +599,18 @@ class DynamicCost { baldr::DateTime::get_tz_db().from_index(tz_index)); } + /*** + * Evaluates mode-specific and time-dependent access restrictions, including a binary + * search to get the tile's access restrictions. + * + * @param access_mode The access mode to get restrictions for + * @param edge The edge to check for restrictions + * @param is_dest Is there a destination on the edge? + * @param tile The edge's tile + * @param current_time Needed for time dependent restrictions + * @param tz_index The current timezone index + * @param restriction_idx Records the restriction in the tile for later retrieval + */ inline bool EvaluateRestrictions(uint32_t access_mode, const baldr::DirectedEdge* edge, const bool is_dest, @@ -581,9 +631,10 @@ class DynamicCost { const auto& restriction = restrictions[i]; // Compare the time to the time-based restrictions baldr::AccessType access_type = restriction.type(); - if (access_type == baldr::AccessType::kTimedAllowed || - access_type == baldr::AccessType::kTimedDenied || - access_type == baldr::AccessType::kDestinationAllowed) { + if (!ignore_non_vehicular_restrictions_ && + (access_type == baldr::AccessType::kTimedAllowed || + access_type == baldr::AccessType::kTimedDenied || + access_type == baldr::AccessType::kDestinationAllowed)) { // TODO: if(i > baldr::kInvalidRestriction) LOG_ERROR("restriction index overflow"); restriction_idx = static_cast(i); @@ -770,6 +821,12 @@ class DynamicCost { */ virtual uint8_t travel_type() const; + /** + * Is the current vehicle type HGV? + * @return Returns whether it's a truck. + */ + virtual bool is_hgv() const; + /** * Get the wheelchair required flag. * @return Returns true if wheelchair is required. @@ -882,7 +939,7 @@ class DynamicCost { const baldr::TimeInfo& time_info, uint8_t flow_sources, float edge_speed) const { - // TODO: speed_penality hasn't been extensively tested, might alter this in future + // TODO: speed_penalty hasn't been extensively tested, might alter this in future float average_edge_speed = edge_speed; // dont use current speed layer for penalties as live speeds might be too low/too high // better to use layers with smoothed/constant speeds @@ -960,7 +1017,8 @@ class DynamicCost { // Penalties that all costing methods support float maneuver_penalty_; // Penalty (seconds) when inconsistent names float alley_penalty_; // Penalty (seconds) to use a alley - float destination_only_penalty_; // Penalty (seconds) using private road, driveway, or parking aisle + float destination_only_penalty_; // Penalty (seconds) using private road, driveway, parking aisle or + // destination only road float living_street_penalty_; // Penalty (seconds) to use a living street float track_penalty_; // Penalty (seconds) to use tracks float service_penalty_; // Penalty (seconds) to use a generic service road @@ -976,6 +1034,10 @@ class DynamicCost { bool shortest_; bool ignore_restrictions_{false}; + bool ignore_non_vehicular_restrictions_{false}; + // not a requestion parameter, it's true if either ignore_restrictions_ or + // ignore_non_vehicular_restrictions_ is true + bool ignore_turn_restrictions_{false}; bool ignore_oneways_{false}; bool ignore_access_{false}; bool ignore_closures_{false}; @@ -1131,7 +1193,8 @@ class DynamicCost { (edge->use() == baldr::Use::kRailFerry && pred->use() != baldr::Use::kRailFerry); // Additional penalties without any time cost - c.cost += destination_only_penalty_ * (edge->destonly() && !pred->destonly()); + c.cost += destination_only_penalty_ * + ((is_hgv() ? edge->destonly_hgv() : edge->destonly()) && !pred->destonly()); c.cost += alley_penalty_ * (edge->use() == baldr::Use::kAlley && pred->use() != baldr::Use::kAlley); c.cost += maneuver_penalty_ * (!edge->link() && !edge->name_consistency(idx)); @@ -1217,7 +1280,7 @@ void ParseBaseCostOptions(const rapidjson::Value& json, const BaseCostingOptionsConfig& cfg); /** - * Parses all the costing options for all supported costings + * Parses all the costing options for all needed costings * @param doc json document * @param costing_options_key the key in the json document where the options are located * @param options where to store the parsed costing diff --git a/valhalla/sif/edgelabel.h b/valhalla/sif/edgelabel.h index 7281f17ad5..cf37903dad 100644 --- a/valhalla/sif/edgelabel.h +++ b/valhalla/sif/edgelabel.h @@ -2,7 +2,6 @@ #define VALHALLA_SIF_EDGELABEL_H_ #include -#include #include #include #include @@ -18,13 +17,15 @@ constexpr uint32_t kInitialEdgeLabelCountDijkstras = 4000000; constexpr uint32_t kInitialEdgeLabelCountBidirDijkstra = 2000000; /** - * Labeling information for shortest path algorithm. Contains cost, - * predecessor, current time, and assorted information required during - * construction of the shortest path and for reconstructing the path - * upon completion. + * Labeling information for shortest path and graph expansion algorithms. + * Contains cost, predecessor, path distance, and assorted information + * required during construction of the shortest path and for reconstructing + * the path upon completion. * The base EdgeLabel class contains all necessary information for costing - * and for an A* (forward search) algorithm. Derived classes support - * additional information required other path algorithms. + * computations and for use with the double bucket priority queue. The base + * class is used in map-matching (routing part) and the time-distance matrix + * algorithms. Derived classes support additional information required by other + * path algorithms. */ class EdgeLabel { public: @@ -36,9 +37,8 @@ class EdgeLabel { edgeid_(baldr::kInvalidGraphId), opp_index_(0), opp_local_idx_(0), mode_(0), endnode_(baldr::kInvalidGraphId), use_(0), classification_(0), shortcut_(0), dest_only_(0), origin_(0), destination_(0), toll_(0), not_thru_(0), deadend_(0), on_complex_rest_(0), - closure_pruning_(0), has_measured_speed_(0), path_id_(0), restriction_idx_(0), - internal_turn_(0), unpaved_(0), cost_(0, 0), sortcost_(0), distance_(0), - transition_cost_(0, 0) { + closure_pruning_(0), path_id_(0), restriction_idx_(0), internal_turn_(0), unpaved_(0), + has_measured_speed_(0), hgv_access_(0), cost_(0, 0), sortcost_(0) { assert(path_id_ <= baldr::kMaxMultiPathId); } @@ -50,10 +50,8 @@ class EdgeLabel { * @param edge Directed edge. * @param cost True cost (cost and time in seconds) to the edge. * @param sortcost Cost for sorting (includes A* heuristic) - * @param dist Distance to the destination (meters) * @param mode Mode of travel along this edge. * @param path_distance Accumulated path distance - * @param transition_cost Transition cost * @param restriction_idx If this label has restrictions, the index where the restriction is * found * @param closure_pruning Should closure pruning be enabled on this path? @@ -61,58 +59,40 @@ class EdgeLabel { * @param internal_turn Did we make an turn on a short internal edge. * @param path_id When searching more than one path at a time this denotes which path * the this label is tracking + * @param destonly Destination only, either mode-specific or general + * @param hgv_access Whether HGV is allowed */ EdgeLabel(const uint32_t predecessor, const baldr::GraphId& edgeid, const baldr::DirectedEdge* edge, const Cost& cost, const float sortcost, - const float dist, const TravelMode mode, const uint32_t path_distance, - const Cost& transition_cost, const uint8_t restriction_idx, const bool closure_pruning, const bool has_measured_speed, const InternalTurn internal_turn, - const uint8_t path_id = 0) + const uint8_t path_id = 0, + const bool destonly = false, + const bool hgv_access = false) : predecessor_(predecessor), path_distance_(path_distance), restrictions_(edge->restrictions()), edgeid_(edgeid), opp_index_(edge->opp_index()), opp_local_idx_(edge->opp_local_idx()), mode_(static_cast(mode)), endnode_(edge->endnode()), use_(static_cast(edge->use())), classification_(static_cast(edge->classification())), shortcut_(edge->shortcut()), - dest_only_(edge->destonly()), origin_(0), destination_(0), toll_(edge->toll()), - not_thru_(edge->not_thru()), deadend_(edge->deadend()), + origin_(0), destination_(0), toll_(edge->toll()), not_thru_(edge->not_thru()), + deadend_(edge->deadend()), on_complex_rest_(edge->part_of_complex_restriction() || edge->start_restriction() || edge->end_restriction()), - closure_pruning_(closure_pruning), has_measured_speed_(has_measured_speed), path_id_(path_id), - restriction_idx_(restriction_idx), internal_turn_(static_cast(internal_turn)), - unpaved_(edge->unpaved()), cost_(cost), sortcost_(sortcost), distance_(dist), - transition_cost_(transition_cost) { + closure_pruning_(closure_pruning), path_id_(path_id), restriction_idx_(restriction_idx), + internal_turn_(static_cast(internal_turn)), unpaved_(edge->unpaved()), + has_measured_speed_(has_measured_speed), hgv_access_(hgv_access), cost_(cost), + sortcost_(sortcost) { + dest_only_ = destonly ? destonly : edge->destonly(); assert(path_id_ <= baldr::kMaxMultiPathId); } - /** - * Update an existing edge label with new predecessor and cost information. - * The mode, edge Id, and end node remain the same. - * @param predecessor Predecessor directed edge in the shortest path. - * @param cost True cost (and elapsed time in seconds) to the edge. - * @param sortcost Cost for sorting (includes A* heuristic). - * @param transition_cost Transition cost - * @param restriction_idx Does the edge have time dependent restrictions. - */ - void Update(const uint32_t predecessor, - const Cost& cost, - const float sortcost, - const Cost& transition_cost, - const uint8_t restriction_idx) { - predecessor_ = predecessor; - cost_ = cost; - sortcost_ = sortcost; - transition_cost_ = transition_cost; - restriction_idx_ = restriction_idx; - } - /** * Update an existing edge label with new predecessor and cost information. * Update transit information: prior stop Id will stay the same but trip Id @@ -122,20 +102,17 @@ class EdgeLabel { * @param cost True cost (and elapsed time in seconds) to the edge. * @param sortcost Cost for sorting (includes A* heuristic). * @param path_distance Accumulated path distance. - * @param transition_cost Transition cost * @param restriction_idx If this label has restrictions, the index where the restriction is found */ void Update(const uint32_t predecessor, const Cost& cost, const float sortcost, const uint32_t path_distance, - const Cost& transition_cost, const uint8_t restriction_idx) { predecessor_ = predecessor; cost_ = cost; sortcost_ = sortcost; path_distance_ = path_distance; - transition_cost_ = transition_cost; restriction_idx_ = restriction_idx; } @@ -192,16 +169,6 @@ class EdgeLabel { sortcost_ = sortcost; } - /** - * Get the distance to the destination. - * In case of bidirectional search it may represent the distance to the start point: - * origin for the forward search and destination for the reverse search. - * @return Returns the distance in meters. - */ - float distance() const { - return distance_; - } - /** * Get the use of the directed edge. * @return Returns edge use. @@ -372,15 +339,6 @@ class EdgeLabel { deadend_ = is_deadend; } - /** - * Get the transition cost. This is used in the bidirectional A* - * to determine the cost at the connection. But is also used for general stats - * @return Returns the transition cost (including penalties). - */ - sif::Cost transition_cost() const { - return transition_cost_; - } - /** * Returns the location/path id (index) of the path that this label is tracking. Useful when your * algorithm tracks multiple path expansions at the same time @@ -415,6 +373,14 @@ class EdgeLabel { return unpaved_; } + /** + * Does it have HGV access? + * @return Returns true if the (opposing) edge had HGV access + */ + bool has_hgv_access() const { + return hgv_access_; + } + protected: // predecessor_: Index to the predecessor edge label information. // Note: invalid predecessor value uses all 32 bits (so if this needs to @@ -454,7 +420,6 @@ class EdgeLabel { * deadend_: Flag indicating edge is a dead-end. * on_complex_rest_: Part of a complex restriction. * closure_pruning_: Was closure pruning active on prior edge? - * has_measured_speed_: Do we have any of the measured speed types set? */ uint64_t endnode_ : 46; uint64_t use_ : 6; @@ -468,7 +433,6 @@ class EdgeLabel { uint64_t deadend_ : 1; uint64_t on_complex_rest_ : 1; uint64_t closure_pruning_ : 1; - uint64_t has_measured_speed_ : 1; // path id can be used to track more than one path at the same time in the same labelset // its limited to 7 bits because edgestatus only had 7 and matching made sense to reduce confusion @@ -478,21 +442,100 @@ class EdgeLabel { uint32_t internal_turn_ : 2; // Flag indicating edge is an unpaved road. uint32_t unpaved_ : 1; - uint32_t spare : 13; + uint32_t has_measured_speed_ : 1; + // Flag if this edge had HGV access + uint32_t hgv_access_ : 1; + uint32_t spare : 12; Cost cost_; // Cost and elapsed time along the path. float sortcost_; // Sort cost - includes A* heuristic. - float distance_; // Distance to the destination. +}; - // Was originally used for reverse search path to remove extra time where paths intersected - // but its now used everywhere to measure the difference in time along the edge vs at the node +/** + * Derived label class used for recosting paths within the LabelCallback. + * transition_cost is added to the label for use when recosting a path. + */ +class PathEdgeLabel : public EdgeLabel { +public: + // Default constructor + PathEdgeLabel() { + } + + /** + * Constructor with values. + * @param predecessor Index into the edge label list for the predecessor + * directed edge in the shortest path. + * @param edgeid Directed edge Id. + * @param edge Directed edge. + * @param cost True cost (cost and time in seconds) to the edge. + * @param sortcost Cost for sorting (includes A* heuristic) + * @param dist Distance to the destination (meters) + * @param mode Mode of travel along this edge. + * @param path_distance Accumulated path distance + * @param transition_cost Transition cost + * @param restriction_idx If this label has restrictions, the index where the restriction is + * found + * @param closure_pruning Should closure pruning be enabled on this path? + * @param has_measured_speed Do we have any of the measured speed types set? + * @param internal_turn Did we make an turn on a short internal edge. + * @param path_id When searching more than one path at a time this denotes which path + * the this label is tracking + * @param destonly Destination only, either mode-specific or general + * @param hgv_access Whether HGV is allowed + */ + PathEdgeLabel(const uint32_t predecessor, + const baldr::GraphId& edgeid, + const baldr::DirectedEdge* edge, + const Cost& cost, + const float sortcost, + const TravelMode mode, + const uint32_t path_distance, + const Cost& transition_cost, + const uint8_t restriction_idx, + const bool closure_pruning, + const bool has_measured_speed, + const InternalTurn internal_turn, + const uint8_t path_id = 0, + const bool destonly = false, + const bool hgv_access = false) + : EdgeLabel(predecessor, + edgeid, + edge, + cost, + sortcost, + mode, + path_distance, + restriction_idx, + closure_pruning, + has_measured_speed, + internal_turn, + path_id, + destonly, + hgv_access), + transition_cost_(transition_cost) { + assert(path_id_ <= baldr::kMaxMultiPathId); + } + + /** + * Get the transition cost. This is used in the bidirectional A* + * to determine the cost at the connection. But is also used for general stats + * @return Returns the transition cost (including penalties). + */ + sif::Cost transition_cost() const { + return transition_cost_; + } + +protected: + // Used to measure the difference in time along the edge vs at the node Cost transition_cost_; }; /** - * EdgeLabel used for bidirectional path algorithms: Bidirectional A* - * and CostMatrix (which does not use a heuristic based on distance - * to the destination). + * Derived EdgeLabel class used for A* path algorithms and CostMatrix. + * NOTE - despite the name, this is also used by unidirectional A* algorithms + * (not simply bidirectional A*). + * transition_cost, distance (from the destination), and opposing edge index + * are included in the derived class. */ class BDEdgeLabel : public EdgeLabel { public: @@ -520,6 +563,8 @@ class BDEdgeLabel : public EdgeLabel { * found * @param path_id When searching more than one path at a time this denotes which path * the this label is tracking + * @param destonly Destination only, either mode-specific or general + * @param hgv_access Whether HGV is allowed */ BDEdgeLabel(const uint32_t predecessor, const baldr::GraphId& edgeid, @@ -535,22 +580,25 @@ class BDEdgeLabel : public EdgeLabel { const bool has_measured_speed, const sif::InternalTurn internal_turn, const uint8_t restriction_idx, - const uint8_t path_id = 0) + const uint8_t path_id = 0, + const bool destonly = false, + const bool hgv_access = false) : EdgeLabel(predecessor, edgeid, edge, cost, sortcost, - dist, mode, 0, - transition_cost, restriction_idx, closure_pruning, has_measured_speed, internal_turn, - path_id), - opp_edgeid_(oppedgeid), not_thru_pruning_(not_thru_pruning) { + path_id, + destonly, + hgv_access), + transition_cost_(transition_cost), opp_edgeid_(oppedgeid), + not_thru_pruning_(not_thru_pruning), distance_(dist) { } /** @@ -573,6 +621,8 @@ class BDEdgeLabel : public EdgeLabel { * found * @param path_id When searching more than one path at a time this denotes which path * the this label is tracking + * @param destonly Destination only, either mode-specific or general + * @param hgv_access Whether HGV is allowed */ BDEdgeLabel(const uint32_t predecessor, const baldr::GraphId& edgeid, @@ -587,22 +637,25 @@ class BDEdgeLabel : public EdgeLabel { const bool has_measured_speed, const sif::InternalTurn internal_turn, const uint8_t restriction_idx, - const uint8_t path_id = 0) + const uint8_t path_id = 0, + const bool destonly = false, + const bool hgv_access = false) : EdgeLabel(predecessor, edgeid, edge, cost, cost.cost, - 0, mode, path_distance, - transition_cost, restriction_idx, closure_pruning, has_measured_speed, internal_turn, - path_id), - opp_edgeid_(oppedgeid), not_thru_pruning_(not_thru_pruning) { + path_id, + destonly, + hgv_access), + transition_cost_(transition_cost), opp_edgeid_(oppedgeid), + not_thru_pruning_(not_thru_pruning), distance_(0.0f) { } /** @@ -622,6 +675,8 @@ class BDEdgeLabel : public EdgeLabel { * @param internal_turn Did we make an turn on a short internal edge. * @param path_id When searching more than one path at a time this denotes which path * the this label is tracking + * @param destonly Destination only, either mode-specific or general + * @param hgv_access Whether HGV is allowed */ BDEdgeLabel(const uint32_t predecessor, const baldr::GraphId& edgeid, @@ -634,22 +689,24 @@ class BDEdgeLabel : public EdgeLabel { const bool closure_pruning, const bool has_measured_speed, const sif::InternalTurn internal_turn, - const uint8_t path_id = 0) + const uint8_t path_id = 0, + const bool destonly = false, + const bool hgv_access = false) : EdgeLabel(predecessor, edgeid, edge, cost, sortcost, - dist, mode, 0, - Cost{}, restriction_idx, closure_pruning, has_measured_speed, internal_turn, - path_id), - not_thru_pruning_(!edge->not_thru()) { + path_id, + destonly, + hgv_access), + transition_cost_({}), not_thru_pruning_(!edge->not_thru()), distance_(dist) { opp_edgeid_ = {}; } @@ -698,6 +755,25 @@ class BDEdgeLabel : public EdgeLabel { restriction_idx_ = restriction_idx; } + /** + * Get the distance to the destination. + * In case of bidirectional search it may represent the distance to the start point: + * origin for the forward search and destination for the reverse search. + * @return Returns the distance in meters. + */ + float distance() const { + return distance_; + } + + /** + * Get the transition cost. This is used in the bidirectional A* + * to determine the cost at the connection. But is also used for general stats + * @return Returns the transition cost (including penalties). + */ + sif::Cost transition_cost() const { + return transition_cost_; + } + /** * Get the GraphId of the opposing directed edge. * @return Returns the GraphId of the opposing directed edge. @@ -714,13 +790,27 @@ class BDEdgeLabel : public EdgeLabel { return not_thru_pruning_; } + /** + * Sets the path distance for this EdgeLabel. + * @param distance Path distance. + */ + void set_path_distance(const float distance) { + path_distance_ = distance; + } + protected: + // Was originally used for reverse search path to remove extra time where paths intersected + // but its now used everywhere to measure the difference in time along the edge vs at the node + Cost transition_cost_; + // Graph Id of the opposing edge. // not_thru_pruning_: Is not thru pruning enabled? uint64_t opp_edgeid_ : 63; // Could be 46 (to provide more spare) // TODO: Move not_thru_prunin to the base class - EdgeLabel so that we can // consolidate all pruning related properties at 1 place uint64_t not_thru_pruning_ : 1; + + float distance_; // Distance to the destination. }; /** @@ -753,6 +843,7 @@ class MMEdgeLabel : public EdgeLabel { * @param restriction_idx If this label has restrictions, the index where the restriction is found * @param path_id When searching more than one path at a time this denotes which path the * this label is tracking + * @param destonly Destination only, either mode-specific or general */ MMEdgeLabel(const uint32_t predecessor, const baldr::GraphId& edgeid, @@ -770,24 +861,24 @@ class MMEdgeLabel : public EdgeLabel { const bool has_transit, const Cost& transition_cost, const uint8_t restriction_idx, - const uint8_t path_id = 0) + const uint8_t path_id = 0, + const bool destonly = false) : EdgeLabel(predecessor, edgeid, edge, cost, sortcost, - dist, mode, path_distance, - transition_cost, restriction_idx, true, false, InternalTurn::kNoTurn, - path_id), - prior_stopid_(prior_stopid), tripid_(tripid), blockid_(blockid), - transit_operator_(transit_operator), has_transit_(has_transit), - walking_distance_(walking_distance) { + path_id, + destonly), + transition_cost_(transition_cost), prior_stopid_(prior_stopid), tripid_(tripid), + blockid_(blockid), transit_operator_(transit_operator), has_transit_(has_transit), + walking_distance_(walking_distance), distance_(dist) { } /** @@ -825,6 +916,25 @@ class MMEdgeLabel : public EdgeLabel { restriction_idx_ = restriction_idx; } + /** + * Get the distance to the destination. + * In case of bidirectional search it may represent the distance to the start point: + * origin for the forward search and destination for the reverse search. + * @return Returns the distance in meters. + */ + float distance() const { + return distance_; + } + + /** + * Get the transition cost. This is used in the bidirectional A* + * to determine the cost at the connection. But is also used for general stats + * @return Returns the transition cost (including penalties). + */ + sif::Cost transition_cost() const { + return transition_cost_; + } + /** * Get the prior transit stop Id. * @return Returns the prior transit stop Id. @@ -873,6 +983,10 @@ class MMEdgeLabel : public EdgeLabel { } protected: + // Was originally used for reverse search path to remove extra time where paths intersected + // but its now used everywhere to measure the difference in time along the edge vs at the node + Cost transition_cost_; + // GraphId of the predecessor transit stop. baldr::GraphId prior_stopid_; @@ -888,6 +1002,8 @@ class MMEdgeLabel : public EdgeLabel { // Accumulated walking distance to prune the expansion uint32_t walking_distance_; + + float distance_; // Distance to the destination. }; } // namespace sif diff --git a/valhalla/sif/hierarchylimits.h b/valhalla/sif/hierarchylimits.h index 006f6683b4..dadbdce640 100644 --- a/valhalla/sif/hierarchylimits.h +++ b/valhalla/sif/hierarchylimits.h @@ -1,7 +1,7 @@ #ifndef VALHALLA_SIF_HIERARCHYLIMITS_H_ #define VALHALLA_SIF_HIERARCHYLIMITS_H_ -#include +#include #include // Default hierarchy transitions. Note that this corresponds to a 3 level @@ -46,8 +46,7 @@ struct HierarchyLimits { // always allowed. Used for A*. /** - * Set hierarchy limits for the specified level using a property tree. - * @param pt Property tree + * Set hierarchy limits for the specified level. * @param level Hierarchy level */ HierarchyLimits(const uint32_t level) : up_transition_count(0) { diff --git a/valhalla/sif/motorcyclecost.h b/valhalla/sif/motorcyclecost.h index c5024e81ce..b555eec1a9 100644 --- a/valhalla/sif/motorcyclecost.h +++ b/valhalla/sif/motorcyclecost.h @@ -1,9 +1,6 @@ #ifndef VALHALLA_SIF_MOTORCYCLECOST_H_ #define VALHALLA_SIF_MOTORCYCLECOST_H_ -#include - -#include #include #include #include diff --git a/valhalla/sif/motorscootercost.h b/valhalla/sif/motorscootercost.h index 5146690e2f..cb68bb7280 100644 --- a/valhalla/sif/motorscootercost.h +++ b/valhalla/sif/motorscootercost.h @@ -1,9 +1,6 @@ #ifndef VALHALLA_SIF_MOTORSCOOTERCOST_H_ #define VALHALLA_SIF_MOTORSCOOTERCOST_H_ -#include - -#include #include #include #include diff --git a/valhalla/sif/nocost.h b/valhalla/sif/nocost.h index 97b033078b..3bb42909a0 100644 --- a/valhalla/sif/nocost.h +++ b/valhalla/sif/nocost.h @@ -1,9 +1,6 @@ #ifndef VALHALLA_SIF_NOCOST_H_ #define VALHALLA_SIF_NOCOST_H_ -#include - -#include #include #include #include diff --git a/valhalla/sif/pedestriancost.h b/valhalla/sif/pedestriancost.h index d036d6a6fb..1563d260ed 100644 --- a/valhalla/sif/pedestriancost.h +++ b/valhalla/sif/pedestriancost.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_SIF_PEDESTRIANCOST_H_ #define VALHALLA_SIF_PEDESTRIANCOST_H_ -#include #include #include #include diff --git a/valhalla/sif/recost.h b/valhalla/sif/recost.h index b47fb14db1..aecae6f420 100644 --- a/valhalla/sif/recost.h +++ b/valhalla/sif/recost.h @@ -12,13 +12,13 @@ namespace sif { // what this function calls to get the next edge using EdgeCallback = std::function; // what this function calls to emit the next label -using LabelCallback = std::function; +using LabelCallback = std::function; /** * Will take a sequence of edges and create the set of edge labels that would represent it * Allows for the caller to essentially re-compute the costing of a given path * - * @param reader used to get access to graph data. modifyable because its got a cache + * @param reader used to get access to graph data. modifiable because its got a cache * @param costing single costing object to be used for costing/access computations * @param edge_cb the callback used to get each edge in the path * @param label_cb the callback used to emit each label in the path diff --git a/valhalla/sif/transitcost.h b/valhalla/sif/transitcost.h index f47f23a2fd..86bb2df6b2 100644 --- a/valhalla/sif/transitcost.h +++ b/valhalla/sif/transitcost.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_SIF_TRANSITCOST_H_ #define VALHALLA_SIF_TRANSITCOST_H_ -#include #include #include #include diff --git a/valhalla/sif/truckcost.h b/valhalla/sif/truckcost.h index e83a0e5984..88be47b2bf 100644 --- a/valhalla/sif/truckcost.h +++ b/valhalla/sif/truckcost.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_SIF_TRUCKCOST_H_ #define VALHALLA_SIF_TRUCKCOST_H_ -#include #include #include #include diff --git a/valhalla/skadi/sample.h b/valhalla/skadi/sample.h index f54449187f..ef88c3e5fa 100644 --- a/valhalla/skadi/sample.h +++ b/valhalla/skadi/sample.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #include diff --git a/valhalla/skadi/util.h b/valhalla/skadi/util.h index 026aefb38e..b2d0adc123 100644 --- a/valhalla/skadi/util.h +++ b/valhalla/skadi/util.h @@ -1,7 +1,6 @@ #ifndef __VALHALLA_UTIL_H__ #define __VALHALLA_UTIL_H__ -#include #include #include diff --git a/valhalla/thor/astar_bss.h b/valhalla/thor/astar_bss.h index 59221a9083..ffe24dd084 100644 --- a/valhalla/thor/astar_bss.h +++ b/valhalla/thor/astar_bss.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #include @@ -71,19 +70,9 @@ class AStarBSSAlgorithm : public PathAlgorithm { */ virtual void Clear() override; - /** - * Set a maximum label count. The path algorithm terminates if this - * is exceeded. - * @param max_count Maximum number of labels to allow. - */ - void set_max_label_count(const uint32_t max_count) { - max_label_count_ = max_count; - } - protected: - uint32_t max_label_count_; // Max label count to allow - sif::TravelMode mode_; // Current travel mode - uint8_t travel_type_; // Current travel type + sif::TravelMode mode_; // Current travel mode + uint8_t travel_type_; // Current travel type // A* heuristic AStarHeuristic pedestrian_astarheuristic_; @@ -94,10 +83,10 @@ class AStarBSSAlgorithm : public PathAlgorithm { std::shared_ptr bicycle_costing_; // Vector of edge labels (requires access by index). - std::vector edgelabels_; + std::vector edgelabels_; // Adjacency list - approximate double bucket sort - baldr::DoubleBucketQueue adjacencylist_; + baldr::DoubleBucketQueue adjacencylist_; // Edge status. Mark edges that are in adjacency list or settled. EdgeStatus pedestrian_edgestatus_; @@ -128,7 +117,7 @@ class AStarBSSAlgorithm : public PathAlgorithm { */ void ExpandForward(baldr::GraphReader& graphreader, const baldr::GraphId& node, - const sif::EdgeLabel& pred, + const sif::BDEdgeLabel& pred, const uint32_t pred_idx, const bool from_transition, const bool from_bss, @@ -150,9 +139,8 @@ class AStarBSSAlgorithm : public PathAlgorithm { * Set the destination edge(s). * @param graphreader Graph tile reader. * @param dest Location information of the destination. - * @return Returns the relative density near the destination (0-15) */ - uint32_t SetDestination(baldr::GraphReader& graphreader, const valhalla::Location& dest); + void SetDestination(baldr::GraphReader& graphreader, const valhalla::Location& dest); /** * Form the path from the adjacency list. Recovers the path from the diff --git a/valhalla/thor/bidirectional_astar.h b/valhalla/thor/bidirectional_astar.h index 25af2ec185..b64761c2cf 100644 --- a/valhalla/thor/bidirectional_astar.h +++ b/valhalla/thor/bidirectional_astar.h @@ -2,10 +2,7 @@ #define VALHALLA_THOR_BIDIRECTIONAL_ASTAR_H_ #include -#include #include -#include -#include #include #include @@ -153,7 +150,7 @@ class BidirectionalAStar : public PathAlgorithm { * @return returns true if the expansion continued from this node */ template - bool Expand(baldr::GraphReader& graphreader, + void Expand(baldr::GraphReader& graphreader, const baldr::GraphId& node, sif::BDEdgeLabel& pred, const uint32_t pred_idx, diff --git a/valhalla/thor/centroid.h b/valhalla/thor/centroid.h index 884397db9f..aba4d55cef 100644 --- a/valhalla/thor/centroid.h +++ b/valhalla/thor/centroid.h @@ -1,9 +1,6 @@ #pragma once #include -#include -#include -#include #include #include @@ -46,7 +43,7 @@ struct PathIntersection { /** * Equality operator for hashed containers to resolve hash collisions - * @param i the other intersection to compare aginst this one + * @param i the other intersection to compare against this one * @return true if i is equal to this intersection */ bool operator==(const PathIntersection& i) const; @@ -148,7 +145,7 @@ class Centroid : public thor::Dijkstras { * Tell the expansion how many labels to expect and how many buckets to use * * @param bucket_count impacts the number of buckets in the double bucket queue - * @param edge_label_reservation an estimate of the total number of edgelabels for this exapansion + * @param edge_label_reservation an estimate of the total number of edgelabels for this expansion */ virtual void GetExpansionHints(uint32_t& bucket_count, uint32_t& edge_label_reservation) const override; diff --git a/valhalla/thor/costmatrix.h b/valhalla/thor/costmatrix.h index 4d1667f5e4..e0a2e41544 100644 --- a/valhalla/thor/costmatrix.h +++ b/valhalla/thor/costmatrix.h @@ -2,25 +2,29 @@ #define VALHALLA_THOR_COSTMATRIX_H_ #include -#include #include #include -#include #include #include #include #include #include +#include #include #include +#include #include -#include +#include #include namespace valhalla { namespace thor { +enum class MatrixExpansionType { reverse = 0, forward = 1 }; +constexpr bool MATRIX_FORW = static_cast(MatrixExpansionType::forward); +constexpr bool MATRIX_REV = static_cast(MatrixExpansionType::reverse); + // These cost thresholds are in addition to the distance thresholds. If either forward or reverse // costs exceed the threshold the search is terminated. constexpr float kCostThresholdAutoDivisor = @@ -37,7 +41,7 @@ constexpr float kCostThresholdPedestrianDivisor = */ struct LocationStatus { int threshold; - std::set remaining_locations; + std::set unfound_connections; LocationStatus(const int t) : threshold(t) { } @@ -53,13 +57,14 @@ struct BestCandidate { baldr::GraphId opp_edgeid; sif::Cost cost; uint32_t distance; - uint32_t threshold; + uint32_t max_iterations; BestCandidate(const baldr::GraphId& e1, baldr::GraphId& e2, const sif::Cost& c, const uint32_t d) - : found(false), edgeid(e1), opp_edgeid(e2), cost(c), distance(d), threshold(0) { + : found(false), edgeid(e1), opp_edgeid(e2), cost(c), distance(d), max_iterations(0) { } - void Update(const baldr::GraphId& e1, baldr::GraphId& e2, const sif::Cost& c, const uint32_t d) { + void + Update(const baldr::GraphId& e1, const baldr::GraphId& e2, const sif::Cost& c, const uint32_t d) { edgeid = e1; opp_edgeid = e2; cost = c; @@ -74,7 +79,7 @@ struct BestCandidate { * Shortest Paths". * https://i11www.iti.uni-karlsruhe.de/_media/teaching/theses/files/da-sknopp-06.pdf */ -class CostMatrix { +class CostMatrix : public MatrixAlgorithm { public: /** * Default constructor. Most internal values are set when a query is made so @@ -87,31 +92,37 @@ class CostMatrix { /** * Forms a time distance matrix from the set of source locations * to the set of target locations. - * @param source_location_list List of source/origin locations. - * @param target_location_list List of target/destination locations. - * @param graphreader Graph reader for accessing routing graph. - * @param mode_costing Costing methods. - * @param mode Travel mode to use. + * @param request the full request + * @param graphreader List of source/origin locations. + * @param mode_costing List of target/destination locations. + * @param mode Graph reader for accessing routing graph. * @param max_matrix_distance Maximum arc-length distance for current mode. - * @return time/distance from origin index to all other locations */ - std::vector - SourceToTarget(google::protobuf::RepeatedPtrField& source_location_list, - google::protobuf::RepeatedPtrField& target_location_list, - baldr::GraphReader& graphreader, - const sif::mode_costing_t& mode_costing, - const sif::travel_mode_t mode, - const float max_matrix_distance, - const bool has_time = false, - const bool invariant = false); + bool SourceToTarget(Api& request, + baldr::GraphReader& graphreader, + const sif::mode_costing_t& mode_costing, + const sif::travel_mode_t mode, + const float max_matrix_distance) override; /** * Clear the temporary information generated during time+distance * matrix construction. */ - void clear(); + void Clear() override; + + /** + * Get the algorithm's name + * @return the name of the algorithm + */ + inline const std::string& name() override { + return MatrixAlgoToString(Matrix::CostMatrix); + } protected: + uint32_t max_reserved_labels_count_; + uint32_t max_reserved_locations_count_; + bool check_reverse_connections_; + // Access mode used by the costing method uint32_t access_mode_; @@ -121,48 +132,35 @@ class CostMatrix { // Current costing mode std::shared_ptr costing_; - uint32_t max_reserved_labels_count_; - + // TODO(nils): instead of these array based structures, rather do this: + // https://github.com/valhalla/valhalla/pull/4372#discussion_r1402163444 // Number of source and target locations that can be expanded - uint32_t source_count_; - uint32_t remaining_sources_; - uint32_t target_count_; - uint32_t remaining_targets_; + std::array locs_count_; + std::array locs_remaining_; - // The cost threshold being used for the currently executing query - float current_cost_threshold_; + // The path distance threshold being used for the currently executing query + float current_pathdist_threshold_; // Status - std::vector source_status_; - std::vector target_status_; - - // Adjacency lists, EdgeLabels, EdgeStatus, and hierarchy limits for each - // source location (forward traversal) - std::vector> source_hierarchy_limits_; - std::vector> source_adjacency_; - std::vector> source_edgelabel_; - std::vector source_edgestatus_; - - // Adjacency lists, EdgeLabels, EdgeStatus, and hierarchy limits for each - // target location (reverse traversal) - std::vector> target_hierarchy_limits_; - std::vector> target_adjacency_; - std::vector> target_edgelabel_; - std::vector target_edgestatus_; + std::array, 2> locs_status_; + + // Adjacency lists, EdgeLabels, EdgeStatus, and hierarchy limits for each location + std::array>, 2> hierarchy_limits_; + std::array>, 2> adjacency_; + std::array>, 2> edgelabel_; + std::array, 2> edgestatus_; + + // A* heuristics for both trees and each location + std::array, 2> astar_heuristics_; // List of best connections found so far std::vector best_connection_; + bool ignore_hierarchy_limits_; + // when doing timezone differencing a timezone cache speeds up the computation baldr::DateTime::tz_sys_info_cache_t tz_cache_; - /** - * Get the cost threshold based on the current mode and the max arc-length distance - * for that mode. - * @param max_matrix_distance Maximum arc-length distance for current mode. - */ - float GetCostThreshold(const float max_matrix_distance); - /** * Form the initial time distance matrix given the sources * and destinations. @@ -170,13 +168,16 @@ class CostMatrix { * @param target_location_list List of target/destination locations. */ void Initialize(const google::protobuf::RepeatedPtrField& source_location_list, - const google::protobuf::RepeatedPtrField& target_location_list); + const google::protobuf::RepeatedPtrField& target_location_list, + const valhalla::Matrix& matrix); /** * Iterate the forward search from the source/origin location. * @param index Index of the source location. * @param n Iteration counter. * @param graphreader Graph reader for accessing routing graph. + * @param time_info The origin's timeinfo object + * @param invariant Whether time should be treated as invariant */ void ForwardSearch(const uint32_t index, const uint32_t n, @@ -190,8 +191,53 @@ class CostMatrix { * @param source Source index. * @param pred Edge label of the predecessor. * @param n Iteration counter. + * @param graphreader the graph reader instance + * @param options the request options to check for the position along origin and destination + * edges + */ + void CheckForwardConnections(const uint32_t source, + const sif::BDEdgeLabel& pred, + const uint32_t n, + baldr::GraphReader& graphreader, + const valhalla::Options& options); + + template + bool Expand(const uint32_t index, + const uint32_t n, + baldr::GraphReader& graphreader, + const valhalla::Options& options, + const baldr::TimeInfo& time_info = baldr::TimeInfo::invalid(), + const bool invariant = false); + + template + bool ExpandInner(baldr::GraphReader& graphreader, + const uint32_t index, + const sif::BDEdgeLabel& pred, + const baldr::DirectedEdge* opp_pred_edge, + const baldr::NodeInfo* nodeinfo, + const uint32_t pred_idx, + const EdgeMetadata& meta, + uint32_t& shortcuts, + const graph_tile_ptr& tile, + const baldr::TimeInfo& time_info); + + /** + * Check if the edge on the backward search connects to a reached edge + * on the reverse search tree. + * @param target target index. + * @param pred Edge label of the predecessor. + * @param n Iteration counter. + * @param graphreader the graph reader instance + * @param options the request options to check for the position along origin and destination + * edges */ - void CheckForwardConnections(const uint32_t source, const sif::BDEdgeLabel& pred, const uint32_t n); + void CheckReverseConnections(const uint32_t target, + const sif::BDEdgeLabel& pred, + const uint32_t n, + baldr::GraphReader& graphreader, + const valhalla::Options& options); /** * Update status when a connection is found. @@ -204,8 +250,9 @@ class CostMatrix { * Iterate the backward search from the target/destination location. * @param index Index of the target location. * @param graphreader Graph reader for accessing routing graph. + * @param n Iteration counter. */ - void BackwardSearch(const uint32_t index, baldr::GraphReader& graphreader); + void BackwardSearch(const uint32_t index, baldr::GraphReader& graphreader, const uint32_t n); /** * Sets the source/origin locations. Search expands forward from these @@ -247,18 +294,24 @@ class CostMatrix { /** * If time awareness was requested for the CostMatrix algorithm, we need * to form the paths the sources & targets generated, and recost them to - * update the best connections, before returning the result. + * update the best connections, before returning the result. Optionally + * returns the path's shape. * @param graphreader Graph tile reader * @param origins The source locations * @param targets The target locations * @param time_infos The time info objects for the sources * @param invariant Whether time is invariant + * @return optionally the path's shape or "" */ - void RecostPaths(baldr::GraphReader& graphreader, - google::protobuf::RepeatedPtrField& sources, - google::protobuf::RepeatedPtrField& targets, - const std::vector& time_infos, - bool invariant); + std::string RecostFormPath(baldr::GraphReader& graphreader, + BestCandidate& connection, + const valhalla::Location& source, + const valhalla::Location& target, + const uint32_t source_idx, + const uint32_t target_idx, + const baldr::TimeInfo& time_info, + const bool invariant, + const ShapeFormat shape_format); /** * Sets the date_time on the origin locations. @@ -280,17 +333,39 @@ class CostMatrix { return infos; }; + void ModifyHierarchyLimits() { + // Distance threshold optimized for unidirectional search. For bidirectional case + // they can be lowered. + // Decrease distance thresholds only for arterial roads for now + for (size_t source = 0; source < locs_count_[MATRIX_FORW]; source++) { + if (hierarchy_limits_[MATRIX_FORW][source][1].max_up_transitions != kUnlimitedTransitions) + hierarchy_limits_[MATRIX_FORW][source][1].expansion_within_dist /= 2.f; + } + for (size_t target = 0; target < locs_count_[MATRIX_REV]; target++) { + if (hierarchy_limits_[MATRIX_REV][target][1].max_up_transitions != kUnlimitedTransitions) + hierarchy_limits_[MATRIX_REV][target][1].expansion_within_dist /= 2.f; + } + }; + /** - * Form a time/distance matrix from the results. - * @return Returns a time distance matrix among locations. + * Get the minimum AStar heuristic for a given source/target, i.e. for a source we get + * the minimum heuristic of all targets for the forward expansion, so that we direct + * the search towards the closest target/source. + * + * @param loc_idx either the source or target index + * @param node_ll the current edge's end node's lat/lon + * @returns The heuristic for the closest target/source of the passed node */ - std::vector FormTimeDistanceMatrix(); + template + float GetAstarHeuristic(const uint32_t loc_idx, const PointLL& node_ll) const; private: - class TargetMap; + class ReachedMap; - // Mark each target edge with a list of target indexes that have reached it - std::unique_ptr targets_; + // Mark each source/target edge with a list of source/target indexes that have reached it + std::unique_ptr targets_; + std::unique_ptr sources_; }; } // namespace thor diff --git a/valhalla/thor/dijkstras.h b/valhalla/thor/dijkstras.h index 5c82491c01..91bd822d94 100644 --- a/valhalla/thor/dijkstras.h +++ b/valhalla/thor/dijkstras.h @@ -2,10 +2,8 @@ #define VALHALLA_THOR_Dijkstras_H_ #include -#include #include #include -#include #include #include @@ -70,8 +68,15 @@ class Dijkstras { * @param expansion_callback the functor to call back when the Dijkstra makes progress * on a given edge */ - using expansion_callback_t = std::function< - void(baldr::GraphReader&, baldr::GraphId, const char*, const char*, float, uint32_t, float)>; + using expansion_callback_t = std::function; void set_track_expansion(const expansion_callback_t& expansion_callback) { expansion_callback_ = expansion_callback; } diff --git a/valhalla/thor/isochrone.h b/valhalla/thor/isochrone.h index 5a62891ff0..79767e60cc 100644 --- a/valhalla/thor/isochrone.h +++ b/valhalla/thor/isochrone.h @@ -2,11 +2,7 @@ #define VALHALLA_THOR_ISOCHRONE_H_ #include -#include #include -#include -#include -#include #include #include diff --git a/valhalla/thor/map_matcher.h b/valhalla/thor/map_matcher.h index 447eec205e..e4a3005b80 100644 --- a/valhalla/thor/map_matcher.h +++ b/valhalla/thor/map_matcher.h @@ -1,11 +1,7 @@ #ifndef VALHALLA_THOR_MAP_MATCHER_H_ #define VALHALLA_THOR_MAP_MATCHER_H_ -#include #include -#include -#include -#include #include #include diff --git a/valhalla/thor/matrix_common.h b/valhalla/thor/matrix_common.h deleted file mode 100644 index e7ecf499c5..0000000000 --- a/valhalla/thor/matrix_common.h +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef VALHALLA_THOR_MATRIX_COMMON_H_ -#define VALHALLA_THOR_MATRIX_COMMON_H_ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace valhalla { -namespace thor { - -enum class MatrixType : bool { TimeDist, Cost }; - -// Default for time distance matrix is to find all locations -constexpr uint32_t kAllLocations = std::numeric_limits::max(); -constexpr float kMaxCost = 99999999.9999f; - -// Structure to hold information about each destination. -struct Destination { - // per-origin information - bool settled; // Has the best time/distance to this destination - // been found? - sif::Cost best_cost; // Current best cost to this destination - // Set of still available correlated edges; - std::unordered_set dest_edges_available; - - // global information which only needs to be set once or is reset for every origin in the algorithm - uint32_t distance; // Path distance for the best cost path - float threshold; // Threshold above current best cost where no longer - // need to search for this destination. - // partial distance of correlated edges - std::unordered_map dest_edges_percent_along; - - // Constructor - set best_cost to an absurdly high value so any new cost - // will be lower. - Destination() : settled(false), best_cost{kMaxCost, kMaxCost}, distance(0), threshold(0.0f) { - } - - // clears the per-origin information - void reset() { - settled = false; - best_cost = {kMaxCost, kMaxCost}; - dest_edges_available.clear(); - } -}; - -// Time and Distance structure -struct TimeDistance { - uint32_t time; // Time in seconds - uint32_t dist; // Distance in meters - std::string date_time; - - TimeDistance() : time(0), dist(0), date_time("") { - } - - TimeDistance(const uint32_t secs, const uint32_t meters) : time(secs), dist(meters), date_time("") { - } - - TimeDistance(const uint32_t secs, const uint32_t meters, std::string date_time) - : time(secs), dist(meters), date_time(date_time) { - } -}; - -// Will return a destination's date_time string -inline std::string get_date_time(const std::string& origin_dt, - const uint64_t& origin_tz, - const baldr::GraphId& pred_id, - baldr::GraphReader& reader, - const uint64_t& offset) { - if (origin_dt.empty()) { - return ""; - } else if (!offset) { - return origin_dt; - } - graph_tile_ptr tile = nullptr; - uint32_t dest_tz = 0; - if (pred_id.Is_Valid()) { - // get the timezone of the output location - auto out_nodes = reader.GetDirectedEdgeNodes(pred_id, tile); - dest_tz = reader.GetTimezone(out_nodes.first, tile) || reader.GetTimezone(out_nodes.second, tile); - } - - auto in_epoch = - baldr::DateTime::seconds_since_epoch(origin_dt, - baldr::DateTime::get_tz_db().from_index(origin_tz)); - uint64_t out_epoch = in_epoch + offset; - std::string out_dt = - baldr::DateTime::seconds_to_date(out_epoch, baldr::DateTime::get_tz_db().from_index(dest_tz), - false); - - return out_dt; -} - -// return true if any location had a valid time set -// return false if it doesn't make sense computationally and add warnings accordingly -inline bool check_matrix_time(Api& request, const MatrixType type) { - const auto& options = request.options(); - bool less_sources = options.sources().size() <= options.targets().size(); - - for (const auto& source : options.sources()) { - if (!source.date_time().empty()) { - if (!less_sources && type == MatrixType::TimeDist) { - add_warning(request, 201); - return false; - } - return true; - } - } - for (const auto& target : options.targets()) { - if (!target.date_time().empty()) { - if (less_sources && type == MatrixType::TimeDist) { - add_warning(request, 202); - return false; - } else if (type == MatrixType::Cost) { - add_warning(request, 206); - return false; - } - return true; - } - } - - return false; -} - -} // namespace thor -} // namespace valhalla - -#endif // VALHALLA_THOR_MATRIX_COMMON_H_ diff --git a/valhalla/thor/matrixalgorithm.h b/valhalla/thor/matrixalgorithm.h new file mode 100644 index 0000000000..3e0fb34a91 --- /dev/null +++ b/valhalla/thor/matrixalgorithm.h @@ -0,0 +1,243 @@ +#ifndef __VALHALLA_THOR_MATRIXALGORITHM_H__ +#define __VALHALLA_THOR_MATRIXALGORITHM_H__ + +#include + +#include + +#include +#include +#include +// TODO(nils): should abstract more so we don't pull this in +#include +#include + +namespace valhalla { +namespace thor { + +// Default for time distance matrix is to find all locations +constexpr uint32_t kAllLocations = std::numeric_limits::max(); +constexpr float kMaxCost = 99999999.9999f; + +/** + * Pure virtual class defining the interface for MatrixAlgorithm + */ +class MatrixAlgorithm { +public: + /** + * Constructor + */ + MatrixAlgorithm(const boost::property_tree::ptree& config) + : interrupt_(nullptr), has_time_(false), not_thru_pruning_(true), expansion_callback_(), + clear_reserved_memory_(config.get("clear_reserved_memory", false)) { + } + + MatrixAlgorithm(const MatrixAlgorithm&) = delete; + MatrixAlgorithm& operator=(const MatrixAlgorithm&) = delete; + + /** + * Destructor + */ + virtual ~MatrixAlgorithm() { + } + + /** + * Forms a time distance matrix from the set of source locations + * to the set of target locations. + * @param request the full request + * @param graphreader List of source/origin locations. + * @param mode_costing List of target/destination locations. + * @param mode Graph reader for accessing routing graph. + * @param max_matrix_distance Maximum arc-length distance for current mode. + * @param has_time whether time-dependence was requested + * @param invariant whether invariant time-dependence was requested + * @param shape_format which shape_format, if any + * @returns Whether there were unfound connections + */ + virtual bool SourceToTarget(Api& request, + baldr::GraphReader& graphreader, + const sif::mode_costing_t& mode_costing, + const sif::travel_mode_t mode, + const float max_matrix_distance) = 0; + + /** + * Clear the temporary information. + */ + virtual void Clear() = 0; + + /** + * Get the algorithm's name + * @return the name of the algorithm + */ + virtual const std::string& name() = 0; + + /** + * Returns the name of the algorithm + * @return the name of the algorithm + */ + void set_has_time(const bool has_time) { + has_time_ = has_time; + }; + + /** + * + * There is a rare case where we may encounter only_restrictions with edges being + * marked as not_thru. Basically the only way to get in this area is via one edge + * and all other edges are restricted, but this one edge is also marked as not_thru. + * Therefore, on the first pass the expansion stops as we cannot take the restricted + * turns and we cannot go into the not_thru region. On the 2nd pass, we now ignore + * not_thru flags and allow entry into the not_thru region due to the fact that + * not_thru_pruning_ is false. See the gurka test not_thru_pruning_. + * + * Set the not_thru_pruning_ + * @param pruning set the not_thru_pruning_ to pruning value. + * only set on the second pass + */ + void set_not_thru_pruning(const bool pruning) { + not_thru_pruning_ = pruning; + } + + /** + * Get the not thru pruning + * @return Returns not_thru_pruning_ + */ + bool not_thru_pruning() { + return not_thru_pruning_; + } + + /** + * Set a callback that will throw when the path computation should be aborted + * @param interrupt_callback the function to periodically call to see if + * we should abort + */ + void set_interrupt(const std::function* interrupt_callback) { + interrupt_ = interrupt_callback; + } + + /** + * Sets the functor which will track the algorithms expansion. + * + * @param expansion_callback the functor to call back when the algorithm makes progress + * on a given edge + */ + using expansion_callback_t = std::function; + void set_track_expansion(const expansion_callback_t& expansion_callback) { + expansion_callback_ = expansion_callback; + } + +protected: + const std::function* interrupt_; + + // whether time was specified + bool has_time_; + + // Indicates whether to allow access into a not-thru region. + bool not_thru_pruning_; + + // for tracking the expansion of the algorithm visually + expansion_callback_t expansion_callback_; + + uint32_t max_reserved_labels_count_; + + // if `true` clean reserved memory for edge labels + bool clear_reserved_memory_; + + // on first pass, resizes all PBF sequences and defaults to 0 or "" + inline static void reserve_pbf_arrays(valhalla::Matrix& matrix, size_t size, uint32_t pass = 0) { + if (pass == 0) { + matrix.mutable_from_indices()->Resize(size, 0U); + matrix.mutable_to_indices()->Resize(size, 0U); + matrix.mutable_distances()->Resize(size, 0U); + matrix.mutable_times()->Resize(size, 0U); + matrix.mutable_second_pass()->Resize(size, false); + // repeated strings don't support Resize() + matrix.mutable_date_times()->Reserve(size); + matrix.mutable_time_zone_offsets()->Reserve(size); + matrix.mutable_time_zone_names()->Reserve(size); + matrix.mutable_shapes()->Reserve(size); + for (size_t i = 0; i < size; i++) { + auto* date_time = matrix.mutable_date_times()->Add(); + *date_time = ""; + auto* time_zone_offset = matrix.mutable_time_zone_offsets()->Add(); + *time_zone_offset = ""; + auto* time_zone_name = matrix.mutable_time_zone_names()->Add(); + *time_zone_name = ""; + auto* shape = matrix.mutable_shapes()->Add(); + *shape = ""; + } + } + } +}; + +// Structure to hold information about each destination. +struct Destination { + // per-origin information + bool settled; // Has the best time/distance to this destination + // been found? + sif::Cost best_cost; // Current best cost to this destination + // Set of still available correlated edges; + std::unordered_set dest_edges_available; + + // global information which only needs to be set once or is reset for every origin in the algorithm + uint32_t distance; // Path distance for the best cost path + float threshold; // Threshold above current best cost where no longer + // need to search for this destination. + // partial distance of correlated edges + std::unordered_map dest_edges_percent_along; + + // Constructor - set best_cost to an absurdly high value so any new cost + // will be lower. + Destination() : settled(false), best_cost{kMaxCost, kMaxCost}, distance(0), threshold(0.0f) { + } + + // clears the per-origin information + void reset() { + settled = false; + best_cost = {kMaxCost, kMaxCost}; + dest_edges_available.clear(); + } +}; + +// return true if any location had a valid time set +// return false if it doesn't make sense computationally and add warnings accordingly +inline bool check_matrix_time(Api& request, const Matrix::Algorithm algo) { + const auto& options = request.options(); + bool less_sources = options.sources().size() <= options.targets().size(); + + for (const auto& source : options.sources()) { + if (!source.date_time().empty()) { + if (!less_sources && algo == Matrix::TimeDistanceMatrix) { + add_warning(request, 201); + return false; + } + return true; + } + } + for (const auto& target : options.targets()) { + if (!target.date_time().empty()) { + if (less_sources && algo == Matrix::TimeDistanceMatrix) { + add_warning(request, 202); + return false; + } else if (algo == Matrix::CostMatrix) { + add_warning(request, 206); + return false; + } + return true; + } + } + + return false; +} + +} // namespace thor +} // namespace valhalla + +#endif // __VALHALLA_THOR_MATRIXALGORITHM_H__ diff --git a/valhalla/thor/multimodal.h b/valhalla/thor/multimodal.h index 365e948d57..c0e779a935 100644 --- a/valhalla/thor/multimodal.h +++ b/valhalla/thor/multimodal.h @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -75,9 +74,8 @@ class MultiModalPathAlgorithm : public PathAlgorithm { protected: uint32_t max_walking_dist_; - uint32_t max_label_count_; // Max label count to allow - sif::TravelMode mode_; // Current travel mode - uint8_t travel_type_; // Current travel type + sif::TravelMode mode_; // Current travel mode + uint8_t travel_type_; // Current travel type bool date_set_; bool date_before_tile_; diff --git a/valhalla/thor/pathalgorithm.h b/valhalla/thor/pathalgorithm.h index 0d1a5e977f..4a75b48793 100644 --- a/valhalla/thor/pathalgorithm.h +++ b/valhalla/thor/pathalgorithm.h @@ -1,10 +1,6 @@ #pragma once #include -#include -#include -#include -#include #include #include @@ -126,8 +122,15 @@ class PathAlgorithm { * @param expansion_callback the functor to call back when the algorithm makes progress * on a given edge */ - using expansion_callback_t = std::function< - void(baldr::GraphReader&, baldr::GraphId, const char*, const char*, float, uint32_t, float)>; + using expansion_callback_t = std::function; void set_track_expansion(const expansion_callback_t& expansion_callback) { expansion_callback_ = expansion_callback; } @@ -149,32 +152,32 @@ class PathAlgorithm { // if `true` clean reserved memory for edge labels bool clear_reserved_memory_; +}; - /** - * Check for path completion along the same edge. Edge ID in question - * is along both an origin and destination and origin shows up at the - * beginning of the edge while the destination shows up at the end of - * the edge. - * @param edgeid Edge id. - * @param origin Origin path location information. - * @param destination Destination path location information. - */ - virtual bool IsTrivial(const baldr::GraphId& edgeid, - const valhalla::Location& origin, - const valhalla::Location& destination) const { - for (const auto& destination_edge : destination.correlation().edges()) { - if (destination_edge.graph_id() == edgeid) { - for (const auto& origin_edge : origin.correlation().edges()) { - if (origin_edge.graph_id() == edgeid && - origin_edge.percent_along() <= destination_edge.percent_along()) { - return true; - } +/** + * Check for path completion along the same edge. Edge ID in question + * is along both an origin and destination and origin shows up at the + * beginning of the edge while the destination shows up at the end of + * the edge. + * @param edgeid Edge id. + * @param origin Origin path location information. + * @param destination Destination path location information. + */ +inline bool IsTrivial(const baldr::GraphId& edgeid, + const valhalla::Location& origin, + const valhalla::Location& destination) { + for (const auto& destination_edge : destination.correlation().edges()) { + if (destination_edge.graph_id() == edgeid) { + for (const auto& origin_edge : origin.correlation().edges()) { + if (origin_edge.graph_id() == edgeid && + origin_edge.percent_along() <= destination_edge.percent_along()) { + return true; } } } - return false; } -}; + return false; +} // Container for the data we iterate over in Expand* function struct EdgeMetadata { diff --git a/valhalla/thor/pathinfo.h b/valhalla/thor/pathinfo.h index ea46cb6e19..1e48fbd7dc 100644 --- a/valhalla/thor/pathinfo.h +++ b/valhalla/thor/pathinfo.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_THOR_PATHINFO_H_ #define VALHALLA_THOR_PATHINFO_H_ -#include #include #include diff --git a/valhalla/thor/route_matcher.h b/valhalla/thor/route_matcher.h index f2e78d519f..b42494cef4 100644 --- a/valhalla/thor/route_matcher.h +++ b/valhalla/thor/route_matcher.h @@ -1,11 +1,6 @@ #ifndef VALHALLA_THOR_ROUTE_MATCHER_H_ #define VALHALLA_THOR_ROUTE_MATCHER_H_ -#include -#include -#include -#include -#include #include #include diff --git a/valhalla/thor/timedistancebssmatrix.h b/valhalla/thor/timedistancebssmatrix.h index 7add0830d2..eb62014215 100644 --- a/valhalla/thor/timedistancebssmatrix.h +++ b/valhalla/thor/timedistancebssmatrix.h @@ -2,28 +2,26 @@ #define VALHALLA_THOR_TIMEDISTANCEBSSMATRIX_H_ #include -#include #include #include -#include #include #include #include #include #include +#include #include #include #include #include -#include -#include +#include namespace valhalla { namespace thor { // Class to compute time + distance matrices among locations. -class TimeDistanceBSSMatrix { +class TimeDistanceBSSMatrix : public MatrixAlgorithm { public: /** * Default constructor. Most internal values are set when a query is made so @@ -34,42 +32,35 @@ class TimeDistanceBSSMatrix { /** * Forms a time distance matrix from the set of source locations * to the set of target locations. - * @param source_location_list List of source/origin locations. - * @param target_location_list List of target/destination locations. + * @param request the full request * @param graphreader Graph reader for accessing routing graph. * @param mode_costing Costing methods. * @param mode Travel mode to use. Actually It doesn't make sense in matrix_bss, * because the travel mode must be pedestrian and bicycle * @param max_matrix_distance Maximum arc-length distance for current mode. - * @param matrix_locations Number of matrix locations to satisfy a one to many or many to - * one request. This allows partial results: e.g. find time/distance - * to the closest 20 out of 50 locations). * @return time/distance from origin index to all other locations */ - inline std::vector - SourceToTarget(const google::protobuf::RepeatedPtrField& source_location_list, - const google::protobuf::RepeatedPtrField& target_location_list, - baldr::GraphReader& graphreader, - const sif::mode_costing_t& mode_costing, - const sif::travel_mode_t /*mode*/, - const float max_matrix_distance, - const uint32_t matrix_locations = kAllLocations) { + inline bool SourceToTarget(Api& request, + baldr::GraphReader& graphreader, + const sif::mode_costing_t& mode_costing, + const sif::travel_mode_t /*mode*/, + const float max_matrix_distance) override { - LOG_INFO("matrix::TimeDistanceBSSMatrix"); + request.mutable_matrix()->set_algorithm(Matrix::TimeDistanceMatrix); + + if (request.options().shape_format() != no_shape) + add_warning(request, 207); // Set the costings pedestrian_costing_ = mode_costing[static_cast(sif::travel_mode_t::kPedestrian)]; bicycle_costing_ = mode_costing[static_cast(sif::travel_mode_t::kBicycle)]; - const bool forward_search = source_location_list.size() <= target_location_list.size(); + const bool forward_search = + request.options().sources().size() <= request.options().targets().size(); if (forward_search) { - return ComputeMatrix(source_location_list, target_location_list, - graphreader, max_matrix_distance, - matrix_locations); + return ComputeMatrix(request, graphreader, max_matrix_distance); } else { - return ComputeMatrix(source_location_list, target_location_list, - graphreader, max_matrix_distance, - matrix_locations); + return ComputeMatrix(request, graphreader, max_matrix_distance); } }; @@ -77,7 +68,7 @@ class TimeDistanceBSSMatrix { * Clear the temporary information generated during time+distance * matrix construction. */ - inline void clear() { + inline void Clear() override { auto reservation = clear_reserved_memory_ ? 0 : max_reserved_labels_count_; if (edgelabels_.size() > reservation) { edgelabels_.resize(max_reserved_labels_count_); @@ -88,6 +79,14 @@ class TimeDistanceBSSMatrix { dest_edges_.clear(); }; + /** + * Get the algorithm's name + * @return the name of the algorithm + */ + inline const std::string& name() override { + return MatrixAlgoToString(Matrix::TimeDistanceBSSMatrix); + } + protected: // Number of destinations that have been found and settled (least cost path // computed). @@ -153,12 +152,7 @@ class TimeDistanceBSSMatrix { */ template - std::vector - ComputeMatrix(const google::protobuf::RepeatedPtrField& source_location_list, - const google::protobuf::RepeatedPtrField& target_location_list, - baldr::GraphReader& graphreader, - const float max_matrix_distance, - const uint32_t matrix_locations = kAllLocations); + bool ComputeMatrix(Api& request, baldr::GraphReader& graphreader, const float max_matrix_distance); /** * Expand from the node along the forward search path. Immediately expands @@ -201,7 +195,7 @@ class TimeDistanceBSSMatrix { void SetOrigin(baldr::GraphReader& graphreader, const valhalla::Location& origin); /** - * Initalize destinations for all origins. + * Initialize destinations for all origins. * @param graphreader Graph reader for accessing routing graph. * @param locations List of locations. */ @@ -248,9 +242,10 @@ class TimeDistanceBSSMatrix { /** * Form a time/distance matrix from the results. - * @return Returns a time distance matrix among locations. + * + * @param request the request PBF */ - std::vector FormTimeDistanceMatrix(); + void FormTimeDistanceMatrix(Api& request, const bool forward, const uint32_t origin_index); }; } // namespace thor diff --git a/valhalla/thor/timedistancematrix.h b/valhalla/thor/timedistancematrix.h index 63341cce46..8d41894b76 100644 --- a/valhalla/thor/timedistancematrix.h +++ b/valhalla/thor/timedistancematrix.h @@ -2,26 +2,24 @@ #define VALHALLA_THOR_TIMEDISTANCEMATRIX_H_ #include -#include #include #include -#include #include #include #include #include +#include #include #include #include -#include -#include +#include namespace valhalla { namespace thor { // Class to compute time + distance matrices among locations. -class TimeDistanceMatrix { +class TimeDistanceMatrix : public MatrixAlgorithm { public: /** * Default constructor. Most internal values are set when a query is made so @@ -32,45 +30,35 @@ class TimeDistanceMatrix { /** * Forms a time distance matrix from the set of source locations * to the set of target locations. - * @param source_location_list List of source/origin locations. - * @param target_location_list List of target/destination locations. + * @param request the full request * @param graphreader Graph reader for accessing routing graph. * @param mode_costing Costing methods. * @param mode Travel mode to use. * @param max_matrix_distance Maximum arc-length distance for current mode. - * @param matrix_locations Number of matrix locations to satisfy a one to many or many to - * one request. This allows partial results: e.g. find time/distance - * to the closest 20 out of 50 locations). - * @param has_time Whether the request had valid date_time. - * @param invariant Whether invariant time was requested. * * @return time/distance from all sources to all targets */ - inline std::vector - SourceToTarget(google::protobuf::RepeatedPtrField& source_location_list, - google::protobuf::RepeatedPtrField& target_location_list, - baldr::GraphReader& graphreader, - const sif::mode_costing_t& mode_costing, - const sif::travel_mode_t mode, - const float max_matrix_distance, - const uint32_t matrix_locations = kAllLocations, - const bool invariant = false) { - - LOG_INFO("matrix::TimeDistanceMatrix"); + inline bool SourceToTarget(Api& request, + baldr::GraphReader& graphreader, + const sif::mode_costing_t& mode_costing, + const sif::travel_mode_t mode, + const float max_matrix_distance) override { + + request.mutable_matrix()->set_algorithm(Matrix::TimeDistanceMatrix); + + if (request.options().shape_format() != no_shape) + add_warning(request, 207); // Set the mode and costing mode_ = mode; costing_ = mode_costing[static_cast(mode_)]; - const bool forward_search = source_location_list.size() <= target_location_list.size(); + const bool forward_search = + request.options().sources().size() <= request.options().targets().size(); if (forward_search) { - return ComputeMatrix(source_location_list, target_location_list, - graphreader, max_matrix_distance, matrix_locations, - invariant); + return ComputeMatrix(request, graphreader, max_matrix_distance); } else { - return ComputeMatrix(target_location_list, source_location_list, - graphreader, max_matrix_distance, matrix_locations, - invariant); + return ComputeMatrix(request, graphreader, max_matrix_distance); } }; @@ -78,10 +66,10 @@ class TimeDistanceMatrix { * Clear the temporary information generated during time+distance * matrix construction. */ - inline void clear() { + inline void Clear() override { auto reservation = clear_reserved_memory_ ? 0 : max_reserved_labels_count_; if (edgelabels_.size() > reservation) { - edgelabels_.resize(max_reserved_labels_count_); + edgelabels_.resize(reservation); edgelabels_.shrink_to_fit(); } reset(); @@ -89,6 +77,14 @@ class TimeDistanceMatrix { dest_edges_.clear(); }; + /** + * Get the algorithm's name + * @return the name of the algorithm + */ + inline const std::string& name() override { + return MatrixAlgoToString(Matrix::TimeDistanceMatrix); + } + protected: // Number of destinations that have been found and settled (least cost path // computed). @@ -149,13 +145,7 @@ class TimeDistanceMatrix { */ template - std::vector - ComputeMatrix(google::protobuf::RepeatedPtrField& source_location_list, - google::protobuf::RepeatedPtrField& target_location_list, - baldr::GraphReader& graphreader, - const float max_matrix_distance, - const uint32_t matrix_locations = kAllLocations, - const bool invariant = false); + bool ComputeMatrix(Api& request, baldr::GraphReader& graphreader, const float max_matrix_distance); /** * Expand from the node along the forward search path. Immediately expands @@ -266,17 +256,20 @@ class TimeDistanceMatrix { /** * Form a time/distance matrix from the results. + * + * @param request The full request object * @param reader GraphReader instance * @param origin_dt The origin's date_time string * @param origin_tz The origin's timezone index * @param pred_id The destination edge's GraphId - * - * @return Returns a time distance matrix among locations. */ - std::vector FormTimeDistanceMatrix(baldr::GraphReader& reader, - const std::string& origin_dt, - const uint64_t& origin_tz, - const baldr::GraphId& pred_id); + void FormTimeDistanceMatrix(Api& request, + baldr::GraphReader& reader, + const bool forward, + const uint32_t origin_index, + const std::string& origin_dt, + const uint64_t& origin_tz, + std::unordered_map& edge_ids); }; } // namespace thor diff --git a/valhalla/thor/triplegbuilder.h b/valhalla/thor/triplegbuilder.h index 567e787078..d79bc78ddf 100644 --- a/valhalla/thor/triplegbuilder.h +++ b/valhalla/thor/triplegbuilder.h @@ -1,7 +1,5 @@ #pragma once -#include -#include #include #include #include diff --git a/valhalla/thor/unidirectional_astar.h b/valhalla/thor/unidirectional_astar.h index a4a5526cc5..15d2c0072d 100644 --- a/valhalla/thor/unidirectional_astar.h +++ b/valhalla/thor/unidirectional_astar.h @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -73,15 +72,6 @@ class UnidirectionalAStar : public PathAlgorithm { } } - /** - * Set a maximum label count. The path algorithm terminates if this - * is exceeded. - * @param max_count Maximum number of labels to allow. - */ - void set_max_label_count(const uint32_t max_count) { - max_label_count_ = max_count; - } - protected: /** * Initializes the hierarchy limits, A* heuristic, and adjacency list. @@ -166,9 +156,8 @@ class UnidirectionalAStar : public PathAlgorithm { */ std::vector FormPath(const uint32_t dest); - uint32_t max_label_count_; // Max label count to allow - sif::TravelMode mode_; // Current travel mode - uint8_t travel_type_; // Current travel type + sif::TravelMode mode_; // Current travel mode + uint8_t travel_type_; // Current travel type // Hierarchy limits. std::vector hierarchy_limits_; diff --git a/valhalla/thor/worker.h b/valhalla/thor/worker.h index 3b313066a1..fb0ab64033 100644 --- a/valhalla/thor/worker.h +++ b/valhalla/thor/worker.h @@ -1,7 +1,6 @@ #ifndef __VALHALLA_THOR_SERVICE_H__ #define __VALHALLA_THOR_SERVICE_H__ -#include #include #include @@ -35,7 +34,7 @@ namespace valhalla { namespace thor { -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES void run_service(const boost::property_tree::ptree& config); #endif @@ -45,7 +44,7 @@ class thor_worker_t : public service_worker_t { thor_worker_t(const boost::property_tree::ptree& config, const std::shared_ptr& graph_reader = {}); virtual ~thor_worker_t(); -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES virtual prime_server::worker_t::result_t work(const std::list& job, void* request_info, const std::function& interrupt) override; @@ -54,12 +53,6 @@ class thor_worker_t : public service_worker_t { static void adjust_scores(valhalla::Options& options); - static std::string offset_date(baldr::GraphReader& reader, - const std::string& in_dt, - const baldr::GraphId& in_edge, - float offset, - const baldr::GraphId& out_edge); - void route(Api& request); std::string matrix(Api& request); void optimized_route(Api& request); @@ -83,6 +76,8 @@ class thor_worker_t : public service_worker_t { const Location& origin, const Location& destination, const Options& options); + thor::MatrixAlgorithm* + get_matrix_algorithm(Api& request, const bool has_time, const std::string& costing); void route_match(Api& request); /** * Returns the results of the map match where the first float is the normalized @@ -134,6 +129,7 @@ class thor_worker_t : public service_worker_t { float max_timedep_distance; std::unordered_map max_matrix_distance; SOURCE_TO_TARGET_ALGORITHM source_to_target_algorithm; + bool costmatrix_allow_second_pass; std::shared_ptr reader; meili::MapMatcherFactory matcher_factory; baldr::AttributesController controller; diff --git a/valhalla/tyr/actor.h b/valhalla/tyr/actor.h index 43ecb75d21..b90ecc5875 100644 --- a/valhalla/tyr/actor.h +++ b/valhalla/tyr/actor.h @@ -3,7 +3,6 @@ #include #include -#include #include #include diff --git a/valhalla/tyr/serializer_constants.h b/valhalla/tyr/serializer_constants.h index e8c882f186..080164f786 100644 --- a/valhalla/tyr/serializer_constants.h +++ b/valhalla/tyr/serializer_constants.h @@ -1,3 +1,6 @@ +#ifndef VALHALLA_TYR_ROUTE_SERIALIZER_CONSTANTS_ +#define VALHALLA_TYR_ROUTE_SERIALIZER_CONSTANTS_ +#pragma once #include namespace valhalla { @@ -18,3 +21,4 @@ const std::string kModifierSharpRight = "sharp right"; } // namespace osrmconstants } // namespace tyr } // namespace valhalla +#endif // VALHALLA_TYR_ROUTE_SERIALIZER_CONSTANTS_ diff --git a/valhalla/tyr/serializers.h b/valhalla/tyr/serializers.h index 30cd1b4f4f..6bb72dfdd8 100644 --- a/valhalla/tyr/serializers.h +++ b/valhalla/tyr/serializers.h @@ -1,8 +1,6 @@ #ifndef __VALHALLA_TYR_SERVICE_H__ #define __VALHALLA_TYR_SERVICE_H__ -#include -#include #include #include #include @@ -15,8 +13,9 @@ #include #include #include +#include #include -#include +#include #include namespace valhalla { @@ -30,10 +29,7 @@ std::string serializeDirections(Api& request); /** * Turn a time distance matrix into json that one can look up location pair results from */ -std::string serializeMatrix(const Api& request, - const std::vector& time_distances, - double distance_scale, - thor::MatrixType matrix_type); +std::string serializeMatrix(Api& request); /** * Turn grid data contours into geojson @@ -41,11 +37,13 @@ std::string serializeMatrix(const Api& request, * @param grid_contours the contours generated from the grid * @param colors the #ABC123 hex string color used in geojson fill color */ -std::string serializeIsochrones(const Api& request, +std::string serializeIsochrones(Api& request, std::vector::contour_interval_t>& intervals, - midgard::GriddedData<2>::contours_t& contours, - bool polygons = true, - bool show_locations = false); + const std::shared_ptr>& isogrid); +/** + * Write GeoJSON from expansion pbf + */ +std::string serializeExpansion(Api& request, const std::string& algo); /** * Turn heights and ranges into a height response @@ -125,6 +123,127 @@ std::string serializePbf(Api& request); */ void serializeWarnings(const valhalla::Api& api, rapidjson::writer_wrapper_t& writer); baldr::json::ArrayPtr serializeWarnings(const valhalla::Api& api); + +/** + * Turns a line into a GeoJSON LineString geometry. + * + * @param shape The points making up the line. + * @returns The GeoJSON geometry of the LineString + */ +baldr::json::MapPtr geojson_shape(const std::vector shape); + +// Elevation serialization support + +/** + * Update elevations where consecutive bridges or tunnels occur. This does a linear interpolation of + * elevation from just prior to first bridge/tunnel to just after the last bridge/tunnel. This is + * done since elevation is stored for single edges and if a bridge/tunnel spans multiple edges then + * the elevation can be incorrect. + * @param elevation Elevation vector. + * @param bridges List of bridges/tunnels - the start and end index into the elevation list for + * each. + */ +inline void update_bridge_elevations(std::vector& elevation, + std::vector>& bridges) { + for (const auto& bridge : bridges) { + // Validate elevation indexes. Get elevation before and after the bridge/tunnel and + // form delta elevation for linear interpolation. + int32_t n = bridge.second - bridge.first; + if (n > 0 && bridge.first < elevation.size() && bridge.second < elevation.size()) { + float start_elevation = bridge.first > 0 ? elevation[bridge.first - 1] : elevation[0]; + float end_elevation = + bridge.second < elevation.size() - 1 ? elevation[bridge.second + 1] : elevation.back(); + float delta = (end_elevation - start_elevation) / n; + float elev = start_elevation + delta; + for (uint32_t i = bridge.first; i <= bridge.second; ++i) { + elevation[i] = elev; + elev += delta; + } + } + } +} + +/** + * Convenience method to get elevation along a path from TripLeg. Samples + * the edge elevation information at the specified interval. + * @param path_leg Trip path for this route leg. + * @param interval Elevation sampling interval (meters) along the path. + * @param start_distance Optional start distance along the path. This is used when + * matched and unmatched sections so that a consistent sampling + * interval is preserved. + * @return Returns an array of elevation postings along the path. + */ +inline std::vector +get_elevation(const TripLeg& path_leg, const float interval, const float start_distance = 0.0f) { + // Store the first elevation if start_distance == 0 + std::vector elevation; + auto first_elevation = path_leg.node(0).edge().elevation(0); + if (start_distance == 0.0f) { + elevation.push_back(first_elevation); + } + + uint32_t bridge_edge_count = 0; + uint32_t first_bridge_index; + float distance = 0.0f; // Distance from prior elevation posting + float remaining = -start_distance; // How much of the current edge interval remains + float prior_elevation = first_elevation; + std::vector> bridges; + for (const auto& node : path_leg.node()) { + // Get the edge on the path, the starting elevation and sampling interval + auto path_edge = node.edge(); + float edge_interval = path_edge.elevation_sampling_interval(); + + // Identify consecutive bridge/tunnel edges and store elevation indexes at start and end. + if (path_edge.bridge() || path_edge.tunnel()) { + if (bridge_edge_count == 0) { + first_bridge_index = elevation.size(); + } + ++bridge_edge_count; + } else { + // Not a bridge/tunnel. Add a bridge elevation pair if more than 1 consecutive bridge/tunnel + // exists prior to this edge. Make sure elevation size > 0 - may have very short tunnel edges + // after an unmatched section (no new elevation added). + if (bridge_edge_count > 1 && elevation.size() > 0) { + bridges.emplace_back(first_bridge_index, elevation.size() - 1); + } + bridge_edge_count = 0; + } + + // Iterate through the edge elevation (skip the first) + for (int32_t i = 1; i < path_edge.elevation_size(); ++i) { + auto elev = path_edge.elevation(i); + + // Update distance from prior elevation posting + distance = interval - remaining; + remaining += edge_interval; + + // Store elevation while distance remaining > interval + while (remaining > interval) { + // Linear interpolation between prior elevation + float p1 = distance / edge_interval; + elevation.push_back(prior_elevation * (1.0f - p1) + elev * p1); + remaining -= interval; + distance += interval; + } + + // Update prior elevation + prior_elevation = elev; + } + } + + // Store the last elevation + elevation.push_back(prior_elevation); + + // Update elevations along consecutive bridges/tunnels. Update bridges if + // last edge was a bridge (and edge before was also a bridge). + if (bridge_edge_count > 1) { + bridges.emplace_back(first_bridge_index, elevation.size() - 1); + } + update_bridge_elevations(elevation, bridges); + + return elevation; +} + } // namespace tyr } // namespace valhalla diff --git a/valhalla/valhalla.h b/valhalla/valhalla.h index 421459ff6e..d584964915 100644 --- a/valhalla/valhalla.h +++ b/valhalla/valhalla.h @@ -1,5 +1,5 @@ #pragma once #define VALHALLA_VERSION_MAJOR 3 -#define VALHALLA_VERSION_MINOR 4 +#define VALHALLA_VERSION_MINOR 5 #define VALHALLA_VERSION_PATCH 0 diff --git a/valhalla/worker.h b/valhalla/worker.h index 3332e4983b..6f131d039c 100644 --- a/valhalla/worker.h +++ b/valhalla/worker.h @@ -8,7 +8,7 @@ #include #include -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES #include #include #endif @@ -59,7 +59,7 @@ struct valhalla_exception_t : public std::runtime_error { * already filled out, it will be validated and the json will be ignored */ void ParseApi(const std::string& json_request, Options::Action action, Api& api); -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES /** * Take the json OR pbf request and parse/validate it. If you pass a protobuf mime type in the request * it is assumed that the body of the request is protobuf bytes and any json will be ignored. Likewise @@ -75,10 +75,16 @@ void ParseApi(const prime_server::http_request_t& http_request, Api& api); std::string serialize_error(const valhalla_exception_t& exception, Api& options); -// function to add warnings to proto info object -void add_warning(valhalla::Api& api, unsigned code); +/** + * Adds a warning to the request PBF object. + * + * @param api the full request + * @param code the warning code + * @param extra an optional string to append to the hard-coded warning message + */ +void add_warning(valhalla::Api& api, unsigned code, const std::string& extra = ""); -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES prime_server::worker_t::result_t serialize_error(const valhalla_exception_t& exception, prime_server::http_request_info_t& request_info, Api& options); @@ -102,7 +108,7 @@ class service_worker_t { virtual ~service_worker_t(); -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES /** * The main work function that stages in the prime_server will call when responding to requests * diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json new file mode 100644 index 0000000000..391ce615db --- /dev/null +++ b/vcpkg-configuration.json @@ -0,0 +1,5 @@ +{ + "overlay-ports": [ + "./overlay-ports-vcpkg" + ] +} diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000000..fbb848fe00 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", + "dependencies": [ + "boost-algorithm", + "boost-foreach", + "boost-format", + "boost-geometry", + "boost-heap", + "boost-optional", + "boost-property-tree", + "boost-range", + "boost-tokenizer", + "curl", + "cxxopts", + "date", + { + "name": "dirent", + "platform": "windows" + }, + "gdal", + "geos", + "libspatialite", + "pkgconf", + "pybind11", + "rapidjson", + "robin-hood-hashing", + "spatialite-tools", + "luajit", + "lz4", + "protobuf", + "sqlite3", + "zlib" + ] +}