├── .clang-format ├── .clang-tidy ├── .gitattributes ├── .github ├── CODEOWNERS └── workflows │ ├── build-linux.yml │ ├── build-macos-arm.yml │ ├── build-macos.yml │ ├── build-wheels-push.yml │ ├── build-wheels.yml │ └── build-windows.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── CMakeLists.txt ├── LICENSE ├── README.rst ├── config.h.in ├── dev ├── CMakeLists.txt ├── osm2gmns_dev.cpp └── osm2gmns_dev.py ├── docs ├── Makefile ├── make.bat └── source │ ├── _images │ ├── bbbike.png │ ├── consolidation.png │ ├── geofabrik.png │ ├── mrnet.png │ ├── net_asu.png │ ├── net_az.png │ ├── net_dfw.png │ ├── net_london.png │ ├── net_melbourne.png │ ├── net_midwest.png │ ├── net_nanjing.png │ ├── net_yangzhou.png │ ├── osm_id.png │ ├── osmhp.png │ └── poi1.png │ ├── acknowledgement.rst │ ├── conf.py │ ├── get-osm-data.rst │ ├── gmns.rst │ ├── index.rst │ ├── installation.rst │ ├── mrm.rst │ ├── public-api.rst │ ├── quick-start.rst │ ├── requirements.txt │ └── sample-net.rst ├── osm2gmns ├── __init__.py ├── downloader.py └── osm2gmns.py ├── osm2gmns_lib.cpp ├── osm2gmns_main.cpp ├── pyproject.toml ├── requirements.txt └── src ├── CMakeLists.txt ├── config.h ├── constants.h ├── functions.cpp ├── functions.h ├── io.cpp ├── io.h ├── networks.cpp ├── networks.h ├── osmconfig.cpp ├── osmconfig.h ├── osmnetwork.cpp ├── osmnetwork.h ├── utils.cpp └── utils.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | 5 | # Some folks prefer to write "int& foo" while others prefer "int &foo". The 6 | # Google Style Guide only asks for consistency within a project, we chose 7 | # "int& foo" for this project: 8 | DerivePointerAlignment: false 9 | PointerAlignment: Left 10 | 11 | # The Google Style Guide only asks for consistency w.r.t. "east const" vs. 12 | # "const west" alignment of cv-qualifiers. In this project we use "east const". 13 | QualifierAlignment: Left 14 | 15 | 16 | ColumnLimit: 120 17 | 18 | # Format raw string literals with a `pb` or `protos` tag as protos. 19 | RawStringFormats: 20 | - Language: TextProto 21 | Delimiters: 22 | - 'pb' 23 | - 'protos' 24 | BasedOnStyle: Google 25 | 26 | CommentPragmas: '(@copydoc|@copybrief|@see|@overload|@snippet)' 27 | --- 28 | Language: Proto 29 | BasedOnStyle: Google 30 | ... 31 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: '*,-=,-fuchsia-*,-zircon-*,-modernize-use-trailing-return-type,-hicpp-use-emplace,-modernize-use-emplace,-llvm-*,-llvmlibc-*,-readability-function-cognitive-complexity,-altera-*,-openmp-exception-escape,-cppcoreguidelines-owning-memory,-bugprone-easily-swappable-parameters,-bugprone-branch-clone' 2 | WarningsAsErrors: '*' 3 | HeaderFilterRegex: 'src/.*' 4 | FormatStyle: 'file' 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # review when someone opens a pull request. 2 | * @jiawlu 3 | 4 | # The owner of the ownersfile 5 | /.github/CODEOWNERS @jiawlu -------------------------------------------------------------------------------- /.github/workflows/build-linux.yml: -------------------------------------------------------------------------------- 1 | name: Build Linux 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | name: Build Linux 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: "Update apt get" 17 | run: sudo apt-get update && sudo apt-get upgrade -y 18 | 19 | - name: "Install required packages." 20 | run: sudo apt-get install -y build-essential ccache llvm-dev libclang-dev clang cppcheck libboost-all-dev curl libcurl4-openssl-dev doxygen git graphviz libssl-dev make ninja-build libomp-dev python3 python3-pip tar unzip wget iwyu 21 | 22 | - name: "Build" 23 | run: rm -rf build && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -GNinja .. && ninja 24 | -------------------------------------------------------------------------------- /.github/workflows/build-macos-arm.yml: -------------------------------------------------------------------------------- 1 | name: Build macOS-ARM 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | name: Build macos-ARM 12 | runs-on: macos-14 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Select Xcode 15.4 17 | run: sudo xcode-select -s /Applications/Xcode_15.4.app 18 | 19 | - name: "Install Homebrew" 20 | run: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 21 | 22 | - name: "Install required packages." 23 | run: | 24 | brew update 25 | brew install ninja libomp 26 | 27 | - name: "Build" 28 | run: | 29 | rm -rf build && mkdir build && cd build 30 | cmake -DCMAKE_C_COMPILER=gcc-14 \ 31 | -DCMAKE_CXX_COMPILER=g++-14 \ 32 | -DCMAKE_BUILD_TYPE=Release \ 33 | -GNinja .. 34 | ninja 35 | -------------------------------------------------------------------------------- /.github/workflows/build-macos.yml: -------------------------------------------------------------------------------- 1 | name: Build macOS 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | name: Build macos 12 | runs-on: macos-13 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Select Xcode 15.2 17 | run: sudo xcode-select -s /Applications/Xcode_15.2.app 18 | 19 | - name: "Install Homebrew" 20 | run: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 21 | 22 | - name: "Install required packages." 23 | run: | 24 | brew update 25 | brew install ninja libomp 26 | 27 | - name: "Build" 28 | run: | 29 | rm -rf build && mkdir build && cd build 30 | cmake -DCMAKE_C_COMPILER=gcc-14 \ 31 | -DCMAKE_CXX_COMPILER=g++-14 \ 32 | -DCMAKE_BUILD_TYPE=Release \ 33 | -GNinja .. 34 | ninja 35 | -------------------------------------------------------------------------------- /.github/workflows/build-wheels-push.yml: -------------------------------------------------------------------------------- 1 | name: build-wheels-push 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | build_wheels: 12 | name: Build wheel for ${{ matrix.buildplat[1] }} ${{ matrix.python }} 13 | runs-on: ${{ matrix.buildplat[0] }} 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | buildplat: 18 | - [ubuntu-latest, manylinux_x86_64] 19 | - [macos-13, macosx_x86_64] 20 | - [macos-14, macosx_arm64] 21 | - [windows-2019, win_amd64] 22 | python: ["cp38", "cp39", "cp310", "cp311", "cp312"] 23 | steps: 24 | - uses: actions/checkout@v3 25 | 26 | - name: Select Xcode for macosx_x86_64 27 | if: matrix.buildplat[1] == 'macosx_x86_64' 28 | run: sudo xcode-select -s /Applications/Xcode_15.2.app 29 | 30 | - name: Select Xcode for macosx_arm64 31 | if: matrix.buildplat[1] == 'macosx_arm64' 32 | run: sudo xcode-select -s /Applications/Xcode_15.4.app 33 | 34 | - name: Install packages for macos 35 | if: ${{ contains(matrix.buildplat[1], 'macosx') }} 36 | run: | 37 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 38 | brew update 39 | brew install ninja libomp 40 | 41 | - uses: actions/setup-python@v5 42 | with: 43 | python-version: 3.8 44 | if: ${{ matrix.python }} == 'cp38' && runner.os == 'macOS' && runner.arch == 'ARM64' 45 | 46 | - name: Build wheels for manylinux_x86_64 47 | if: matrix.buildplat[1] == 'manylinux_x86_64' 48 | uses: pypa/cibuildwheel@v2.19 49 | env: 50 | CIBW_BUILD: ${{ matrix.python }}-${{ matrix.buildplat[1] }} 51 | 52 | - name: Build wheels for macosx_x86_64 53 | if: matrix.buildplat[1] == 'macosx_x86_64' 54 | uses: pypa/cibuildwheel@v2.19 55 | env: 56 | CIBW_BUILD: ${{ matrix.python }}-${{ matrix.buildplat[1] }} 57 | CIBW_ENVIRONMENT_MACOS: > 58 | CC=gcc-14 59 | CXX=g++-14 60 | MACOSX_DEPLOYMENT_TARGET=13.0 61 | 62 | - name: Build wheels for macosx_arm64 63 | if: matrix.buildplat[1] == 'macosx_arm64' 64 | uses: pypa/cibuildwheel@v2.19 65 | env: 66 | CIBW_BUILD: ${{ matrix.python }}-${{ matrix.buildplat[1] }} 67 | CIBW_ENVIRONMENT_MACOS: > 68 | CC=gcc-14 69 | CXX=g++-14 70 | MACOSX_DEPLOYMENT_TARGET=14.0 71 | 72 | - name: Build wheels for windows 73 | if: ${{ contains(matrix.buildplat[1], 'win') }} 74 | uses: pypa/cibuildwheel@v2.19 75 | env: 76 | CIBW_BUILD: ${{ matrix.python }}-${{ matrix.buildplat[1] }} 77 | CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel" 78 | CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair -w {dest_dir} {wheel}" 79 | 80 | - uses: actions/upload-artifact@v4 81 | with: 82 | name: dist-${{ matrix.python }}-${{ matrix.buildplat[1] }} 83 | path: ./wheelhouse/*.whl 84 | 85 | 86 | build_sdist: 87 | name: Build source distribution 88 | runs-on: ubuntu-latest 89 | steps: 90 | - uses: actions/checkout@v3 91 | 92 | - name: Build sdist 93 | shell: bash -l {0} 94 | run: pipx run build --sdist 95 | 96 | - uses: actions/upload-artifact@v4 97 | with: 98 | name: dist-source 99 | path: dist/*.tar.gz 100 | 101 | 102 | upload_pypi: 103 | name: Publish osm2gmns to PyPI 104 | runs-on: ubuntu-latest 105 | needs: [build_wheels, build_sdist] 106 | 107 | environment: 108 | name: pypi 109 | url: https://pypi.org/p/osm2gmns 110 | 111 | permissions: 112 | id-token: write 113 | 114 | steps: 115 | - uses: actions/download-artifact@v4 116 | with: 117 | pattern: dist-* 118 | merge-multiple: true 119 | path: dist 120 | 121 | - name: Publish package distributions to PyPI 122 | uses: pypa/gh-action-pypi-publish@release/v1 -------------------------------------------------------------------------------- /.github/workflows/build-wheels.yml: -------------------------------------------------------------------------------- 1 | name: build-wheels 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build_wheels: 12 | name: Build wheel for ${{ matrix.buildplat[1] }} ${{ matrix.python }} 13 | runs-on: ${{ matrix.buildplat[0] }} 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | buildplat: 18 | - [ubuntu-latest, manylinux_x86_64] 19 | - [macos-13, macosx_x86_64] 20 | - [macos-14, macosx_arm64] 21 | - [windows-2019, win_amd64] 22 | python: ["cp38", "cp39", "cp310", "cp311", "cp312"] 23 | steps: 24 | - uses: actions/checkout@v3 25 | 26 | - name: Select Xcode for macosx_x86_64 27 | if: matrix.buildplat[1] == 'macosx_x86_64' 28 | run: sudo xcode-select -s /Applications/Xcode_15.2.app 29 | 30 | - name: Select Xcode for macosx_arm64 31 | if: matrix.buildplat[1] == 'macosx_arm64' 32 | run: sudo xcode-select -s /Applications/Xcode_15.4.app 33 | 34 | - name: Install packages for macos 35 | if: ${{ contains(matrix.buildplat[1], 'macosx') }} 36 | run: | 37 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 38 | brew update 39 | brew install ninja libomp 40 | 41 | - uses: actions/setup-python@v5 42 | with: 43 | python-version: 3.8 44 | if: ${{ matrix.python }} == 'cp38' && runner.os == 'macOS' && runner.arch == 'ARM64' 45 | 46 | - name: Build wheels for manylinux_x86_64 47 | if: matrix.buildplat[1] == 'manylinux_x86_64' 48 | uses: pypa/cibuildwheel@v2.19 49 | env: 50 | CIBW_BUILD: ${{ matrix.python }}-${{ matrix.buildplat[1] }} 51 | 52 | - name: Build wheels for macosx_x86_64 53 | if: matrix.buildplat[1] == 'macosx_x86_64' 54 | uses: pypa/cibuildwheel@v2.19 55 | env: 56 | CIBW_BUILD: ${{ matrix.python }}-${{ matrix.buildplat[1] }} 57 | CIBW_ENVIRONMENT_MACOS: > 58 | CC=gcc-14 59 | CXX=g++-14 60 | MACOSX_DEPLOYMENT_TARGET=13.0 61 | 62 | - name: Build wheels for macosx_arm64 63 | if: matrix.buildplat[1] == 'macosx_arm64' 64 | uses: pypa/cibuildwheel@v2.19 65 | env: 66 | CIBW_BUILD: ${{ matrix.python }}-${{ matrix.buildplat[1] }} 67 | CIBW_ENVIRONMENT_MACOS: > 68 | CC=gcc-14 69 | CXX=g++-14 70 | MACOSX_DEPLOYMENT_TARGET=14.0 71 | 72 | - name: Build wheels for windows 73 | if: ${{ contains(matrix.buildplat[1], 'win') }} 74 | uses: pypa/cibuildwheel@v2.19 75 | env: 76 | CIBW_BUILD: ${{ matrix.python }}-${{ matrix.buildplat[1] }} 77 | CIBW_BEFORE_BUILD_WINDOWS: "pip install delvewheel" 78 | CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel repair -w {dest_dir} {wheel}" 79 | 80 | - uses: actions/upload-artifact@v4 81 | with: 82 | name: dist-${{ matrix.python }}-${{ matrix.buildplat[1] }} 83 | path: ./wheelhouse/*.whl 84 | 85 | 86 | build_sdist: 87 | name: Build source distribution 88 | runs-on: ubuntu-latest 89 | steps: 90 | - uses: actions/checkout@v3 91 | 92 | - name: Build sdist 93 | shell: bash -l {0} 94 | run: pipx run build --sdist 95 | 96 | - uses: actions/upload-artifact@v4 97 | with: 98 | name: dist-source 99 | path: dist/*.tar.gz -------------------------------------------------------------------------------- /.github/workflows/build-windows.yml: -------------------------------------------------------------------------------- 1 | name: Build Windows 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | name: Build Windows 12 | runs-on: windows-2019 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | # - name: Install Ninja 17 | # run: choco install ninja 18 | 19 | # - name: Build 20 | # run: | 21 | # mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -GNinja .. && ninja 22 | 23 | - name: Build 24 | run: | 25 | mkdir build && cd build && cmake .. && cmake --build . --config Release 26 | 27 | - name: Upload build directory as artifact 28 | if: failure() 29 | uses: actions/upload-artifact@v4 30 | with: 31 | name: build-directory 32 | path: build/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/* 2 | .idea/* 3 | .DS_Store 4 | *.pyc 5 | .pypirc 6 | cmake-build*/ 7 | [Bb]in/ 8 | compile_commands.json 9 | dev/maps/ 10 | osm2gmns/* 11 | !osm2gmns/*.py 12 | !osm2gmns/dlls 13 | 14 | 15 | # Byte-compiled / optimized / DLL files 16 | *__pycache__/* 17 | *.py[cod] 18 | *$py.class 19 | 20 | # Distribution / packaging 21 | .Python 22 | build*/ 23 | develop-eggs/ 24 | dist/ 25 | downloads/ 26 | eggs/ 27 | .eggs/ 28 | lib/ 29 | lib64/ 30 | parts/ 31 | sdist/ 32 | var/ 33 | wheels/ 34 | share/python-wheels/ 35 | *.egg-info/ 36 | .installed.cfg 37 | *.egg 38 | sources/* 39 | MANIFEST 40 | 41 | # PyInstaller 42 | # Usually these files are written by a python script from a template 43 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 44 | *.manifest 45 | *.spec 46 | 47 | # Installer logs 48 | pip-log.txt 49 | pip-delete-this-directory.txt 50 | 51 | # Unit test / coverage reports 52 | htmlcov/ 53 | .tox/ 54 | .nox/ 55 | .coverage 56 | .coverage.* 57 | .cache 58 | nosetests.xml 59 | coverage.xml 60 | *.cover 61 | *.py,cover 62 | .hypothesis/ 63 | .pytest_cache/ 64 | cover/ 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | .pybuilder/ 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # IPython 77 | profile_default/ 78 | ipython_config.py 79 | 80 | # pyenv 81 | # For a library or package, you might want to ignore these files since the code is 82 | # intended to run in multiple environments; otherwise, check them in: 83 | # .python-version 84 | 85 | # pipenv 86 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 87 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 88 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 89 | # install all needed dependencies. 90 | #Pipfile.lock 91 | 92 | # poetry 93 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 94 | # This is especially recommended for binary packages to ensure reproducibility, and is more 95 | # commonly ignored for libraries. 96 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 97 | #poetry.lock 98 | 99 | # pdm 100 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 101 | #pdm.lock 102 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 103 | # in version control. 104 | # https://pdm.fming.dev/#use-with-ide 105 | .pdm.toml 106 | 107 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 108 | __pypackages__/ 109 | 110 | # Celery stuff 111 | celerybeat-schedule 112 | celerybeat.pid 113 | 114 | # SageMath parsed files 115 | *.sage.py 116 | 117 | # Environments 118 | .env 119 | .venv 120 | env/ 121 | venv/ 122 | ENV/ 123 | env.bak/ 124 | venv.bak/ 125 | 126 | # mkdocs documentation 127 | /site 128 | 129 | # mypy 130 | .mypy_cache/ 131 | .dmypy.json 132 | dmypy.json 133 | 134 | # Pyre type checker 135 | .pyre/ 136 | 137 | # pytype static type analyzer 138 | .pytype/ 139 | 140 | # Cython debug symbols 141 | cython_debug/ 142 | 143 | # PyCharm 144 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 145 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 146 | # and can be added to the global gitignore or merged into this file. For a more nuclear 147 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | fail_fast: false 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v4.5.0 5 | hooks: 6 | - id: trailing-whitespace 7 | - id: check-added-large-files 8 | - id: check-yaml 9 | exclude: ".clang-*" 10 | - repo: https://github.com/jiawlu/pre-commit-hooks 11 | rev: 49cddc161d4fe41ee9f16922f2c0e634b0a115ff 12 | hooks: 13 | - id: clang-format 14 | args: [--style=file, -i] 15 | - id: clang-tidy 16 | args: [-p=./cmake-build-debug, --fix-errors] -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file for Sphinx projects 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | # Required 5 | version: 2 6 | 7 | # Set the OS, Python version and other tools you might need 8 | build: 9 | os: ubuntu-20.04 10 | tools: 11 | python: "3.11" 12 | # You can also specify other tool versions: 13 | # nodejs: "20" 14 | # rust: "1.70" 15 | # golang: "1.20" 16 | 17 | # Build documentation in the "docs/" directory with Sphinx 18 | sphinx: 19 | configuration: docs/source/conf.py 20 | # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs 21 | # builder: "dirhtml" 22 | # Fail on all warnings to avoid broken references 23 | # fail_on_warning: true 24 | 25 | # Optionally build your docs in additional formats such as PDF and ePub 26 | # formats: 27 | # - pdf 28 | # - epub 29 | 30 | # Optional but recommended, declare the Python requirements required 31 | # to build your documentation 32 | # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 33 | python: 34 | install: 35 | - requirements: docs/source/requirements.txt -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.25) 2 | project(osm2gmns LANGUAGES C CXX) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_CXX_STANDARD_REQUIRED True) 6 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 7 | 8 | 9 | #include(cmake/StandardSettings.cmake) 10 | #include(cmake/rootutils.cmake) 11 | message("\nStarted CMake for ${PROJECT_NAME} v${PROJECT_VERSION}...\n") 12 | file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/config) 13 | configure_file(${PROJECT_SOURCE_DIR}/config.h.in ${PROJECT_BINARY_DIR}/config/config.h) 14 | 15 | option(CMAKE_INSTALL_LOCAL_ONLY "Install only locally" ON) 16 | # set(CMAKE_INSTALL_LIBDIR "lib") 17 | # set(CMAKE_INSTALL_LIBDIR ".") 18 | set(BUILD_SHARED_LIBS OFF) 19 | set(ENABLE_SHARED_LIB OFF CACHE BOOL "Build libbz2 as a shared library" FORCE) 20 | set(ENABLE_STATIC_LIB ON CACHE BOOL "Build libbz2 in static mode also" FORCE) 21 | set(EXPAT_SHARED_LIBS OFF) 22 | set(EXPAT_MSVC_STATIC_CRT ON) 23 | 24 | # find_program(iwyu_path NAMES include-what-you-use iwyu REQUIRED) 25 | SET(CMAKE_EXPORT_COMPILE_COMMANDS ON) 26 | 27 | include(FetchContent) 28 | set(FETCHCONTENT_QUIET OFF) 29 | 30 | SET(ABSL_PROPAGATE_CXX_STD ON) 31 | SET(ABSL_BUILD_TESTING OFF) 32 | #set(ABSL_ENABLE_INSTALL ON) 33 | 34 | message("CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") 35 | 36 | find_package(OpenMP REQUIRED) 37 | if (OPENMP_FOUND) 38 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") 39 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") 40 | endif() 41 | set(Required_Libraries ${Required_Libraries} OpenMP::OpenMP_CXX) 42 | 43 | FetchContent_Declare( 44 | absl 45 | GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git 46 | GIT_TAG 20230125.3 47 | SYSTEM 48 | ) 49 | FetchContent_MakeAvailable(absl) 50 | include_directories(SYSTEM ${absl_SOURCE_DIR}) 51 | set(Required_Libraries ${Required_Libraries} 52 | absl::base 53 | absl::algorithm 54 | absl::log 55 | absl::log_initialize 56 | absl::hash 57 | absl::memory 58 | absl::numeric 59 | absl::strings 60 | absl::raw_hash_map 61 | ) 62 | 63 | FetchContent_Declare( 64 | protozero 65 | GIT_REPOSITORY https://github.com/mapbox/protozero.git 66 | GIT_TAG v1.7.1 67 | SYSTEM 68 | ) 69 | FetchContent_Populate(protozero) 70 | include_directories(SYSTEM ${protozero_SOURCE_DIR}/include) 71 | 72 | FetchContent_Declare( 73 | bzip2 74 | GIT_REPOSITORY https://gitlab.com/bzip2/bzip2.git 75 | GIT_TAG master 76 | SYSTEM 77 | ) 78 | FetchContent_MakeAvailable(bzip2) 79 | set(Required_Libraries ${Required_Libraries} bz2_static) 80 | include_directories(SYSTEM ${bzip2_SOURCE_DIR}) 81 | 82 | FetchContent_Declare( 83 | expat 84 | GIT_REPOSITORY https://github.com/libexpat/libexpat.git 85 | GIT_TAG R_2_6_2 86 | SOURCE_SUBDIR expat 87 | SYSTEM 88 | ) 89 | FetchContent_MakeAvailable(expat) 90 | include_directories(SYSTEM ${expat_SOURCE_DIR}/expat/lib) 91 | include_directories(SYSTEM ${expat_BINARY_DIR}) 92 | if (WIN32) 93 | set(Required_Libraries ${Required_Libraries} expat) 94 | elseif (UNIX) 95 | set(Required_Libraries ${Required_Libraries} ${expat_BINARY_DIR}/libexpat.a) 96 | else () 97 | message(FATAL_ERROR "Unsupported operating system") 98 | endif () 99 | # if (CMAKE_SYSTEM_NAME STREQUAL "Windows") 100 | # set(Required_Libraries ${Required_Libraries} libexpat) 101 | # else () 102 | # set(Required_Libraries ${Required_Libraries} expat) 103 | # endif() 104 | # link_directories(${expat_BINARY_DIR}) 105 | 106 | FetchContent_Declare( 107 | z 108 | GIT_REPOSITORY https://github.com/madler/zlib.git 109 | GIT_TAG v1.3.1 110 | SYSTEM 111 | ) 112 | FetchContent_MakeAvailable(z) 113 | include_directories(BEFORE SYSTEM ${z_SOURCE_DIR}) 114 | include_directories(BEFORE SYSTEM ${z_BINARY_DIR}) 115 | if (WIN32) 116 | set(Required_Libraries ${Required_Libraries} zlibstatic) 117 | elseif (UNIX) 118 | set(Required_Libraries ${Required_Libraries} ${z_BINARY_DIR}/libz.a) 119 | else () 120 | message(FATAL_ERROR "Unsupported operating system") 121 | endif () 122 | # link_directories(BEFORE ${z_BINARY_DIR}) 123 | 124 | FetchContent_Declare( 125 | osmium 126 | GIT_REPOSITORY https://github.com/osmcode/libosmium.git 127 | GIT_TAG v2.20.0 128 | SYSTEM 129 | ) 130 | FetchContent_Populate(osmium) 131 | include_directories(SYSTEM ${osmium_SOURCE_DIR}/include) 132 | 133 | 134 | FetchContent_Declare( 135 | geos 136 | GIT_REPOSITORY https://github.com/libgeos/geos.git 137 | GIT_TAG 3.12.2 138 | SYSTEM 139 | ) 140 | FetchContent_MakeAvailable(geos) 141 | set(Required_Libraries ${Required_Libraries} GEOS::geos) 142 | include_directories(SYSTEM ${geos_SOURCE_DIR}/include) 143 | include_directories(SYSTEM ${geos_BINARY_DIR}/include) 144 | add_definitions(-DUSE_UNSTABLE_GEOS_CPP_API) 145 | 146 | FetchContent_Declare( 147 | fast-cpp-csv-parser 148 | GIT_REPOSITORY https://github.com/ben-strasser/fast-cpp-csv-parser.git 149 | GIT_TAG master 150 | SYSTEM 151 | OVERRIDE_FIND_PACKAGE 152 | ) 153 | FetchContent_Populate(fast-cpp-csv-parser) 154 | include_directories(SYSTEM ${fast-cpp-csv-parser_SOURCE_DIR}) 155 | 156 | FetchContent_Declare( 157 | geographiclib 158 | GIT_REPOSITORY https://github.com/geographiclib/geographiclib.git 159 | GIT_TAG v2.4 160 | SYSTEM 161 | ) 162 | FetchContent_MakeAvailable(geographiclib) 163 | set(Required_Libraries ${Required_Libraries} GeographicLib::GeographicLib) 164 | include_directories(SYSTEM ${geographiclib_SOURCE_DIR}/include) 165 | 166 | add_subdirectory(src) 167 | 168 | option(OSM2GMNS_BUILD_EXE "Build osm2gmns executable" ON) 169 | if (OSM2GMNS_BUILD_EXE) 170 | add_executable(osm2gmns osm2gmns_main.cpp) 171 | target_link_libraries(osm2gmns osm2gmns_core) 172 | endif () 173 | 174 | option(OSM2GMNS_BUILD_LIB "Build osm2gmns library" ON) 175 | if (OSM2GMNS_BUILD_LIB) 176 | add_library(osm2gmnslib SHARED osm2gmns_lib.cpp) 177 | target_link_libraries(osm2gmnslib osm2gmns_core) 178 | set_target_properties(osm2gmnslib PROPERTIES OUTPUT_NAME osm2gmns) 179 | if (WIN32) 180 | set_target_properties(osm2gmnslib PROPERTIES 181 | COMPILE_FLAGS "-static-libgcc -static-libstdc++" 182 | LINK_FLAGS "-static-libgcc -static-libstdc++") 183 | endif () 184 | endif () 185 | 186 | option(OSM2GMNS_BUILD_DEV "Build executable for development" OFF) 187 | if (OSM2GMNS_BUILD_DEV) 188 | add_subdirectory(dev) 189 | endif () 190 | 191 | 192 | # if(UNIX) 193 | # if(APPLE) 194 | # if (BUILD_OSM2GMNS_EXE) 195 | # set_target_properties(osm2gmns PROPERTIES INSTALL_RPATH "@executable_path/lib") 196 | # endif () 197 | # if (BUILD_OSM2GMNS_LIB) 198 | # set_target_properties(osm2gmnslib PROPERTIES INSTALL_RPATH "@loader_path/lib") 199 | # endif () 200 | # else() 201 | # if (BUILD_OSM2GMNS_EXE) 202 | # set_target_properties(osm2gmns PROPERTIES INSTALL_RPATH "$ORIGIN/lib") 203 | # endif () 204 | # if (BUILD_OSM2GMNS_LIB) 205 | # set_target_properties(osm2gmnslib PROPERTIES INSTALL_RPATH "$ORIGIN/lib") 206 | # endif () 207 | # endif() 208 | # elseif(WIN32) 209 | # # Copy dependencies to the same directory as the executable and library 210 | # add_custom_command(TARGET YourExecutable POST_BUILD 211 | # COMMAND ${CMAKE_COMMAND} -E copy_if_different 212 | # $ 213 | # $ 214 | # ) 215 | # add_custom_command(TARGET YourLibrary POST_BUILD 216 | # COMMAND ${CMAKE_COMMAND} -E copy_if_different 217 | # $ 218 | # $ 219 | # ) 220 | # # Install dependencies alongside the executable and library 221 | # install(FILES $ DESTINATION ${INSTALL_ROOT_DIR}) 222 | # endif() 223 | 224 | 225 | if (OSM2GMNS_BUILD_EXE) 226 | install(TARGETS osm2gmns RUNTIME DESTINATION .) 227 | endif () 228 | if (OSM2GMNS_BUILD_LIB) 229 | if (WIN32) 230 | install(TARGETS osm2gmnslib RUNTIME DESTINATION .) 231 | else () 232 | install(TARGETS osm2gmnslib LIBRARY DESTINATION .) 233 | endif () 234 | endif () 235 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | osm2gmns 2 | ==================================== 3 | | **Authors**: `Jiawei Lu`_, `Xuesong (Simon) Zhou`_ 4 | | **Email**: lujiaweiwk@gmail.com, xzhou74@asu.edu 5 | 6 | 7 | osm2gmns is a high-performance Python package designed to convert `OpenStreetMap`_ (OSM) 8 | data into standardized transportation networks. Leveraging a C++ core wrapped in an 9 | intuitive Python interface, osm2gmns offers both computational speed and ease of use. 10 | **It empowers researchers and practitioners to generate detailed, multi-modal networks (driving, cycling, walking, railway, aeroway) for any region worldwide with minimal coding effort**. 11 | 12 | The package outputs networks primarily in the `GMNS`_ (General Modeling Network Specification) 13 | format, promoting interoperability and simplifying data exchange within the transportation 14 | modeling community. 15 | 16 | .. code-block:: python 17 | 18 | >>> import osm2gmns as og 19 | >>> net = og.getNetFromFile('map.osm') 20 | >>> og.outputNetToCSV(net) 21 | 22 | 23 | *This v1.x version introduces significant architectural changes, performance improvements, and new features compared to* `v0.x`_, *and is not fully backward compatible*. 24 | 25 | - *We encourage users to adopt v1.x for the latest capabilities and ongoing development*. 26 | - *v0.x will only receive critical bug fixes moving forward*. 27 | - *Functionality like Multi-Resolution Modeling (MRM), currently available only in v0.x, is planned for future v1.x releases*. 28 | 29 | 30 | Citation 31 | ==================================== 32 | 33 | If osm2gmns contributes to your research, please cite the following publication: 34 | 35 | Lu, J., & Zhou, X.S. (2023). Virtual track networks: A hierarchical modeling framework and 36 | open-source tools for simplified and efficient connected and automated mobility (CAM) system 37 | design based on general modeling network specification (GMNS). Transportation Research 38 | Part C: Emerging Technologies, 153, 104223. [`link`_] 39 | 40 | 41 | User's guide 42 | ==================================== 43 | 44 | Users are referred to `user's guide`_ for a detailed introduction of osm2gmns. 45 | 46 | 47 | 48 | .. _`Jiawei Lu`: https://www.linkedin.com/in/jiawlu/ 49 | .. _`Xuesong (Simon) Zhou`: https://www.linkedin.com/in/xzhou/ 50 | .. _`OpenStreetMap`: https://www.openstreetmap.org 51 | .. _`GMNS`: https://github.com/zephyr-data-specs/GMNS 52 | .. _`v0.x`: https://osm2gmns.readthedocs.io/en/v0.x 53 | .. _`link`: https://doi.org/10.1016/j.trc.2023.104223 54 | .. _`user's guide`: https://osm2gmns.readthedocs.io -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | #ifndef OSM2GMNS_CONFIG_HPP 2 | #define OSM2GMNS_CONFIG_HPP 3 | 4 | #define PROJECT_NAME "@PROJECT_NAME@" 5 | #define PROJECT_VERSION "@PROJECT_VERSION@" 6 | #define PROJECT_VERSION_MAJOR "@PROJECT_VERSION_MAJOR@" 7 | #define PROJECT_VERSION_MINOR "@PROJECT_VERSION_MINOR@" 8 | #define PROJECT_VERSION_PATCH "@PROJECT_VERSION_PATCH@" 9 | 10 | #endif // OSM2GMNS_CONFIG_HPP -------------------------------------------------------------------------------- /dev/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(osm2gmns_dev) 2 | 3 | add_executable(osm2gmns_dev osm2gmns_dev.cpp) 4 | 5 | target_include_directories(osm2gmns_dev PRIVATE ${PROJECT_SOURCE_DIR}/../src) 6 | target_link_libraries(osm2gmns_dev PRIVATE osm2gmns_core) 7 | -------------------------------------------------------------------------------- /dev/osm2gmns_dev.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jiawei Lu on 2/17/23. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "config.h" 15 | #include "functions.h" 16 | #include "io.h" 17 | #include "networks.h" 18 | #include "osmconfig.h" 19 | 20 | int main(int /*argc*/, char* /*argv*/[]) { 21 | try { 22 | absl::InitializeLog(); 23 | absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo); 24 | 25 | // const auto map_folder = std::filesystem::path("maps/Texas"); 26 | // const auto map_folder = std::filesystem::path("maps/Dallas"); 27 | // const auto map_folder = std::filesystem::path("maps/Debug/Wickenburg"); 28 | const auto map_folder = std::filesystem::path("maps/Debug/AZ"); 29 | 30 | // const std::string map_filename = "map.osm.pbf"; 31 | // const std::string map_filename = "northwest-latest.osm.pbf"; 32 | // const std::string map_filename = "map.osm"; 33 | const std::string map_filename = "arizona.pbf"; 34 | 35 | const auto* osm_parsing_config = 36 | new OsmParsingConfig{{"highway", "ref"}, {"oneway", "junction", "lanes"}, {"building", "type"}}; 37 | 38 | Network* network = getNetFromFile(map_folder / map_filename, 39 | {ModeType::AUTO, ModeType::BIKE, ModeType::RAILWAY, ModeType::AEROWAY}, 40 | {HighWayLinkType::MOTORWAY, HighWayLinkType::TRUNK, HighWayLinkType::PRIMARY, 41 | HighWayLinkType::SECONDARY, HighWayLinkType::TERTIARY}, 42 | {HighWayLinkType::RESIDENTIAL}, true, 1.0, osm_parsing_config, true); 43 | 44 | // consolidateComplexIntersections(network, false, map_folder / "intersection.csv"); 45 | 46 | // generateNodeActivityInfo(network); 47 | 48 | // fillLinkAttributesWithDefaultValues(network, true, {}, false, {{HighWayLinkType::MOTORWAY, 1}}, true, 49 | // {{HighWayLinkType::MOTORWAY, 1}}); 50 | 51 | outputNetToCSV(network, map_folder); 52 | 53 | delete network; 54 | } catch (const std::exception& e) { 55 | std::cerr << e.what() << '\n'; 56 | return 1; 57 | } 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /dev/osm2gmns_dev.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 4 | sys.path.append(parent_dir) 5 | 6 | import osm2gmns as og 7 | 8 | map_folder = r'dev/maps/yuba' 9 | 10 | map_name = 'map.osm.pbf' 11 | 12 | net = og.getNetFromFile(os.path.join(map_folder, map_name), 13 | link_types=['motorway', 'trunk', 'primary', 'secondary'], 14 | connector_link_types=['tertiary'], POI=True) 15 | 16 | og.consolidateComplexIntersections(net, auto_identify=True) 17 | 18 | og.generateNodeActivityInfo(net) 19 | 20 | og.fillLinkAttributesWithDefaultValues(net, default_lanes=False, default_lanes_dict={}, 21 | default_speed=True, default_speed_dict={}, 22 | default_capacity=True, default_capacity_dict={'primary':1289}) 23 | 24 | og.outputNetToCSV(net, map_folder) 25 | 26 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/source/_images/bbbike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiawlu/OSM2GMNS/2f8fa8552eb566ee55a5e199b31b28fd44da15ce/docs/source/_images/bbbike.png -------------------------------------------------------------------------------- /docs/source/_images/consolidation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiawlu/OSM2GMNS/2f8fa8552eb566ee55a5e199b31b28fd44da15ce/docs/source/_images/consolidation.png -------------------------------------------------------------------------------- /docs/source/_images/geofabrik.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiawlu/OSM2GMNS/2f8fa8552eb566ee55a5e199b31b28fd44da15ce/docs/source/_images/geofabrik.png -------------------------------------------------------------------------------- /docs/source/_images/mrnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiawlu/OSM2GMNS/2f8fa8552eb566ee55a5e199b31b28fd44da15ce/docs/source/_images/mrnet.png -------------------------------------------------------------------------------- /docs/source/_images/net_asu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiawlu/OSM2GMNS/2f8fa8552eb566ee55a5e199b31b28fd44da15ce/docs/source/_images/net_asu.png -------------------------------------------------------------------------------- /docs/source/_images/net_az.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiawlu/OSM2GMNS/2f8fa8552eb566ee55a5e199b31b28fd44da15ce/docs/source/_images/net_az.png -------------------------------------------------------------------------------- /docs/source/_images/net_dfw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiawlu/OSM2GMNS/2f8fa8552eb566ee55a5e199b31b28fd44da15ce/docs/source/_images/net_dfw.png -------------------------------------------------------------------------------- /docs/source/_images/net_london.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiawlu/OSM2GMNS/2f8fa8552eb566ee55a5e199b31b28fd44da15ce/docs/source/_images/net_london.png -------------------------------------------------------------------------------- /docs/source/_images/net_melbourne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiawlu/OSM2GMNS/2f8fa8552eb566ee55a5e199b31b28fd44da15ce/docs/source/_images/net_melbourne.png -------------------------------------------------------------------------------- /docs/source/_images/net_midwest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiawlu/OSM2GMNS/2f8fa8552eb566ee55a5e199b31b28fd44da15ce/docs/source/_images/net_midwest.png -------------------------------------------------------------------------------- /docs/source/_images/net_nanjing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiawlu/OSM2GMNS/2f8fa8552eb566ee55a5e199b31b28fd44da15ce/docs/source/_images/net_nanjing.png -------------------------------------------------------------------------------- /docs/source/_images/net_yangzhou.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiawlu/OSM2GMNS/2f8fa8552eb566ee55a5e199b31b28fd44da15ce/docs/source/_images/net_yangzhou.png -------------------------------------------------------------------------------- /docs/source/_images/osm_id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiawlu/OSM2GMNS/2f8fa8552eb566ee55a5e199b31b28fd44da15ce/docs/source/_images/osm_id.png -------------------------------------------------------------------------------- /docs/source/_images/osmhp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiawlu/OSM2GMNS/2f8fa8552eb566ee55a5e199b31b28fd44da15ce/docs/source/_images/osmhp.png -------------------------------------------------------------------------------- /docs/source/_images/poi1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiawlu/OSM2GMNS/2f8fa8552eb566ee55a5e199b31b28fd44da15ce/docs/source/_images/poi1.png -------------------------------------------------------------------------------- /docs/source/acknowledgement.rst: -------------------------------------------------------------------------------- 1 | =============== 2 | Acknowledgement 3 | =============== 4 | 5 | This project is partially supported by National Science Foundation - United States under 6 | Grant No. CMMI 1663657 "Collaborative Research: Real-time Management of Large Fleets of 7 | Self-Driving Vehicles Using Virtual CyberTracks" 8 | 9 | The second author also thanks for the early support from FHWA project titled "The Effective 10 | Integration of Analysis, Modeling, and Simulation Tools - AMS Data hub Concept of Operations". 11 | https://www.fhwa.dot.gov/publications/research/operations/13036/004.cfm 12 | 13 | Many thanks for GMNS specification efforts led by Scott Smith and Ian Berg from Volpe Center, 14 | USDOT. Their TRB poster can be found at https://github.com/zephyr-data-specs/GMNS/blob/TRB/TRBPoster_22-02127.pdf 15 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | sys.path.insert(0, os.path.abspath('../..')) 16 | 17 | # autodoc_mock_imports = [ 18 | # "shapely", 19 | # "osmium", 20 | # "numpy" 21 | # ] 22 | # -- Project information ----------------------------------------------------- 23 | 24 | project = 'osm2gmns' 25 | copyright = '2020-, Jiawei Lu, Xuesong (Simon) Zhou' 26 | author = 'Jiawei Lu, Xuesong (Simon) Zhou' 27 | 28 | # The full version, including alpha/beta/rc tags 29 | release = '1.0.1' 30 | 31 | 32 | # -- General configuration --------------------------------------------------- 33 | 34 | # Add any Sphinx extension module names here, as strings. They can be 35 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 36 | # ones. 37 | # extensions = ['sphinx_rtd_theme', 'sphinx.ext.autodoc', "sphinx.ext.napoleon"] 38 | extensions = ['sphinx.ext.autodoc', "sphinx.ext.napoleon"] 39 | 40 | # Add any paths that contain templates here, relative to this directory. 41 | # templates_path = ['_templates'] 42 | 43 | # List of patterns, relative to source directory, that match files and 44 | # directories to ignore when looking for source files. 45 | # This pattern also affects html_static_path and html_extra_path. 46 | exclude_patterns = [] 47 | 48 | 49 | # -- Options for HTML output ------------------------------------------------- 50 | 51 | # The theme to use for HTML and HTML Help pages. See the documentation for 52 | # a list of builtin themes. 53 | # 54 | html_theme = 'sphinx_rtd_theme' 55 | # html_theme = 'bootstrap' 56 | 57 | 58 | # Add any paths that contain custom static files (such as style sheets) here, 59 | # relative to this directory. They are copied after the builtin static files, 60 | # so a file named "default.css" will overwrite the builtin "default.css". 61 | # html_static_path = ['_static'] -------------------------------------------------------------------------------- /docs/source/get-osm-data.rst: -------------------------------------------------------------------------------- 1 | .. _section-get-osm-data: 2 | 3 | ============ 4 | Get OSM Data 5 | ============ 6 | 7 | osm2gmns requires OpenStreetMap data as input. 8 | Four options are provided to download OSM data. 9 | Users can choose the one that best suits their needs. 10 | 11 | .. note:: 12 | 13 | - The file formats of map data supported in osm2gmns include ``.osm``, ``.xml``, and ``.pbf``. 14 | 15 | 16 | 1) OpenStreetMap Homepage (Suitable for Smaller Areas) 17 | 18 | Go to the OpenStreetMap `homepage`_. 19 | Navigate to your area of interest. 20 | Click the "Export" button at the top. You can adjust the bounding box manually if needed. 21 | Click the blue "Export" button in the left panel to download the data. 22 | 23 | Note that if the target area is too large, you may get an error message: "You requested too many nodes 24 | (limit is 50000). Either request a smaller area, or use planet.osm". In this case, you can always click 25 | ``Overpass API`` to download the network you need via a mirror site. 26 | 27 | .. figure:: _images/osmhp.png 28 | :name: osmhp_pic 29 | :align: center 30 | :width: 100% 31 | 32 | Downloading data directly from the OpenStreetMap website export tool 33 | 34 | 35 | 2) Geofabrik (Recommended for Administrative Regions) 36 | 37 | Visit the `Geofabrik`_ download website. 38 | Navigate through the continental and regional links to find your area. 39 | Download the data extract for your specific administrative region. 40 | 41 | Generally, there are multiple types of file format for users to choose when downloading map data. 42 | 43 | .. figure:: _images/geofabrik.png 44 | :name: geofabrik_pic 45 | :align: center 46 | :width: 100% 47 | 48 | Downloading pre-packaged regional extracts from Geofabrik 49 | 50 | 51 | 3) BBBike (Custom Polygonal Areas) 52 | 53 | Go to the `BBBike`_ download website. 54 | Use the map interface to draw a custom polygon around your exact area of interest. 55 | Enter your email address and select the desired format. 56 | Submit the request; you will receive an email with a download link when the extract is ready. 57 | 58 | .. figure:: _images/bbbike.png 59 | :name: bbbike_pic 60 | :align: center 61 | :width: 100% 62 | 63 | Selecting a custom area using the BBBike extract service 64 | 65 | 66 | 4) Overpass API (Programmatic Download by Relation ID) 67 | 68 | osm2gmns includes a function ``downloadOSMData`` to download data directly via the Overpass API, 69 | using an OSM Relation ID. 70 | 71 | Go to the OpenStreetMap `homepage`_. 72 | Search for the administrative region or feature (can be a state, a city, or even an university). 73 | Click on the correct search result. 74 | The URL in your browser will contain the relation ID. The number is the ID. You can also often find it listed in the object's details/tags on the left panel. 75 | 76 | .. figure:: _images/osm_id.png 77 | :name: osm_id 78 | :align: center 79 | :width: 100% 80 | 81 | Finding the Relation ID for a region on OpenStreetMap 82 | 83 | Use the following code to download the OSM data for the region using the relation ID. 84 | 85 | .. code-block:: python 86 | 87 | >>> import osm2gmns as og 88 | 89 | >>> og.downloadOSMData(110833, 'tempe.osm') 90 | 91 | 92 | .. _`homepage`: https://www.openstreetmap.org 93 | .. _`Geofabrik`: https://download.geofabrik.de/ 94 | .. _`BBBike`: https://extract.bbbike.org/ -------------------------------------------------------------------------------- /docs/source/gmns.rst: -------------------------------------------------------------------------------- 1 | =========== 2 | GMNS 3 | =========== 4 | 5 | General Modeling Network Specification (`GMNS`_), proposed by the `Zephyr Foundation`_, 6 | defines a common human and machine readable format for sharing routable road network files. 7 | It is designed to be used in multi-modal static and dynamic transportation planning and 8 | operations models. It will facilitate the sharing of tools and data sources by modelers. 9 | For additional information on GMNS goals, history and requirements, please see the `wiki`_. 10 | 11 | 12 | GMNS (version 0.92) includes the following features for use in static models: 13 | 14 | - Configuration information and use definitions. 15 | - Node and link files, to establish a routable network. 16 | 17 | For dynamic models, GMNS (version 0.92) includes the following optional additional features: 18 | 19 | - A segment file, with information that overrides the characteristics of a portion of a link. 20 | - A lane file that allocates portions of the right-of-way. Lanes include travel lanes used by motor vehicles. They may also optionally include bike lanes, parking lanes, and shoulders. 21 | - A segment_lane file that specifies additional lanes, dropped lanes, or changes to lane properties on a segment of a link. 22 | - A movement file that specifies how inbound and outbound lanes connect at an intersection. 23 | - Link, segment, lane and movement time-of-day (TOD) files, that allocate usage of network elements by time-of-day and day-of-week. 24 | - Signal phase and timing files, for basic implementation of traffic signals. 25 | 26 | osm2gmns uses GMNS as the standard when processing and manipulating networks, and thus any 27 | network in GMNS format is fully compatible with osm2gmns. 28 | 29 | 30 | .. _`GMNS`: https://github.com/zephyr-data-specs/GMNS 31 | .. _`Zephyr Foundation`: https://zephyrtransport.org 32 | .. _`wiki`: https://github.com/zephyr-data-specs/GMNS/wiki 33 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | osm2gmns 2 | ==================================== 3 | | **Authors**: `Jiawei Lu`_, `Xuesong (Simon) Zhou`_ 4 | | **Email**: lujiaweiwk@gmail.com, xzhou74@asu.edu 5 | 6 | 7 | osm2gmns is a high-performance Python package designed to convert `OpenStreetMap`_ (OSM) 8 | data into standardized transportation networks. Leveraging a C++ core wrapped in an 9 | intuitive Python interface, osm2gmns offers both computational speed and ease of use. 10 | **It empowers researchers and practitioners to generate detailed, multi-modal networks (driving, cycling, walking, railway, aeroway) for any region worldwide with minimal coding effort**. 11 | 12 | The package outputs networks primarily in the `GMNS`_ (General Modeling Network Specification) 13 | format, promoting interoperability and simplifying data exchange within the transportation 14 | modeling community. 15 | 16 | .. code-block:: python 17 | 18 | >>> import osm2gmns as og 19 | >>> net = og.getNetFromFile('map.osm') 20 | >>> og.outputNetToCSV(net) 21 | 22 | 23 | .. note:: 24 | **Welcome to the osm2gmns v1.x Documentation!** 25 | 26 | This v1.x version introduces significant architectural changes, performance improvements, and 27 | new features compared to `v0.x`_, and is **not fully backward compatible**. 28 | 29 | - We encourage users to adopt v1.x for the latest capabilities and ongoing development. 30 | - v0.x will only receive critical bug fixes moving forward. 31 | - Functionality like Multi-Resolution Modeling (MRM), currently available only in v0.x, is planned for future v1.x releases. 32 | 33 | 34 | Key Features 35 | ==================================== 36 | 37 | 1. Comprehensive Network Modeling 38 | 39 | - Worldwide Network: Generate networks from OpenStreetMap for any region globally 40 | - Multi-Modal Support: Generate networks for vehicles, bicycles, pedestrians, railways, and aeroways 41 | - GMNS Compatibility: Standardized output format for interoperability with other tools 42 | 43 | 2. Advanced Functionality 44 | 45 | - Intersection Consolidation: Simplifies complex junctions for various modeling needs 46 | - Movement Generation: Creates turning movements at intersections 47 | - Directed Network Generation: Automatic creation of directional links for bidirectional roadways 48 | - Traffic Zone Creation: Supports origin-destination modeling 49 | - Ready-to-Use Networks: Automatic inference of critical attributes (lanes, speed, capacity) 50 | - Network Visualization: Built-in tools for visual inspection and verification 51 | 52 | 3. Performance and Usability 53 | 54 | - High-Performance Core: Written in C++ for maximum computational efficiency 55 | - Intuitive Python Interface: Simple API makes complex network extraction straightforward 56 | 57 | 58 | Citation 59 | ==================================== 60 | 61 | If osm2gmns contributes to your research, please cite the following publication: 62 | 63 | Lu, J., & Zhou, X.S. (2023). Virtual track networks: A hierarchical modeling framework and 64 | open-source tools for simplified and efficient connected and automated mobility (CAM) system 65 | design based on general modeling network specification (GMNS). Transportation Research 66 | Part C: Emerging Technologies, 153, 104223. [`link`_] 67 | 68 | 69 | Contents 70 | ==================================== 71 | 72 | .. toctree:: 73 | :maxdepth: 2 74 | 75 | installation 76 | quick-start 77 | get-osm-data 78 | public-api 79 | gmns 80 | mrm 81 | sample-net 82 | acknowledgement 83 | 84 | 85 | .. _`Jiawei Lu`: https://www.linkedin.com/in/jiawlu/ 86 | .. _`Xuesong (Simon) Zhou`: https://www.linkedin.com/in/xzhou/ 87 | .. _`OpenStreetMap`: https://www.openstreetmap.org 88 | .. _`GMNS`: https://github.com/zephyr-data-specs/GMNS 89 | .. _`v0.x`: https://osm2gmns.readthedocs.io/en/v0.x 90 | .. _`link`: https://doi.org/10.1016/j.trc.2023.104223 91 | -------------------------------------------------------------------------------- /docs/source/installation.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Installation 3 | ============ 4 | 5 | You can install the latest release of osm2gmns at `PyPI`_ via ``pip``: 6 | 7 | .. code-block:: bash 8 | 9 | pip install osm2gmns 10 | 11 | 12 | .. _`PyPI`: https://pypi.org/project/osm2gmns -------------------------------------------------------------------------------- /docs/source/mrm.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | Multi-Resolution Modeling 3 | ========================= 4 | 5 | Multi-Resolution Modeling (MRM) is a modeling technology that creates a family of models 6 | that represent the same phenomenon or a set of questions at more than two different resolutions. 7 | The fine-grained spatial scales could cover corridors, roads, and lane representations, 8 | and the temporal resolution refers to the time interval (or time stamps) at which the 9 | dynamic state of the model is updated, typically ranging from days to seconds. Each 10 | type of model (macroscopic, mesoscopic, or microscopic) has its own advantages and 11 | disadvantages, and represents a trade-off between scales and resolution levels. The 12 | ultimate goal of MRM is to seamlessly integrate models with different temporal and 13 | spatial resolutions, while the focus of the cross-resolution approach is to 14 | bridge the gaps between macroscopic and microscopic levels, so as to provide strong 15 | theoretical support and deeper insights for both levels. 16 | 17 | The osm2gmns package adopts the `GMNS`_ standard and further extends it to a inherently 18 | consistent multi-resolution network modeling standard. With a single line of code, 19 | osm2gmns can help users generate corresponding mesoscopic and microscopic networks for 20 | any given macroscopic networks in GMNS format, enabling practitioners and researchers 21 | to carry out various studies on transportation planning, designing, optimization, 22 | simulation, and computation under different spatial granularities. In this section, 23 | we mainly talk about the three levels of transportation network representation. 24 | 25 | 26 | .. figure:: _images/mrnet.png 27 | :name: micronet_pic 28 | :align: center 29 | :width: 100% 30 | 31 | Multi-resolution network representation 32 | 33 | Macroscopic Network 34 | =================================== 35 | 36 | - node.csv 37 | 38 | .. table:: 39 | :class: classic 40 | 41 | +---------------+---------------+----------+---------------------------------------------------------------+ 42 | | Field | Type | Required?| Comments | 43 | +===============+===============+==========+===============================================================+ 44 | | name | string | | | 45 | +---------------+---------------+----------+---------------------------------------------------------------+ 46 | | node_id | int | yes | unique key | 47 | +---------------+---------------+----------+---------------------------------------------------------------+ 48 | | osm_node_id | string or int | | corresponding node id in osm data | 49 | +---------------+---------------+----------+---------------------------------------------------------------+ 50 | | osm_highway | string | | point type in osm data | 51 | +---------------+---------------+----------+---------------------------------------------------------------+ 52 | | zone_id | int | | | 53 | +---------------+---------------+----------+---------------------------------------------------------------+ 54 | | ctrl_type | enum | | signal; null | 55 | +---------------+---------------+----------+---------------------------------------------------------------+ 56 | | node_type | string | | | 57 | +---------------+---------------+----------+---------------------------------------------------------------+ 58 | | activity_type | string | | defined by adjacent links | 59 | +---------------+---------------+----------+---------------------------------------------------------------+ 60 | | is_boundary | enum | | -1: boundary node only with incoming links; 0: no; 1: boundary| 61 | | | | | node only with outgoing links; 2: boundary node with both | 62 | | | | | incoming and outgoing links | 63 | +---------------+---------------+----------+---------------------------------------------------------------+ 64 | | x_coord | double | yes | WGS 84 is used in osm | 65 | +---------------+---------------+----------+---------------------------------------------------------------+ 66 | | y_coord | double | yes | WGS 84 is used in osm | 67 | +---------------+---------------+----------+---------------------------------------------------------------+ 68 | |intersection_id| int | | nodes belonging to one complex intersection have the same id | 69 | +---------------+---------------+----------+---------------------------------------------------------------+ 70 | | poi_id | int | | id of the corresponding poi | 71 | +---------------+---------------+----------+---------------------------------------------------------------+ 72 | | notes | string | | | 73 | +---------------+---------------+----------+---------------------------------------------------------------+ 74 | 75 | - link.csv 76 | 77 | A link is an edge in a network, defined by the nodes it travels from and to. It may have associated geometry 78 | information\ :sup:`[2]`. Similar to node.csv, We also added several new attributes to the link file. Detailed 79 | link data dictionary is listed below. 80 | 81 | .. table:: 82 | :class: classic 83 | 84 | +----------------+---------------+----------+---------------------------------------------------------------+ 85 | | Field | Type | Required?| Comments | 86 | +================+===============+==========+===============================================================+ 87 | | name | string | | | 88 | +----------------+---------------+----------+---------------------------------------------------------------+ 89 | | link_id | int | yes | unique key | 90 | +----------------+---------------+----------+---------------------------------------------------------------+ 91 | | osm_way_id | string or int | | corresponding way id in osm data | 92 | +----------------+---------------+----------+---------------------------------------------------------------+ 93 | | from_node_id | int | yes | | 94 | +----------------+---------------+----------+---------------------------------------------------------------+ 95 | | to_node_id | int | yes | | 96 | +----------------+---------------+----------+---------------------------------------------------------------+ 97 | | dir_flag | enum | | 1: forward, -1: backward, 0:bidirectionial | 98 | +----------------+---------------+----------+---------------------------------------------------------------+ 99 | | length | float | | unit: meter | 100 | +----------------+---------------+----------+---------------------------------------------------------------+ 101 | | lanes | int | | | 102 | +----------------+---------------+----------+---------------------------------------------------------------+ 103 | | free_speed | float | | unit: kilometer/hour | 104 | +----------------+---------------+----------+---------------------------------------------------------------+ 105 | | capacity | float | | unit: veh/hr/lane | 106 | +----------------+---------------+----------+---------------------------------------------------------------+ 107 | | link_type_name | string | | | 108 | +----------------+---------------+----------+---------------------------------------------------------------+ 109 | | link_type | int | | | 110 | +----------------+---------------+----------+---------------------------------------------------------------+ 111 | | geometry | Geometry | | `wkt`_ | 112 | +----------------+---------------+----------+---------------------------------------------------------------+ 113 | | allowed_uses | enum | | auto, bike, walk | 114 | +----------------+---------------+----------+---------------------------------------------------------------+ 115 | | from_biway | bool | | 1: link created from a bidirectional way, 0: not | 116 | +----------------+---------------+----------+---------------------------------------------------------------+ 117 | | is_link | bool | | 1: link connecting two roads, 0: not | 118 | +----------------+---------------+----------+---------------------------------------------------------------+ 119 | 120 | There are two optional files including ``movement.csv`` and ``segement.csv`` that follow the exact same format as 121 | defined in the GMMS standard. Readers can check the GMNS website for details. 122 | 123 | In addition to the above files defined in the GMNS standard, osm2gmns can also produce ``poi.csv`` files 124 | where point of interest information is stored. Detailed poi data dictionary is listed below. 125 | 126 | .. table:: 127 | :class: classic 128 | 129 | +-----------------+---------------+----------+---------------------------------------------------------------+ 130 | | Field | Type | Required?| Comments | 131 | +=================+===============+==========+===============================================================+ 132 | | name | string | | | 133 | +-----------------+---------------+----------+---------------------------------------------------------------+ 134 | | poi_id | int | yes | unique key | 135 | +-----------------+---------------+----------+---------------------------------------------------------------+ 136 | | osm_way_id | string or int | | corresponding way id in osm data | 137 | +-----------------+---------------+----------+---------------------------------------------------------------+ 138 | | osm_relation_id | string or int | | corresponding relation id in osm data | 139 | +-----------------+---------------+----------+---------------------------------------------------------------+ 140 | | building | string | | building tag in osm data | 141 | +-----------------+---------------+----------+---------------------------------------------------------------+ 142 | | amenity | string | | amenity tag in osm data | 143 | +-----------------+---------------+----------+---------------------------------------------------------------+ 144 | | way | string | | way tag in osm data | 145 | +-----------------+---------------+----------+---------------------------------------------------------------+ 146 | | geometry | Geometry | yes | `wkt`_ | 147 | +-----------------+---------------+----------+---------------------------------------------------------------+ 148 | | centroid | Geometry | | `wkt`_ | 149 | +-----------------+---------------+----------+---------------------------------------------------------------+ 150 | | area | float | | area of the poi. unit: square meter | 151 | +-----------------+---------------+----------+---------------------------------------------------------------+ 152 | | area_ft2 | float | | area of the poi. unit: square feet | 153 | +-----------------+---------------+----------+---------------------------------------------------------------+ 154 | 155 | 156 | Mesoscopic Network 157 | =================================== 158 | 159 | Compared to the original macroscopic network, the mesoscopic network has more detailed 160 | information of the intersections. In the mesoscopic network, the research team expanded 161 | each intersection represented by a node in the macroscopic network. The team built a 162 | connector link for each intersection movement to facilitate intersection modeling, 163 | especially for signalized intersections. 164 | 165 | Macroscopic and mesoscopic networks have different link-level coding schemes. Macroscopic 166 | networks often represent a road segment between two adjacent intersections as a link; 167 | however, lane changes sometimes occur within a link, especially when close to intersections. 168 | Changes in the number of lanes result in capacity changes, but the link attributes cannot 169 | properly reflect these changes. This situation may bring inconvenience or even potential 170 | errors when performing network modeling. In the GMNS standard, the comma-separated values 171 | (CSV) file, segment.csv, stores lane changes. The research team split and converted each 172 | link with lane changes from a macroscopic network to multiple mesoscopic links so that 173 | each mesoscopic link has a homogeneous capacity. 174 | 175 | - node.csv 176 | 177 | .. table:: 178 | :class: classic 179 | 180 | +-------------+---------------+----------+---------------------------------------------------------------+ 181 | | Field | Type | Required?| Comments | 182 | +=============+===============+==========+===============================================================+ 183 | | node_id | int | yes | unique key | 184 | +-------------+---------------+----------+---------------------------------------------------------------+ 185 | | zone_id | int | | | 186 | +-------------+---------------+----------+---------------------------------------------------------------+ 187 | | x_coord | double | yes | WGS 84 is used in osm | 188 | +-------------+---------------+----------+---------------------------------------------------------------+ 189 | | y_coord | double | yes | WGS 84 is used in osm | 190 | +-------------+---------------+----------+---------------------------------------------------------------+ 191 | |macro_node_id| int | | id of its parent macroscopic node | 192 | +-------------+---------------+----------+---------------------------------------------------------------+ 193 | |macro_link_id| int | | id of its parent macroscopic link | 194 | +-------------+---------------+----------+---------------------------------------------------------------+ 195 | |activity_type| string | | | 196 | +-------------+---------------+----------+---------------------------------------------------------------+ 197 | | is_boundary | enum | | -1: boundary node only with incoming links; 0: no; 1: boundary| 198 | | | | | node only with outgoing links | 199 | +-------------+---------------+----------+---------------------------------------------------------------+ 200 | 201 | - link.csv 202 | 203 | A link is an edge in a network, defined by the nodes it travels from and to. It may have associated geometry 204 | information\ :sup:`[2]`. Similar to node.csv, We also added several new attributes to the link file. Detailed 205 | link data dictionary is listed below. 206 | 207 | .. table:: 208 | :class: classic 209 | 210 | +----------------+---------------+----------+---------------------------------------------------------------+ 211 | | Field | Type | Required?| Comments | 212 | +================+===============+==========+===============================================================+ 213 | | link_id | int | yes | unique key | 214 | +----------------+---------------+----------+---------------------------------------------------------------+ 215 | | from_node_id | int | yes | | 216 | +----------------+---------------+----------+---------------------------------------------------------------+ 217 | | to_node_id | int | yes | | 218 | +----------------+---------------+----------+---------------------------------------------------------------+ 219 | | dir_flag | enum | | 1: forward, -1: backward, 0:bidirectionial | 220 | +----------------+---------------+----------+---------------------------------------------------------------+ 221 | | length | float | | unit: meter | 222 | +----------------+---------------+----------+---------------------------------------------------------------+ 223 | | lanes | int | | | 224 | +----------------+---------------+----------+---------------------------------------------------------------+ 225 | | free_speed | float | | unit: kilometer/hour | 226 | +----------------+---------------+----------+---------------------------------------------------------------+ 227 | | capacity | float | | unit: veh/hr/lane | 228 | +----------------+---------------+----------+---------------------------------------------------------------+ 229 | | link_type_name | string | | | 230 | +----------------+---------------+----------+---------------------------------------------------------------+ 231 | | link_type | int | | | 232 | +----------------+---------------+----------+---------------------------------------------------------------+ 233 | | geometry | Geometry | | `wkt`_ | 234 | +----------------+---------------+----------+---------------------------------------------------------------+ 235 | | macro_node_id | int | | id of its parent macroscopic node | 236 | +----------------+---------------+----------+---------------------------------------------------------------+ 237 | | macro_link_id | int | | id of its parent macroscopic link | 238 | +----------------+---------------+----------+---------------------------------------------------------------+ 239 | | mvmt_txt_id | enum | | NBL, NBT, NBR, NBU, SBL, SBT, SBR, SBU, EBL, EBT, EBR, EBU, | 240 | | | | | WBL, WBT, WBR, WBU | 241 | +----------------+---------------+----------+---------------------------------------------------------------+ 242 | | allowed_uses | enum | | auto, bike, walk | 243 | +----------------+---------------+----------+---------------------------------------------------------------+ 244 | 245 | 246 | Microscopic Network 247 | =================================== 248 | 249 | In the Maryland case study, microscopic networks used a lane-by-lane, cell-based representation. 250 | Instead of a conceptual line segment, lanes represented each link. The research team further 251 | discretized lanes into small cells to accurately describe vehicle motion status when moving on 252 | the road. The team also created changing cells to enable vehicles to switch trajectories between 253 | lanes. Users can customize the length of cells to accommodate different modeling needs. 254 | 255 | - node.csv 256 | 257 | .. table:: 258 | :class: classic 259 | 260 | +-------------+---------------+----------+---------------------------------------------------------------+ 261 | | Field | Type | Required?| Comments | 262 | +=============+===============+==========+===============================================================+ 263 | | node_id | int | yes | unique key | 264 | +-------------+---------------+----------+---------------------------------------------------------------+ 265 | | zone_id | int | | | 266 | +-------------+---------------+----------+---------------------------------------------------------------+ 267 | | x_coord | double | yes | WGS 84 is used in osm | 268 | +-------------+---------------+----------+---------------------------------------------------------------+ 269 | | y_coord | double | yes | WGS 84 is used in osm | 270 | +-------------+---------------+----------+---------------------------------------------------------------+ 271 | | meso_link_id| int | | id of its parent mesoscopic link | 272 | +-------------+---------------+----------+---------------------------------------------------------------+ 273 | | lane_no | int | | start from 1 from inner side to outer side | 274 | +-------------+---------------+----------+---------------------------------------------------------------+ 275 | | is_boundary | enum | | -1: boundary node only with incoming links; 0: no; 1: boundary| 276 | | | | | node only with outgoing links | 277 | +-------------+---------------+----------+---------------------------------------------------------------+ 278 | 279 | - link.csv 280 | 281 | A link is an edge in a network, defined by the nodes it travels from and to. It may have associated geometry 282 | information\ :sup:`[2]`. Similar to node.csv, We also added several new attributes to the link file. Detailed 283 | link data dictionary is listed below. 284 | 285 | .. table:: 286 | :class: classic 287 | 288 | +----------------+---------------+----------+---------------------------------------------------------------+ 289 | | Field | Type | Required?| Comments | 290 | +================+===============+==========+===============================================================+ 291 | | link_id | int | yes | unique key | 292 | +----------------+---------------+----------+---------------------------------------------------------------+ 293 | | from_node_id | int | yes | | 294 | +----------------+---------------+----------+---------------------------------------------------------------+ 295 | | to_node_id | int | yes | | 296 | +----------------+---------------+----------+---------------------------------------------------------------+ 297 | | dir_flag | enum | | 1: forward, -1: backward, 0:bidirectionial | 298 | +----------------+---------------+----------+---------------------------------------------------------------+ 299 | | length | float | | unit: meter | 300 | +----------------+---------------+----------+---------------------------------------------------------------+ 301 | | lanes | int | | | 302 | +----------------+---------------+----------+---------------------------------------------------------------+ 303 | | free_speed | float | | unit: kilometer/hour | 304 | +----------------+---------------+----------+---------------------------------------------------------------+ 305 | | capacity | float | | unit: veh/hr/lane | 306 | +----------------+---------------+----------+---------------------------------------------------------------+ 307 | | link_type_name | string | | | 308 | +----------------+---------------+----------+---------------------------------------------------------------+ 309 | | link_type | int | | | 310 | +----------------+---------------+----------+---------------------------------------------------------------+ 311 | | geometry | Geometry | | `wkt`_ | 312 | +----------------+---------------+----------+---------------------------------------------------------------+ 313 | | macro_node_id | int | | id of its parent macroscopic node | 314 | +----------------+---------------+----------+---------------------------------------------------------------+ 315 | | macro_link_id | int | | id of its parent macroscopic link | 316 | +----------------+---------------+----------+---------------------------------------------------------------+ 317 | | meso_link_id | int | | id of its parent mesoscopic link | 318 | +----------------+---------------+----------+---------------------------------------------------------------+ 319 | | cell_type | enum | | 1: traveling cell, 2: lane changing cell | 320 | +----------------+---------------+----------+---------------------------------------------------------------+ 321 | |additional_cost | float | | | 322 | +----------------+---------------+----------+---------------------------------------------------------------+ 323 | | lane_no | int | | start from 1 from inner side to outer side | 324 | +----------------+---------------+----------+---------------------------------------------------------------+ 325 | | mvmt_txt_id | enum | | NBL, NBT, NBR, NBU, SBL, SBT, SBR, SBU, EBL, EBT, EBR, EBU, | 326 | | | | | WBL, WBT, WBR, WBU | 327 | +----------------+---------------+----------+---------------------------------------------------------------+ 328 | | allowed_uses | enum | | auto, bike, walk | 329 | +----------------+---------------+----------+---------------------------------------------------------------+ 330 | 331 | 332 | 333 | \ :sup:`[1]` https://github.com/zephyr-data-specs/GMNS/blob/master/Specification/Node.md 334 | 335 | \ :sup:`[2]` https://github.com/zephyr-data-specs/GMNS/blob/master/Specification/Link.md 336 | 337 | .. _`GMNS`: https://github.com/zephyr-data-specs/GMNS 338 | .. _`wkt`: https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry 339 | -------------------------------------------------------------------------------- /docs/source/public-api.rst: -------------------------------------------------------------------------------- 1 | ========== 2 | Public API 3 | ========== 4 | 5 | .. autofunction:: osm2gmns.getNetFromFile 6 | .. autofunction:: osm2gmns.outputNetToCSV 7 | .. autofunction:: osm2gmns.consolidateComplexIntersections 8 | .. autofunction:: osm2gmns.fillLinkAttributesWithDefaultValues 9 | .. autofunction:: osm2gmns.generateNodeActivityInfo 10 | .. autofunction:: osm2gmns.downloadOSMData 11 | -------------------------------------------------------------------------------- /docs/source/quick-start.rst: -------------------------------------------------------------------------------- 1 | =========== 2 | Quick Start 3 | =========== 4 | 5 | This section provides introductory examples to demonstrate the core workflow of using 6 | osm2gmns for generating, processing, and exporting transportation networks. 7 | 8 | osm2gmns processes network data from local OpenStreetMap files (.osm, .xml, or .pbf 9 | formats). Therefore, the first step is always to download the OSM data for your region 10 | of interest. Several methods exist for this, detailed in the :ref:`section-get-osm-data` 11 | section. 12 | 13 | In this guide, we use the area around Arizona State University, Tempe Campus as an 14 | example to introduce some major functions in osm2gmns. The downloaded osm file is named 15 | as ``asu.osm``. 16 | 17 | 18 | Basic Network Generation 19 | ========================= 20 | 21 | Load a network from an OSM file and export it to GMNS-compliant CSV files. 22 | 23 | .. code-block:: python 24 | 25 | >>> import osm2gmns as og 26 | >>> net = og.getNetFromFile('asu.osm') 27 | >>> og.outputNetToCSV(net) 28 | 29 | By default, ``getNetFromFile`` extracts the drivable network (mode_types='auto'). 30 | In the example, the output folder is set as the current working directory. Users 31 | can specify the output folder by using the argument ``output_folder``. The output 32 | files include ``node.csv`` and ``link.csv``, which contain node and link information 33 | respectively. 34 | 35 | 36 | Consolidate Intersections 37 | ========================= 38 | 39 | In OpenStreetMap, large intersections are often mapped using multiple nodes. 40 | For many traffic analysis tasks, it's beneficial to represent such intersections 41 | as single nodes. The ``consolidateComplexIntersections`` function achieves this, 42 | typically focusing on signalized junctions. 43 | 44 | .. code-block:: python 45 | 46 | >>> net = og.getNetFromFile('asu.osm') 47 | >>> og.consolidateComplexIntersections(net, auto_identify=True) 48 | >>> og.outputNetToCSV(net) 49 | 50 | .. figure:: _images/consolidation.png 51 | :name: consolidate_pic 52 | :align: center 53 | :width: 100% 54 | 55 | Complex intersection consolidation 56 | 57 | The resulting network can be visualized using tools like `QGIS`_ or `NeXTA`_ 58 | to inspect the consolidated intersections. 59 | 60 | 61 | Network Types and POI 62 | ========================= 63 | 64 | osm2gmns supports five different mode types, including ``auto``, ``bike``, ``walk``, ``railway``, ``aeroway``. 65 | Users can get different types of networks by specifying the argument ``mode_types`` (default: ``auto``). 66 | 67 | .. code-block:: python 68 | 69 | >>> # obtain the network for bike 70 | >>> net = og.getNetFromFile('asu.osm', mode_types='bike') 71 | >>> # obtain the network for walk and bike 72 | >>> net = og.getNetFromFile('asu.osm', mode_types=['walk','bike']) 73 | >>> # obtain the network for auto, railway and aeroway 74 | >>> net = og.getNetFromFile('asu.osm', mode_types=['auto','railway','aeroway']) 75 | 76 | Obtain POIs (Point of Interest) from osm map data. 77 | 78 | .. code-block:: python 79 | 80 | >>> net = og.getNetFromFile('asu.osm', POI=True) 81 | 82 | If ``POI=True`` is used, ``outputNetToCSV`` will generate an additional ``poi.csv`` 83 | file alongside ``node.csv`` and ``link.csv``. 84 | 85 | .. figure:: _images/poi1.png 86 | :name: poi1 87 | :align: center 88 | :width: 100% 89 | 90 | Network with POIs 91 | 92 | 93 | Generate Multi-Resolution Networks 94 | ================================== 95 | 96 | Multi-resolution (MRM) functionalities are currently not available in v1.x. 97 | For MRM, users can use the latest v0.x. Check the v0.x `user's guide`_ for details. 98 | 99 | 100 | .. _`QGIS`: https://qgis.org 101 | .. _`NeXTA`: https://github.com/asu-trans-ai-lab/NeXTA4GMNS 102 | .. _`user's guide`: https://osm2gmns.readthedocs.io/en/v0.x -------------------------------------------------------------------------------- /docs/source/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx-rtd-theme==1.3.0 -------------------------------------------------------------------------------- /docs/source/sample-net.rst: -------------------------------------------------------------------------------- 1 | =============== 2 | Sample Networks 3 | =============== 4 | 5 | Dallas Fort Worth International Airport 6 | ======================================== 7 | 8 | .. figure:: _images/net_dfw.png 9 | :name: net_dfw 10 | :align: center 11 | :width: 100% 12 | 13 | Dallas Fort Worth International Airport 14 | 15 | 16 | Arizona State University, Tempe Campus 17 | ======================================== 18 | 19 | .. figure:: _images/net_asu.png 20 | :name: net_asu 21 | :align: center 22 | :width: 100% 23 | 24 | Arizona State University, Tempe Campus 25 | 26 | 27 | Arizona, US 28 | ======================================== 29 | 30 | .. figure:: _images/net_az.png 31 | :name: net_az 32 | :align: center 33 | :width: 100% 34 | 35 | Arizona, US 36 | 37 | 38 | US railway network (midwest) 39 | ======================================== 40 | 41 | .. figure:: _images/net_midwest.png 42 | :name: net_midwest 43 | :align: center 44 | :width: 100% 45 | 46 | US railway network (midwest) 47 | 48 | 49 | Greater London, UK 50 | ======================================== 51 | 52 | .. figure:: _images/net_london.png 53 | :name: net_london 54 | :align: center 55 | :width: 100% 56 | 57 | Greater London, UK 58 | 59 | 60 | Melbourne, Australia 61 | ======================================== 62 | 63 | .. figure:: _images/net_melbourne.png 64 | :name: net_melbourne 65 | :align: center 66 | :width: 100% 67 | 68 | Melbourne, Australia 69 | -------------------------------------------------------------------------------- /osm2gmns/__init__.py: -------------------------------------------------------------------------------- 1 | # @author Jiawei Lu (jiaweil9@asu.edu) 2 | # @time 2/17/23 3:02 PM 3 | # @desc [script description] 4 | 5 | 6 | from osm2gmns.osm2gmns import initlib 7 | from osm2gmns.osm2gmns import getNetFromFile, generateNodeActivityInfo, fillLinkAttributesWithDefaultValues, consolidateComplexIntersections, outputNetToCSV 8 | from osm2gmns.downloader import downloadOSMData 9 | 10 | __version__ = '1.0.1' 11 | 12 | initlib() 13 | 14 | 15 | # zero or more dev releases (denoted with a “.devN” suffix) 16 | # zero or more alpha releases (denoted with a “.aN” suffix) 17 | # zero or more beta releases (denoted with a “.bN” suffix) 18 | # zero or more release candidates (denoted with a “.rcN” suffix) 19 | -------------------------------------------------------------------------------- /osm2gmns/downloader.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo 3 | # Copyright (C) 2009-2020 German Aerospace Center (DLR) and others. 4 | # This program and the accompanying materials are made available under the 5 | # terms of the Eclipse Public License 2.0 which is available at 6 | # https://www.eclipse.org/legal/epl-2.0/ 7 | # This Source Code may also be made available under the following Secondary 8 | # Licenses when the conditions for such availability set forth in the Eclipse 9 | # Public License 2.0 are satisfied: GNU General Public License, version 2 10 | # or later which is available at 11 | # https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html 12 | # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later 13 | 14 | # @file osmGet.py 15 | # @author Daniel Krajzewicz 16 | # @author Jakob Erdmann 17 | # @author Michael Behrisch 18 | # @date 2009-08-01 19 | 20 | 21 | import os 22 | import http.client as httplib 23 | import urllib.parse as urlparse 24 | import base64 25 | 26 | 27 | 28 | _url = "www.overpass-api.de/api/interpreter" 29 | # alternatives: overpass.kumi.systems/api/interpreter, sumo.dlr.de/osm/api/interpreter 30 | 31 | 32 | def _readCompressed(conn, urlpath, query, filename): 33 | conn.request("POST", "/" + urlpath, """ 34 | 35 | 36 | %s 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | """ % query) 47 | response = conn.getresponse() 48 | # print(response.status, response.reason) 49 | if response.status == 200: 50 | print('valid reponses got from API server.') 51 | print('receving data...') 52 | out = open(filename, "wb") 53 | out.write(response.read()) 54 | out.close() 55 | print(f'map data has been written to {filename}') 56 | 57 | 58 | def downloadOSMData(area_id, output_filepath='map.osm', url=_url): 59 | """ 60 | Downloads OpenStreetMap (OSM) data for a specified region using the Overpass API. 61 | 62 | This function queries the Overpass API for a given OSM relation ID, retrieves 63 | the corresponding map data, and saves it to a local file. 64 | 65 | Parameters 66 | ---------- 67 | area_id : int 68 | The OpenStreetMap relation ID for the area of interest. 69 | See the 'Get OSM Data' section in the user guide 70 | for details on finding relation IDs. 71 | output_filepath : str 72 | The desired filepath for the downloaded OSM data. 73 | If no extension or an unsupported extension 74 | is provided, '.osm' will be used. Supported extensions: '.osm', '.xml'. 75 | url : str 76 | The URL of the Overpass API endpoint. 77 | 78 | Returns 79 | ------- 80 | None 81 | The function saves the data to the specified file and prints status 82 | messages to the console. 83 | """ 84 | 85 | file_name, file_extension = os.path.splitext(output_filepath) 86 | if not file_extension: 87 | print(f'WARNING: no file extension in output_filepath {output_filepath}, output_filepath is changed to {file_name}.osm') 88 | output_filepath = f'{file_name}.osm' 89 | elif file_extension not in ['.osm', '.xml']: 90 | print(f'WARNING: the file extension in output_filepath {output_filepath} is not supported, output_filepath is changed to {file_name}.osm') 91 | output_filepath = f'{file_name}.osm' 92 | 93 | if "http" in url: 94 | url = urlparse.urlparse(url) 95 | else: 96 | url = urlparse.urlparse("https://" + url) 97 | if os.environ.get("https_proxy") is not None: 98 | headers = {} 99 | proxy_url = urlparse.urlparse(os.environ.get("https_proxy")) 100 | if proxy_url.username and proxy_url.password: 101 | auth = '%s:%s' % (proxy_url.username, proxy_url.password) 102 | headers['Proxy-Authorization'] = 'Basic ' + base64.b64encode(auth) 103 | conn = httplib.HTTPSConnection(proxy_url.hostname, proxy_url.port) 104 | conn.set_tunnel(url.hostname, 443, headers) 105 | else: 106 | if url.scheme == "https": 107 | conn = httplib.HTTPSConnection(url.hostname, url.port) 108 | else: 109 | conn = httplib.HTTPConnection(url.hostname, url.port) 110 | 111 | if area_id < 3600000000: 112 | area_id += 3600000000 113 | _readCompressed(conn, url.path, '' % area_id, output_filepath) 114 | 115 | conn.close() 116 | -------------------------------------------------------------------------------- /osm2gmns/osm2gmns.py: -------------------------------------------------------------------------------- 1 | # @author Jiawei Lu (jiaweil9@asu.edu) 2 | # @time 2/17/23 3:02 PM 3 | # @desc [script description] 4 | 5 | 6 | import platform 7 | import ctypes 8 | import os 9 | 10 | current_os = platform.system() 11 | if current_os == "Darwin": 12 | library_name = "libosm2gmns.dylib" 13 | elif current_os == "Windows": 14 | library_name = "osm2gmns.dll" 15 | elif current_os == "Linux": 16 | library_name = "libosm2gmns.so" 17 | else: 18 | raise OSError("Unsupported operating system") 19 | 20 | oglib = None 21 | try: 22 | oglib = ctypes.CDLL(os.path.join(os.path.dirname(__file__), library_name)) 23 | except OSError: 24 | print("failed to load osm2gmns dynamic library.") 25 | 26 | 27 | class StrIntDict(ctypes.Structure): 28 | _fields_ = [("key", ctypes.c_char_p), ("value", ctypes.c_int)] 29 | 30 | class StrFloatDict(ctypes.Structure): 31 | _fields_ = [("key", ctypes.c_char_p), ("value", ctypes.c_float)] 32 | 33 | 34 | def initlib(): 35 | if oglib is None: 36 | return 37 | 38 | oglib.initializeAbslLoggingPy.argtypes = [] 39 | 40 | oglib.releaseNetworkMemoryPy.argtypes = [ctypes.c_void_p] 41 | 42 | oglib.getNetFromFilePy.argtypes = [ctypes.c_char_p, 43 | ctypes.POINTER(ctypes.c_char_p), ctypes.c_size_t, 44 | ctypes.POINTER(ctypes.c_char_p), ctypes.c_size_t, 45 | ctypes.POINTER(ctypes.c_char_p), ctypes.c_size_t, 46 | ctypes.c_bool, ctypes.c_float, 47 | ctypes.POINTER(ctypes.c_char_p), ctypes.c_size_t, 48 | ctypes.POINTER(ctypes.c_char_p), ctypes.c_size_t, 49 | ctypes.POINTER(ctypes.c_char_p), ctypes.c_size_t, 50 | ctypes.c_bool] 51 | oglib.getNetFromFilePy.restype = ctypes.c_void_p 52 | 53 | oglib.consolidateComplexIntersectionsPy.argtypes = [ctypes.c_void_p, ctypes.c_bool, ctypes.c_char_p, ctypes.c_float] 54 | 55 | oglib.generateNodeActivityInfoPy.argtypes = [ctypes.c_void_p, ctypes.c_char_p] 56 | 57 | oglib.fillLinkAttributesWithDefaultValuesPy.argtypes = [ctypes.c_void_p, 58 | ctypes.c_bool, ctypes.POINTER(StrIntDict), ctypes.c_size_t, 59 | ctypes.c_bool, ctypes.POINTER(StrFloatDict), ctypes.c_size_t, 60 | ctypes.c_bool, ctypes.POINTER(StrIntDict), ctypes.c_size_t] 61 | 62 | oglib.outputNetToCSVPy.argtypes = [ctypes.c_void_p, ctypes.c_char_p] 63 | 64 | oglib.getNumberOfNodesPy.argtypes = [ctypes.c_void_p] 65 | oglib.getNumberOfNodesPy.restype = ctypes.c_uint64 66 | oglib.getNumberOfLinksPy.argtypes = [ctypes.c_void_p] 67 | oglib.getNumberOfLinksPy.restype = ctypes.c_uint64 68 | 69 | oglib.initializeAbslLoggingPy() 70 | 71 | 72 | class Network: 73 | def __init__(self): 74 | self.cnet = None 75 | 76 | def __del__(self): 77 | oglib.releaseNetworkMemoryPy(self.cnet) 78 | 79 | @property 80 | def number_of_nodes(self): 81 | return oglib.getNumberOfNodesPy(self.cnet) 82 | 83 | @property 84 | def number_of_links(self): 85 | return oglib.getNumberOfLinksPy(self.cnet) 86 | 87 | 88 | def _checkStringToTuple(arg_val): 89 | return (arg_val,) if isinstance(arg_val, str) else arg_val 90 | 91 | def getNetFromFile(filepath, mode_types='auto', link_types=[], connector_link_types=[], POI=False, POI_sampling_ratio=1.0, 92 | osm_node_attributes=[], osm_link_attributes=[], osm_poi_attributes=[], 93 | strict_boundary=True): 94 | """ 95 | Parses an OpenStreetMap file and creates a transportation network object. 96 | 97 | Parameters 98 | ---------- 99 | filepath : str 100 | Path to the input OSM file (.osm, .xml, or .pbf format). 101 | mode_types : str or list of str 102 | Specifies the transportation modes to include. Options: 'auto', 'bike', 103 | 'walk', 'railway', 'aeroway'. Can be a single string or a list of strings. 104 | link_types : str or list of str 105 | Filters the network to include only specified OSM link types. If 106 | empty, all relevant link types for the selected `mode_types` are included. 107 | Supported types include: 'motorway', 'trunk', 'primary', 'secondary', 108 | 'tertiary', 'residential', 'service', 'cycleway', 'footway', 'track', 109 | 'unclassified', 'railway', 'aeroway'. 110 | connector_link_types : str or list of str 111 | Specifies link types that should only be included if they connect directly 112 | to links included via `link_types`. Useful for connecting main networks 113 | to specific areas (e.g., for traffic zones). Same supported types as 114 | `link_types`. If empty, no connector links are specifically handled 115 | this way. 116 | POI : bool 117 | If True, extracts Points of Interest (POIs) from the OSM data. 118 | POI_sampling_ratio : float 119 | Fraction of POIs to extract if POI is True. Must be between 0.0 and 1.0. 120 | osm_node_attributes : str or list of str 121 | List of additional OSM tag keys whose values should be extracted as attributes 122 | for nodes. If empty, only default attributes are extracted. 123 | osm_link_attributes : str or list of str 124 | List of additional OSM tag keys whose values should be extracted as attributes 125 | for links. If empty, only default attributes are extracted. 126 | osm_poi_attributes : str or list of str 127 | List of additional OSM tag keys whose values should be extracted as attributes 128 | for POIs. If empty, only default attributes are extracted. 129 | strict_boundary : bool 130 | If True, clips links that cross the boundary of the downloaded OSM region. 131 | If False, includes the full geometry of any link that partially falls 132 | within the region. 133 | 134 | Returns 135 | ------- 136 | Network 137 | An osm2gmns Network object containing the parsed transportation network data. 138 | """ 139 | 140 | network = Network() 141 | 142 | mode_types_byte_string = [mode_type.encode() for mode_type in _checkStringToTuple(mode_types)] 143 | mode_types_arr = (ctypes.c_char_p * len(mode_types_byte_string))(*mode_types_byte_string) 144 | 145 | link_types_byte_string = [link_type.encode() for link_type in _checkStringToTuple(link_types)] 146 | link_types_arr = (ctypes.c_char_p * len(link_types_byte_string))(*link_types_byte_string) 147 | connector_link_types_byte_string = [link_type.encode() for link_type in _checkStringToTuple(connector_link_types)] 148 | connector_link_types_arr = (ctypes.c_char_p * len(connector_link_types_byte_string))(*connector_link_types_byte_string) 149 | 150 | osm_node_attributes_byte_string = [attr.encode() for attr in _checkStringToTuple(osm_node_attributes)] 151 | osm_node_attributes_arr = (ctypes.c_char_p * len(osm_node_attributes_byte_string))(*osm_node_attributes_byte_string) 152 | osm_link_attributes_byte_string = [attr.encode() for attr in _checkStringToTuple(osm_link_attributes)] 153 | osm_link_attributes_arr = (ctypes.c_char_p * len(osm_link_attributes_byte_string))(*osm_link_attributes_byte_string) 154 | osm_poi_attributes_byte_string = [attr.encode() for attr in _checkStringToTuple(osm_poi_attributes)] 155 | osm_poi_attributes_arr = (ctypes.c_char_p * len(osm_poi_attributes_byte_string))(*osm_poi_attributes_byte_string) 156 | 157 | network.cnet = oglib.getNetFromFilePy(filepath.encode(), 158 | mode_types_arr, len(mode_types_arr), 159 | link_types_arr, len(link_types_arr), 160 | connector_link_types_arr, len(connector_link_types_arr), 161 | POI, POI_sampling_ratio, 162 | osm_node_attributes_arr, len(osm_node_attributes_arr), 163 | osm_link_attributes_arr, len(osm_link_attributes_arr), 164 | osm_poi_attributes_arr, len(osm_poi_attributes_arr), 165 | strict_boundary) 166 | return network 167 | 168 | 169 | def consolidateComplexIntersections(network, auto_identify=False, intersection_filepath=None, int_buffer=20.0): 170 | """ 171 | Consolidates multiple OSM nodes representing a single complex intersection into one node. 172 | 173 | Simplifies network topology by merging nodes that form complex junctions, 174 | often found at large, signalized intersections in OSM data. Consolidation 175 | can be based on (1) automatic detection, (2) an external file defining intersections, 176 | or (3) pre-assigned 'intersection_id' attributes in nodes (if network is loaded from CSV, 177 | nodes with the same 'intersection_id' will be considered as belonging to the same 178 | complex intersection). 179 | 180 | Priority for defining intersections: (3) Pre-existing 'intersection_id' in nodes, 181 | (2) `intersection_filepath`, (1) `auto_identify`. 182 | 183 | Parameters 184 | ---------- 185 | network : Network 186 | The osm2gmns Network object to modify. 187 | auto_identify : bool 188 | If True, attempts to automatically identify complex intersections based on 189 | built-in rules: nodes must have 'ctrl_type' = 'signal' and be connected 190 | by a link shorter than or equal to `int_buffer`. 191 | intersection_filepath : str or None 192 | Path to a CSV file defining complex intersections. Required columns: 193 | 'x_coord', 'y_coord' (defining the intersection center). Optional column: 194 | 'int_buffer' (overrides the global `int_buffer` for that specific intersection). 195 | Nodes within the buffer distance of a defined center will be consolidated. 196 | int_buffer : float 197 | The distance threshold (in meters) used for consolidation. Applied globally 198 | if `auto_identify` is True, or per-intersection if not specified in the 199 | `intersection_filepath`. 200 | 201 | Returns 202 | ------- 203 | None 204 | Modifies the input `network` object in place. 205 | """ 206 | 207 | oglib.consolidateComplexIntersectionsPy(network.cnet, 208 | auto_identify, 209 | intersection_filepath.encode() if intersection_filepath is not None else ''.encode(), 210 | int_buffer) 211 | 212 | 213 | def generateNodeActivityInfo(network, zone_filepath=None): 214 | """ 215 | Generates activity_type, is_boundary, zone_id for nodes in the network. 216 | 217 | Analyzes the network topology to assign relevant attributes to nodes, useful 218 | for transportation modeling. 'activity_type' (e.g., 'motorway', 'residential') 219 | is inferred from adjacent links. 'is_boundary' indicates if a node is at the 220 | edge of the defined network area. 'zone_id' assigns nodes to traffic analysis 221 | zones (TAZs). 222 | 223 | Parameters 224 | ---------- 225 | network : Network 226 | The osm2gmns Network object to modify. 227 | zone_filepath : str or None 228 | Path to a zone definition file (CSV). If provided, 'zone_id' for boundary 229 | nodes is determined based on this file. Otherwise, the 'node_id' is used 230 | as the 'zone_id' for boundary nodes. 231 | Expected CSV fields: "zone_id", "x_coord", "y_coord", "geometry". 232 | 'geometry' should be in WKT format (POINT, POLYGON, or MULTIPOLYGON). 233 | 'x_coord' and 'y_coord' can alternatively define POINT geometry. 234 | Boundary nodes are assigned the 'zone_id' of the containing polygon/multipolygon, 235 | or the nearest point zone if not within any polygon. 236 | 237 | Returns 238 | ------- 239 | None 240 | Modifies the input `network` object in place by adding or updating node attributes. 241 | """ 242 | 243 | oglib.generateNodeActivityInfoPy(network.cnet, 244 | zone_filepath.encode() if zone_filepath is not None else ''.encode()) 245 | 246 | 247 | def fillLinkAttributesWithDefaultValues(network, 248 | default_lanes=False, default_lanes_dict={}, 249 | default_speed=False, default_speed_dict={}, 250 | default_capacity=False, default_capacity_dict={}): 251 | """ 252 | Assigns default values to link attributes (lanes, speed, capacity) if they are missing. 253 | 254 | Populates essential link attributes based on 'link_type' for links where 255 | this information wasn't available in the source OSM data. Users can rely on 256 | built-in defaults or provide dictionaries to override them for specific link types. 257 | 258 | Parameters 259 | ---------- 260 | network : Network 261 | The osm2gmns Network object to modify. 262 | default_lanes : bool 263 | If True, assign default lane counts to links missing this attribute. 264 | default_lanes_dict : dict 265 | A dictionary mapping link types (str) to default lane counts (int). 266 | Overrides built-in defaults for the specified link types. Example: 267 | {'motorway': 3, 'primary': 3}. 268 | default_speed : bool 269 | If True, assign default speed limits (km/h) to links 270 | missing this attribute. 271 | default_speed_dict : dict 272 | A dictionary mapping link types (str) to default speed limits (float). 273 | Overrides built-in defaults. Example: {'residential': 20.0}. 274 | default_capacity : bool 275 | If True, assign default capacities (vehicles per hour per lane) to links 276 | missing this attribute. 277 | default_capacity_dict : dict 278 | A dictionary mapping link types (str) to default capacities (int). 279 | Overrides built-in defaults. Example: {'primary': 1800}. 280 | 281 | Returns 282 | ------- 283 | None 284 | Modifies the input `network` object in place by adding or updating link attributes. 285 | """ 286 | 287 | default_lanes_dict_ = (StrIntDict * len(default_lanes_dict))(*[(k.encode(), v) for k, v in default_lanes_dict.items()]) 288 | default_speed_dict_ = (StrFloatDict * len(default_speed_dict))(*[(k.encode(), v) for k, v in default_speed_dict.items()]) 289 | default_capacity_dict_ = (StrIntDict * len(default_capacity_dict))(*[(k.encode(), v) for k, v in default_capacity_dict.items()]) 290 | oglib.fillLinkAttributesWithDefaultValuesPy(network.cnet, 291 | default_lanes, default_lanes_dict_, len(default_lanes_dict_), 292 | default_speed, default_speed_dict_, len(default_speed_dict_), 293 | default_capacity, default_capacity_dict_, len(default_capacity_dict_)) 294 | 295 | 296 | def outputNetToCSV(network, output_folder=''): 297 | """ 298 | Exports the network object data to CSV files in GMNS format. 299 | 300 | Writes network information into 301 | separate CSV files (node.csv, link.csv, poi.csv) adhering to the 302 | General Modeling Network Specification (GMNS). 303 | 304 | Parameters 305 | ---------- 306 | network : Network 307 | The osm2gmns Network object containing the data to be exported. 308 | output_folder : str 309 | The directory path where the CSV files will be saved. If empty or not 310 | provided, defaults to the current working directory. 311 | 312 | Returns 313 | ------- 314 | None 315 | Creates CSV files in the specified output folder. 316 | """ 317 | 318 | oglib.outputNetToCSVPy(network.cnet, output_folder.encode()) 319 | -------------------------------------------------------------------------------- /osm2gmns_lib.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jiawei Lu on 2/17/23. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "src/config.h" 18 | #include "src/functions.h" 19 | #include "src/io.h" 20 | #include "src/networks.h" 21 | #include "src/osmconfig.h" 22 | 23 | #ifdef _WIN32 24 | #define C_API __declspec(dllexport) 25 | #else 26 | #define C_API 27 | #endif 28 | 29 | absl::flat_hash_set parseLinkTypes(const char** link_types_val, size_t link_types_len) { 30 | absl::flat_hash_set link_types; 31 | link_types.reserve(link_types_len); 32 | for (size_t idx = 0; idx < link_types_len; ++idx) { 33 | const std::string& link_type_str = link_types_val[idx]; // NOLINT 34 | const HighWayLinkType link_type = highwayStringToHighWayLinkType(link_type_str); 35 | if (link_type != HighWayLinkType::OTHER) { 36 | link_types.insert(link_type); 37 | } else { 38 | LOG(WARNING) << "unrecogonized link_type " << link_type_str; 39 | } 40 | } 41 | return link_types; 42 | } 43 | 44 | absl::flat_hash_set parseModeTypes(const char** mode_types_val, size_t mode_types_len) { 45 | absl::flat_hash_set mode_types; 46 | mode_types.reserve(mode_types_len); 47 | for (size_t idx = 0; idx < mode_types_len; ++idx) { 48 | const std::string& mode_type_str = mode_types_val[idx]; // NOLINT 49 | const ModeType mode_type = modeStringToModeType(mode_type_str); 50 | if (mode_type != ModeType::OTHER) { 51 | mode_types.insert(mode_type); 52 | } else { 53 | LOG(WARNING) << "unrecogonized mode_type " << mode_type_str; 54 | } 55 | } 56 | return mode_types; 57 | } 58 | 59 | std::vector parseCharArraysToStringVector(const char** chars_val, size_t chars_len) { 60 | std::vector char_vector; 61 | char_vector.reserve(chars_len); 62 | for (size_t idx = 0; idx < chars_len; ++idx) { 63 | char_vector.emplace_back(chars_val[idx]); // NOLINT 64 | } 65 | return char_vector; 66 | } 67 | 68 | template 69 | struct StrNumDict { 70 | const char* key; 71 | T value; 72 | }; 73 | 74 | template 75 | absl::flat_hash_map parseLinkTypeToNumDict(const StrNumDict* dict_val, size_t dict_len) { 76 | absl::flat_hash_map dict; 77 | for (size_t idx = 0; idx < dict_len; ++idx) { 78 | const std::string& link_type_str = dict_val[idx].key; // NOLINT 79 | const HighWayLinkType link_type = highwayStringToHighWayLinkType(link_type_str); 80 | if (link_type != HighWayLinkType::OTHER) { 81 | dict.emplace(link_type, dict_val[idx].value); // NOLINT 82 | } else { 83 | LOG(WARNING) << "unrecogonized link_type " << link_type_str; 84 | } 85 | } 86 | return dict; 87 | } 88 | 89 | extern "C" { 90 | 91 | C_API void initializeAbslLoggingPy() { 92 | absl::InitializeLog(); 93 | absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo); 94 | }; 95 | 96 | C_API void releaseNetworkMemoryPy(Network* network) { delete network; }; 97 | 98 | C_API Network* getNetFromFilePy(const char* osm_filepath, const char** mode_types_val, size_t mode_types_len, 99 | const char** link_types_val, size_t link_types_len, 100 | const char** connector_link_types_val, size_t connector_link_types_len, bool POI, 101 | float POI_sampling_ratio, const char** osm_node_attributes_val, 102 | size_t osm_node_attributes_len, const char** osm_link_attributes_val, 103 | size_t osm_link_attributes_len, const char** osm_poi_attributes_val, 104 | size_t osm_poi_attributes_len, bool strict_boundary) { 105 | const absl::flat_hash_set mode_types = parseModeTypes(mode_types_val, mode_types_len); 106 | const absl::flat_hash_set link_types = parseLinkTypes(link_types_val, link_types_len); 107 | const absl::flat_hash_set connector_link_types = 108 | parseLinkTypes(connector_link_types_val, connector_link_types_len); 109 | auto* osm_parsing_config = 110 | new OsmParsingConfig{parseCharArraysToStringVector(osm_node_attributes_val, osm_node_attributes_len), 111 | parseCharArraysToStringVector(osm_link_attributes_val, osm_link_attributes_len), 112 | parseCharArraysToStringVector(osm_poi_attributes_val, osm_poi_attributes_len)}; 113 | Network* network = getNetFromFile(osm_filepath, mode_types, link_types, connector_link_types, POI, POI_sampling_ratio, 114 | osm_parsing_config, strict_boundary); 115 | return network; 116 | }; 117 | 118 | C_API void consolidateComplexIntersectionsPy(Network* network, bool auto_identify, const char* intersection_file, 119 | float int_buffer) { 120 | consolidateComplexIntersections(network, auto_identify, intersection_file, int_buffer); 121 | }; 122 | 123 | C_API void generateNodeActivityInfoPy(Network* network, const char* zone_file) { 124 | generateNodeActivityInfo(network, zone_file); 125 | }; 126 | 127 | C_API void fillLinkAttributesWithDefaultValuesPy(Network* network, bool default_lanes, 128 | const StrNumDict* default_lanes_dict_val, 129 | size_t default_lanes_dict_len, bool default_speed, 130 | const StrNumDict* default_speed_dict_val, 131 | size_t default_speed_dict_len, bool default_capacity, 132 | const StrNumDict* default_capacity_dict_val, 133 | size_t default_capacity_dict_len) { 134 | const absl::flat_hash_map default_lanes_dict = 135 | parseLinkTypeToNumDict(default_lanes_dict_val, default_lanes_dict_len); 136 | const absl::flat_hash_map default_speed_dict = 137 | parseLinkTypeToNumDict(default_speed_dict_val, default_speed_dict_len); 138 | const absl::flat_hash_map default_capacity_dict = 139 | parseLinkTypeToNumDict(default_capacity_dict_val, default_capacity_dict_len); 140 | fillLinkAttributesWithDefaultValues(network, default_lanes, default_lanes_dict, default_speed, default_speed_dict, 141 | default_capacity, default_capacity_dict); 142 | } 143 | 144 | C_API void outputNetToCSVPy(const Network* network, const char* output_folder) { 145 | outputNetToCSV(network, output_folder); 146 | }; 147 | 148 | C_API size_t getNumberOfNodesPy(const Network* network) { return network->numberOfNodes(); }; 149 | C_API size_t getNumberOfLinksPy(const Network* network) { return network->numberOfLinks(); }; 150 | } -------------------------------------------------------------------------------- /osm2gmns_main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Lu, Jiawei on 5/22/24. 3 | // 4 | 5 | 6 | #include 7 | 8 | int main() { 9 | std::cout << "hello\n"; 10 | } -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project.urls] 2 | "Source Code" = "https://github.com/jiawlu/OSM2GMNS" 3 | "Bug Tracker" = "https://github.com/jiawlu/OSM2GMNS/issues" 4 | 5 | [build-system] 6 | requires = ["scikit-build-core>=0.9.0"] 7 | build-backend = "scikit_build_core.build" 8 | 9 | [project] 10 | name = "osm2gmns" 11 | version = "1.0.1" 12 | description = "convert map data from OpenStreetMap to network files in GMNS format" 13 | authors = [{ name = "Jiawei Lu", email = "lujiaweiwk@gmail.com" }, { name = "Xuesong (Simon) Zhou", email = "xzhou74@asu.edu" }] 14 | readme = "README.rst" 15 | 16 | requires-python = ">=3.8" 17 | dependencies = [] 18 | 19 | classifiers = [ 20 | "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", 21 | "Programming Language :: Python :: 3 :: Only", 22 | "Programming Language :: Python :: 3.8", 23 | "Programming Language :: Python :: 3.9", 24 | "Programming Language :: Python :: 3.10", 25 | "Programming Language :: Python :: 3.11", 26 | "Programming Language :: Python :: 3.12", 27 | ] 28 | 29 | 30 | [tool.scikit-build] 31 | wheel.packages = ["osm2gmns"] 32 | wheel.install-dir = "osm2gmns" 33 | wheel.expand-macos-universal-tags = true 34 | 35 | cmake.version = ">=3.25" 36 | cmake.args = ["-DBUILD_OSM2GMNS_EXE=OFF"] 37 | cmake.build-type = "Release" 38 | cmake.verbose = true 39 | 40 | sdist.include = [ 41 | "docs", 42 | "python", 43 | "src" 44 | ] 45 | 46 | sdist.exclude = [ 47 | ".github", 48 | ".gitattributes", 49 | ".gitignore", 50 | "build" 51 | ] 52 | 53 | [tool.cibuildwheel] 54 | build = "*" 55 | skip = "cp3{6,7}-*" 56 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiawlu/OSM2GMNS/2f8fa8552eb566ee55a5e199b31b28fd44da15ce/requirements.txt -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(osm2gmns_core STATIC 2 | osmnetwork.cpp 3 | osmconfig.cpp 4 | networks.cpp 5 | io.cpp 6 | utils.cpp 7 | functions.cpp) 8 | 9 | target_link_libraries(osm2gmns_core PUBLIC ${Required_Libraries}) 10 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jiawei Lu on 1/4/25. 3 | // 4 | 5 | #ifndef OSM2GMNS_CONFIG_H 6 | #define OSM2GMNS_CONFIG_H 7 | 8 | #include 9 | #include 10 | 11 | struct OsmParsingConfig { 12 | std::vector osm_node_attributes; 13 | std::vector osm_link_attributes; 14 | std::vector osm_poi_attributes; 15 | }; 16 | 17 | #endif // OSM2GMNS_CONFIG_H -------------------------------------------------------------------------------- /src/constants.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Lu, Jiawei on 5/31/24. 3 | // 4 | 5 | #ifndef OSM2GMNS_CONSTANTS_H 6 | #define OSM2GMNS_CONSTANTS_H 7 | 8 | constexpr double MICROSECONDS_TO_SECOND = 1e-6; 9 | constexpr float MPH_TO_KPH = 1.609; 10 | 11 | #endif // OSM2GMNS_CONSTANTS_H 12 | -------------------------------------------------------------------------------- /src/functions.cpp: -------------------------------------------------------------------------------- 1 | #include "functions.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "config.h" 12 | #include "io.h" 13 | #include "networks.h" 14 | #include "osmconfig.h" 15 | #include "osmnetwork.h" 16 | 17 | Network* getNetFromFile(const std::filesystem::path& osm_filepath, const absl::flat_hash_set& mode_types, 18 | const absl::flat_hash_set& link_types, 19 | const absl::flat_hash_set& connector_link_types, bool POI, 20 | float POI_sampling_ratio, const OsmParsingConfig* osm_parsing_config, bool strict_boundary) { 21 | absl::flat_hash_set connector_link_types_(connector_link_types); 22 | if (!connector_link_types_.empty()) { 23 | if (link_types.empty()) { 24 | LOG(WARNING) << "You are using the default value for the argument link_types, which includes all link types. The " 25 | "arguments link_types and connector_link_types should not contain any common elements. Elements " 26 | "in connector_link_types have been discarded."; 27 | connector_link_types_.clear(); 28 | } else { 29 | std::vector invalid_connector_link_types; 30 | for (const auto& link_type : link_types) { 31 | auto iter = connector_link_types_.find(link_type); 32 | if (iter != connector_link_types_.end()) { 33 | invalid_connector_link_types.push_back(link_type); 34 | connector_link_types_.erase(iter); 35 | } 36 | } 37 | if (!invalid_connector_link_types.empty()) { 38 | LOG(WARNING) << "The arguments link_types and connector_link_types should not contain any common elements. " 39 | "Duplicate elements have been removed from connector_link_types."; 40 | } 41 | } 42 | } 43 | LOG(INFO) << "loading data from osm file"; 44 | auto* osmnet = new OsmNetwork(osm_filepath, mode_types, link_types, connector_link_types_, POI, osm_parsing_config, 45 | strict_boundary); 46 | LOG(INFO) << "start to build network"; 47 | auto* network = new Network(osmnet, link_types, connector_link_types_, POI, POI_sampling_ratio, osm_parsing_config); 48 | LOG(INFO) << "build network done"; 49 | return network; 50 | }; 51 | 52 | void consolidateComplexIntersections(Network* network, bool auto_identify, 53 | const std::filesystem::path& intersection_file, float int_buffer) { 54 | if (!intersection_file.empty() && !std::filesystem::exists(intersection_file)) { 55 | LOG(ERROR) << "intersection file " << intersection_file 56 | << " does not exist. consolidateComplexIntersections() skipped"; 57 | return; 58 | } 59 | if (intersection_file.empty()) { 60 | network->consolidateComplexIntersections(auto_identify, {}, int_buffer); 61 | } else { 62 | const std::vector intersection_vector = readIntersectionFile(intersection_file); 63 | network->consolidateComplexIntersections(auto_identify, intersection_vector, int_buffer); 64 | } 65 | } 66 | 67 | void generateNodeActivityInfo(Network* network, const std::filesystem::path& zone_file) { 68 | if (!zone_file.empty() && !std::filesystem::exists(zone_file)) { 69 | LOG(ERROR) << "zone file " << zone_file << " does not exist. generateNodeActivityInfo() skipped"; 70 | return; 71 | } 72 | if (zone_file.empty()) { 73 | network->generateNodeActivityInfo(); 74 | } else { 75 | const std::vector zone_vector = readZoneFile(zone_file); 76 | network->generateNodeActivityInfo(zone_vector); 77 | } 78 | } 79 | 80 | void fillLinkAttributesWithDefaultValues(Network* network, bool default_lanes, 81 | const absl::flat_hash_map& default_lanes_dict, 82 | bool default_speed, 83 | const absl::flat_hash_map& default_speed_dict, 84 | bool default_capacity, 85 | const absl::flat_hash_map& default_capacity_dict) { 86 | absl::flat_hash_map default_lanes_dict_; 87 | if (default_lanes) { 88 | default_lanes_dict_ = getPresetDefaultLanesDict(); 89 | for (const auto& [link_type, default_val] : default_lanes_dict) { 90 | default_lanes_dict_[link_type] = default_val; 91 | } 92 | } 93 | absl::flat_hash_map default_speed_dict_; 94 | if (default_speed) { 95 | default_speed_dict_ = getPresetDefaultSpeedDict(); 96 | for (const auto& [link_type, default_val] : default_speed_dict) { 97 | default_speed_dict_[link_type] = default_val; 98 | } 99 | } 100 | absl::flat_hash_map default_capacity_dict_; 101 | if (default_capacity) { 102 | default_capacity_dict_ = getPresetDefaultCapacityDict(); 103 | for (const auto& [link_type, default_val] : default_capacity_dict) { 104 | default_capacity_dict_[link_type] = default_val; 105 | } 106 | } 107 | network->fillLinkAttributesWithDefaultValues(default_lanes_dict_, default_speed_dict_, default_capacity_dict_); 108 | } -------------------------------------------------------------------------------- /src/functions.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jiawei Lu on 2/16/23. 3 | // 4 | 5 | #ifndef OSM2GMNS_FUNCTIONS_H 6 | #define OSM2GMNS_FUNCTIONS_H 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include "config.h" 15 | #include "networks.h" 16 | #include "osmconfig.h" 17 | 18 | constexpr float DEFAULT_INT_BUFFER = 20.0; 19 | 20 | Network* getNetFromFile(const std::filesystem::path& osm_filepath, 21 | const absl::flat_hash_set& mode_types = {ModeType::AUTO}, 22 | const absl::flat_hash_set& link_types = {}, 23 | const absl::flat_hash_set& connector_link_types = {}, bool POI = false, 24 | float POI_sampling_ratio = 1.0, const OsmParsingConfig* osm_parsing_config = nullptr, 25 | bool strict_boundary = true); 26 | 27 | void consolidateComplexIntersections(Network* network, bool auto_identify = false, 28 | const std::filesystem::path& intersection_file = "", 29 | float int_buffer = DEFAULT_INT_BUFFER); 30 | 31 | void generateNodeActivityInfo(Network* network, const std::filesystem::path& zone_file = ""); 32 | 33 | void fillLinkAttributesWithDefaultValues( 34 | Network* network, bool default_lanes = false, 35 | const absl::flat_hash_map& default_lanes_dict = {}, bool default_speed = false, 36 | const absl::flat_hash_map& default_speed_dict = {}, bool default_capacity = false, 37 | const absl::flat_hash_map& default_capacity_dict = {}); 38 | 39 | #endif // OSM2GMNS_FUNCTIONS_H 40 | -------------------------------------------------------------------------------- /src/io.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jiawei Lu on 2/16/23. 3 | // 4 | 5 | #include "io.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "csv.h" 29 | #include "networks.h" 30 | #include "osmconfig.h" 31 | 32 | constexpr int COORDINATE_OUTPUT_PRECISION = 7; 33 | constexpr int LENGTH_OUTPUT_PRECISION = 2; 34 | constexpr int AREA_OUTPUT_PRECISION = 1; 35 | 36 | std::string getHighWayLinkTypeStr(const HighWayLinkType& highway_link_type) { 37 | static const absl::flat_hash_map highway_link_type_dict = { 38 | {HighWayLinkType::MOTORWAY, "motorway"}, 39 | {HighWayLinkType::TRUNK, "trunk"}, 40 | {HighWayLinkType::PRIMARY, "primary"}, 41 | {HighWayLinkType::SECONDARY, "secondary"}, 42 | {HighWayLinkType::TERTIARY, "tertiary"}, 43 | {HighWayLinkType::RESIDENTIAL, "residential"}, 44 | {HighWayLinkType::LIVING_STREET, "living_street"}, 45 | {HighWayLinkType::SERVICE, "service"}, 46 | {HighWayLinkType::CYCLEWAY, "cycleway"}, 47 | {HighWayLinkType::FOOTWAY, "footway"}, 48 | {HighWayLinkType::TRACK, "track"}, 49 | {HighWayLinkType::UNCLASSIFIED, "unclassified"}, 50 | {HighWayLinkType::OTHER, "other"}}; 51 | auto iter = highway_link_type_dict.find(highway_link_type); 52 | return iter != highway_link_type_dict.end() ? iter->second : ""; 53 | } 54 | 55 | int32_t getHighWayLinkTypeNo(HighWayLinkType highway_link_type) { 56 | static const absl::flat_hash_map link_type_no_map = { 57 | {HighWayLinkType::MOTORWAY, 1}, {HighWayLinkType::TRUNK, 2}, {HighWayLinkType::PRIMARY, 3}, 58 | {HighWayLinkType::SECONDARY, 4}, {HighWayLinkType::TERTIARY, 5}, {HighWayLinkType::RESIDENTIAL, 6}, 59 | {HighWayLinkType::LIVING_STREET, 7}, {HighWayLinkType::SERVICE, 8}, {HighWayLinkType::CYCLEWAY, 9}, 60 | {HighWayLinkType::FOOTWAY, 10}, {HighWayLinkType::TRACK, 11}, {HighWayLinkType::UNCLASSIFIED, 20}, 61 | {HighWayLinkType::OTHER, 21}}; 62 | 63 | auto iter = link_type_no_map.find(highway_link_type); 64 | if (iter != link_type_no_map.end()) { 65 | return iter->second; 66 | } 67 | return -1; 68 | } 69 | 70 | int32_t getRailwayLinkTypeNo() { 71 | static const int32_t railway_link_type_no = 30; 72 | return railway_link_type_no; 73 | } 74 | 75 | int32_t getAerowayLinkTypeNo() { 76 | static const int32_t aeroway_link_type_no = 40; 77 | return aeroway_link_type_no; 78 | } 79 | 80 | std::string getModeTypeStr(const ModeType& mode_type) { 81 | static const absl::flat_hash_map mode_type_dict = {{ModeType::AUTO, "auto"}, 82 | {ModeType::BIKE, "bike"}, 83 | {ModeType::WALK, "walk"}, 84 | {ModeType::RAILWAY, "railway"}, 85 | {ModeType::AEROWAY, "aeroway"}}; 86 | auto iter = mode_type_dict.find(mode_type); 87 | return iter != mode_type_dict.end() ? iter->second : ""; 88 | } 89 | 90 | void outputNetToCSV(const Network* network, const std::filesystem::path& output_folder) { 91 | LOG(INFO) << "writing network to csv files"; 92 | 93 | const std::filesystem::path node_filepath = output_folder / "node.csv"; 94 | std::ofstream node_file(node_filepath); 95 | if (!node_file) { 96 | LOG(ERROR) << "Cannot open file " << node_filepath; 97 | return; 98 | } 99 | node_file << "name,node_id,osm_node_id,ctrl_type,x_coord,y_coord"; 100 | for (const std::string& attr_name : network->osmParsingConfig()->osm_node_attributes) { 101 | node_file << "," << attr_name; 102 | } 103 | node_file << ",is_boundary,activity_type,poi_id,zone_id,notes\n"; 104 | 105 | for (const Node* node : network->nodeVector()) { 106 | const std::string& name = absl::StrContains(node->name(), ',') ? "\"" + node->name() + "\"" : node->name(); 107 | const std::string& ctrl_type = node->isSignalized() ? "signal" : ""; 108 | const std::string& zone_id = node->zoneId().has_value() ? std::to_string(node->zoneId().value()) : ""; // NOLINT 109 | const std::string& boundary = 110 | node->boundary().has_value() ? std::to_string(node->boundary().value()) : ""; // NOLINT 111 | const std::string& activity_type = 112 | node->activityType().has_value() ? getHighWayLinkTypeStr(node->activityType().value()) : ""; // NOLINT 113 | node_file << name << "," << node->nodeId() << "," << node->osmNodeId() << "," << ctrl_type << "," << std::fixed 114 | << std::setprecision(COORDINATE_OUTPUT_PRECISION) << node->geometry()->getX() << "," 115 | << node->geometry()->getY() << std::defaultfloat; 116 | for (const std::string& attr_value : node->osmAttributes()) { 117 | node_file << "," << (absl::StrContains(attr_value, ',') ? "\"" + attr_value + "\"" : attr_value); 118 | } 119 | node_file << "," << boundary << "," << activity_type << ",," << zone_id << ",\n"; 120 | } 121 | node_file.close(); 122 | 123 | const std::filesystem::path link_filepath = output_folder / "link.csv"; 124 | std::ofstream link_file(link_filepath); 125 | if (!link_file) { 126 | std::cout << "Cannot open file " << link_filepath; 127 | return; 128 | } 129 | link_file << "link_id,name,osm_way_id,from_node_id,to_node_id,directed,geometry,dir_flag,length,facility_type,link_" 130 | "type,free_speed,lanes,capacity,allowed_uses"; 131 | for (const std::string& attr_name : network->osmParsingConfig()->osm_link_attributes) { 132 | link_file << "," << attr_name; 133 | } 134 | link_file << ",notes\n"; 135 | for (const Link* link : network->linkVector()) { 136 | const std::string& name = absl::StrContains(link->name(), ',') ? "\"" + link->name() + "\"" : link->name(); 137 | const std::string& facility_type = 138 | link->wayType() == WayType::HIGHWAY 139 | ? getHighWayLinkTypeStr(link->highwayLinkType()) 140 | : (link->wayType() == WayType::RAILWAY 141 | ? link->railwayLinkType() 142 | : (link->wayType() == WayType::AEROWAY ? link->aerowayLinkType() : "")); 143 | const std::string& type_no = 144 | link->wayType() == WayType::HIGHWAY 145 | ? std::to_string(getHighWayLinkTypeNo(link->highwayLinkType())) 146 | : (link->wayType() == WayType::RAILWAY 147 | ? std::to_string(getRailwayLinkTypeNo()) 148 | : (link->wayType() == WayType::AEROWAY ? std::to_string(getAerowayLinkTypeNo()) : "")); 149 | const std::string& lanes = link->lanes().has_value() ? std::to_string(link->lanes().value()) : ""; // NOLINT 150 | std::string free_speed; 151 | if (link->freeSpeed().has_value()) { 152 | std::ostringstream oss; 153 | oss << std::fixed << std::setprecision(0) << link->freeSpeed().value(); // NOLINT 154 | free_speed = oss.str(); 155 | } 156 | const std::string& capacity = 157 | link->capacity().has_value() ? std::to_string(link->capacity().value()) : ""; // NOLINT 158 | std::string allowed_uses = getModeTypeStr(link->allowedModeTypes().at(0)); 159 | for (size_t idx = 1; idx < link->allowedModeTypes().size(); ++idx) { 160 | allowed_uses += ";" + getModeTypeStr(link->allowedModeTypes().at(idx)); 161 | } 162 | 163 | link_file << link->linkId() << "," << name << "," << link->osmWayId() << "," << link->fromNode()->nodeId() << "," 164 | << link->toNode()->nodeId() << ",1,\"" << link->geometry()->toString() << "\",1," << std::fixed 165 | << std::setprecision(LENGTH_OUTPUT_PRECISION) << link->length() << "," << facility_type << "," << type_no 166 | << "," << free_speed << "," << lanes << "," << capacity << "," << allowed_uses; 167 | for (const std::string& attr_value : link->osmAttributes()) { 168 | link_file << "," << (absl::StrContains(attr_value, ',') ? "\"" + attr_value + "\"" : attr_value); 169 | } 170 | link_file << ",\n"; 171 | } 172 | link_file.close(); 173 | 174 | if (!network->poi()) { 175 | return; 176 | } 177 | const std::filesystem::path poi_filepath = output_folder / "poi.csv"; 178 | std::ofstream poi_file(poi_filepath); 179 | if (!poi_file) { 180 | std::cout << "Cannot open file " << poi_filepath; 181 | return; 182 | } 183 | poi_file << "name,poi_id,osm_way_id,osm_relation_id,building,amenity,leisure,way,geometry,centroid,area,area_ft2"; 184 | for (const std::string& attr_name : network->osmParsingConfig()->osm_poi_attributes) { 185 | poi_file << "," << attr_name; 186 | } 187 | poi_file << "\n"; 188 | for (const POI* poi : network->poiVector()) { 189 | const std::string& name = absl::StrContains(poi->name(), ',') ? "\"" + poi->name() + "\"" : poi->name(); 190 | const std::string osm_way_id = 191 | poi->osmWayId().has_value() ? std::to_string(poi->osmWayId().value()) : ""; // NOLINT 192 | const std::string osm_relation_id = 193 | poi->osmRelationId().has_value() ? std::to_string(poi->osmRelationId().value()) : ""; // NOLINT 194 | const std::string& building = 195 | absl::StrContains(poi->building(), ',') ? "\"" + poi->building() + "\"" : poi->building(); 196 | const std::string& amenity = absl::StrContains(poi->amenity(), ',') ? "\"" + poi->amenity() + "\"" : poi->amenity(); 197 | const std::string& leisure = absl::StrContains(poi->leisure(), ',') ? "\"" + poi->leisure() + "\"" : poi->leisure(); 198 | poi_file << name << "," << poi->poiId() << "," << osm_way_id << "," << osm_relation_id << "," << building << "," 199 | << amenity << "," << leisure << ",,\"" << poi->geometry()->toString() << "\",\"" 200 | << poi->centroidGeometry()->toString() << "\"," << std::fixed << std::setprecision(AREA_OUTPUT_PRECISION) 201 | << poi->area() << ","; 202 | for (const std::string& attr_value : poi->osmAttributes()) { 203 | poi_file << "," << (absl::StrContains(attr_value, ',') ? "\"" + attr_value + "\"" : attr_value); 204 | } 205 | poi_file << "\n"; 206 | } 207 | poi_file.close(); 208 | 209 | LOG(INFO) << "write network done"; 210 | } 211 | 212 | std::vector readZoneFile(const std::filesystem::path& zone_file) { 213 | if (!std::filesystem::exists(zone_file)) { 214 | LOG(ERROR) << "zone file " << zone_file << " does not exist"; 215 | return {}; 216 | } 217 | 218 | constexpr int16_t number_of_columns = 4; 219 | io::CSVReader, io::double_quote_escape<',', '\"'>> in_file( 220 | zone_file.string().c_str()); 221 | bool has_geometry_info = false; 222 | bool has_coord_info = false; 223 | in_file.read_header(io::ignore_missing_column, "zone_id", "x_coord", "y_coord", "geometry"); 224 | if (in_file.has_column("geometry")) { 225 | has_geometry_info = true; 226 | } 227 | if (in_file.has_column("x_coord") && in_file.has_column("y_coord")) { 228 | has_coord_info = true; 229 | } 230 | if (!has_geometry_info && !has_coord_info) { 231 | LOG(ERROR) << "zone file should have either x_coord y_coord or geometry information. zone file will not be loaded"; 232 | return {}; 233 | } 234 | 235 | std::vector zone_vector; 236 | NetIdType zone_id = 0; 237 | double x_coord = 0.0; 238 | double y_coord = 0.0; 239 | std::string geometry_str; 240 | geos::geom::GeometryFactory::Ptr factory = geos::geom::GeometryFactory::create(); 241 | const geos::io::WKTReader reader(factory.get()); 242 | while (in_file.read_row(zone_id, x_coord, y_coord, geometry_str)) { 243 | std::unique_ptr geometry; 244 | if (has_geometry_info) { 245 | geometry = reader.read(geometry_str); 246 | if (geometry->getGeometryTypeId() != geos::geom::GEOS_POINT && 247 | geometry->getGeometryTypeId() != geos::geom::GEOS_POLYGON && 248 | geometry->getGeometryTypeId() != geos::geom::GEOS_MULTIPOLYGON) { 249 | LOG(WARNING) << "unsupported geometry type in the zone file. values in the geometry column should have a type " 250 | "of POINT, POLYGON, or MULTIPOLYGON"; 251 | continue; 252 | } 253 | } else if (has_coord_info) { 254 | geometry = factory->createPoint(geos::geom::Coordinate(x_coord, y_coord)); 255 | } 256 | zone_vector.push_back(new Zone(zone_id, std::move(geometry))); 257 | } 258 | return zone_vector; 259 | } 260 | 261 | std::vector readIntersectionFile(const std::filesystem::path& intersection_file) { 262 | if (!std::filesystem::exists(intersection_file)) { 263 | LOG(ERROR) << "intersection file " << intersection_file << " does not exist"; 264 | return {}; 265 | } 266 | 267 | constexpr int16_t number_of_columns = 4; 268 | io::CSVReader, io::double_quote_escape<',', '\"'>> in_file( 269 | intersection_file.string().c_str()); 270 | in_file.read_header(io::ignore_extra_column, "intersection_id", "x_coord", "y_coord", "int_buffer"); 271 | if (!(in_file.has_column("intersection_id") && in_file.has_column("x_coord") && in_file.has_column("y_coord") && 272 | in_file.has_column("int_buffer"))) { 273 | LOG(ERROR) 274 | << "intersection file should have intersection_id, x_coord and y_coord columns. the file will be not loaded"; 275 | return {}; 276 | } 277 | 278 | std::vector int_vector; 279 | NetIdType int_id = 0; 280 | double x_coord = 0.0; 281 | double y_coord = 0.0; 282 | float int_buffer = 0.0; 283 | geos::geom::GeometryFactory::Ptr factory = geos::geom::GeometryFactory::create(); 284 | absl::flat_hash_set loaded_int_ids; 285 | while (in_file.read_row(int_id, x_coord, y_coord, int_buffer)) { 286 | if (loaded_int_ids.contains(int_id)) { 287 | LOG(WARNING) << "intersection id " << int_id << " is duplicated in the intersection file"; 288 | continue; 289 | } 290 | std::unique_ptr geometry = factory->createPoint(geos::geom::Coordinate(x_coord, y_coord)); 291 | int_vector.push_back(int_buffer > 0 ? new Intersection(int_id, std::move(geometry), int_buffer) 292 | : new Intersection(int_id, std::move(geometry))); 293 | loaded_int_ids.insert(int_id); 294 | } 295 | LOG(INFO) << "intersection file loaded. " << int_vector.size() << " intersections"; 296 | return int_vector; 297 | } -------------------------------------------------------------------------------- /src/io.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jiawei Lu on 2/16/23. 3 | // 4 | 5 | #ifndef OSM2GMNS_IO_H 6 | #define OSM2GMNS_IO_H 7 | 8 | #include 9 | #include 10 | 11 | #include "networks.h" 12 | 13 | void outputNetToCSV(const Network* network, const std::filesystem::path& output_folder); 14 | 15 | std::vector readZoneFile(const std::filesystem::path& zone_file); 16 | 17 | std::vector readIntersectionFile(const std::filesystem::path& intersection_file); 18 | 19 | #endif // OSM2GMNS_IO_H 20 | -------------------------------------------------------------------------------- /src/networks.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jiawei Lu on 2/16/23. 3 | // 4 | 5 | #ifndef OSM2GMNS_NETWORKS_H 6 | #define OSM2GMNS_NETWORKS_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "config.h" 24 | #include "osmconfig.h" 25 | #include "osmnetwork.h" 26 | 27 | using NetIdType = int64_t; 28 | 29 | class Node; 30 | class Link; 31 | 32 | class Node { 33 | public: 34 | explicit Node(const OsmNode* osm_node, const geos::geom::GeometryFactory* factory); 35 | explicit Node(NetIdType node_id, const std::vector& nodes, NetIdType intersection_id, 36 | const geos::geom::GeometryFactory* factory); 37 | 38 | void setNodeId(NetIdType node_id); 39 | void setZoneId(NetIdType zone_id); 40 | void setBoundary(int16_t boundary); 41 | void setActivityType(HighWayLinkType activity_type); 42 | void setIntersectionId(NetIdType intersection_id); 43 | // void setIsValid(bool is_valid); 44 | void addIncomingLink(Link* link); 45 | void addOutgoingLink(Link* link); 46 | 47 | [[nodiscard]] NetIdType nodeId() const; 48 | [[nodiscard]] const std::vector& osmNodes() const; 49 | [[nodiscard]] std::string osmNodeId() const; 50 | [[nodiscard]] const std::string& name() const; 51 | [[nodiscard]] bool isSignalized() const; 52 | [[nodiscard]] const std::unique_ptr& geometry() const; 53 | [[nodiscard]] const std::vector& osmAttributes() const; 54 | [[nodiscard]] std::optional zoneId() const; 55 | [[nodiscard]] std::optional boundary() const; 56 | [[nodiscard]] std::optional activityType() const; 57 | [[nodiscard]] std::optional intersectionId() const; 58 | // [[nodiscard]] bool isValid() const; 59 | [[nodiscard]] const std::vector& incomingLinkVector() const; 60 | [[nodiscard]] const std::vector& outgoingLinkVector() const; 61 | 62 | private: 63 | NetIdType node_id_{-1}; 64 | std::vector osm_nodes_; 65 | std::string name_; 66 | bool is_signalized_{false}; 67 | std::unique_ptr geometry_; 68 | std::vector osm_attributes_; 69 | 70 | std::optional zone_id_; 71 | // boundary: 0 - not a boundary node; -1 - incoming only; 1 - outgoing only; 2 - both incoming and outgoing 72 | std::optional boundary_; 73 | std::optional activity_type_; 74 | std::optional intersection_id_; 75 | // bool is_valid_{true}; 76 | // unsigned long osm_node_id{}; 77 | // std::string osm_highway{}; 78 | // std::string ctrl_type{}; 79 | // Geometry* geometry{}; 80 | // double x{}, y{}; 81 | // std::string notes{}; 82 | 83 | std::vector incoming_link_vector_; 84 | std::vector outgoing_link_vector_; 85 | }; 86 | 87 | class Link { 88 | public: 89 | explicit Link(Node* from_node, Node* to_node); 90 | explicit Link(const OsmWay* osm_way, const std::vector& osm_nodes, bool forward_direction, 91 | size_t osm_way_seq_, const geos::geom::GeometryFactory* factory); 92 | 93 | [[nodiscard]] NetIdType linkId() const; 94 | [[nodiscard]] OsmIdType osmWayId() const; 95 | [[nodiscard]] size_t osmWaySeq() const; 96 | [[nodiscard]] const std::string& name() const; 97 | [[nodiscard]] OsmNode* fromOsmNode() const; 98 | [[nodiscard]] OsmNode* toOsmNode() const; 99 | [[nodiscard]] Node* fromNode() const; 100 | [[nodiscard]] Node* toNode() const; 101 | [[nodiscard]] WayType wayType() const; 102 | [[nodiscard]] HighWayLinkType highwayLinkType() const; 103 | [[nodiscard]] const std::string& railwayLinkType() const; 104 | [[nodiscard]] const std::string& aerowayLinkType() const; 105 | [[nodiscard]] const std::unique_ptr& geometry() const; 106 | [[nodiscard]] double length() const; 107 | [[nodiscard]] std::optional lanes() const; 108 | [[nodiscard]] std::optional freeSpeed() const; 109 | [[nodiscard]] std::optional capacity() const; 110 | [[nodiscard]] const std::vector& allowedModeTypes() const; 111 | [[nodiscard]] const std::vector& osmAttributes() const; 112 | 113 | void setLinkId(NetIdType link_id); 114 | void setFromNode(Node* from_node); 115 | void setToNode(Node* to_node); 116 | void setLanes(int32_t lanes); 117 | void setFreeSpeed(float free_speed); 118 | void setCapacity(int32_t capacity); 119 | // void setIsValid(bool is_valid); 120 | 121 | private: 122 | NetIdType link_id_{-1}; 123 | OsmIdType osm_way_id_{-1}; 124 | size_t osm_way_seq_{0}; 125 | std::string name_; 126 | OsmNode* from_osm_node_{nullptr}; 127 | OsmNode* to_osm_node_{nullptr}; 128 | Node* from_node_{nullptr}; 129 | Node* to_node_{nullptr}; 130 | 131 | WayType way_type_{WayType::OTHER}; 132 | HighWayLinkType highway_link_type_{HighWayLinkType::OTHER}; 133 | std::string railway_link_type_; 134 | std::string aeroway_link_type_; 135 | std::unique_ptr geometry_; 136 | double length_{-1.0}; 137 | // bool is_valid_{true}; 138 | std::optional lanes_; 139 | std::optional free_speed_; 140 | std::optional capacity_; 141 | std::vector allowed_mode_types_; 142 | std::vector osm_attributes_; 143 | }; 144 | 145 | class POI { 146 | public: 147 | explicit POI(const OsmWay* osm_way, std::unique_ptr geometry); 148 | explicit POI(const OsmRelation* osm_relation, std::unique_ptr geometry); 149 | 150 | [[nodiscard]] NetIdType poiId() const; 151 | [[nodiscard]] const std::string& name() const; 152 | [[nodiscard]] std::optional osmWayId() const; 153 | [[nodiscard]] std::optional osmRelationId() const; 154 | [[nodiscard]] const std::string& building() const; 155 | [[nodiscard]] const std::string& amenity() const; 156 | [[nodiscard]] const std::string& leisure() const; 157 | [[nodiscard]] const std::vector& osmAttributes() const; 158 | [[nodiscard]] const std::unique_ptr& geometry() const; 159 | [[nodiscard]] const std::unique_ptr& centroidGeometry() const; 160 | [[nodiscard]] double area() const; 161 | 162 | void setPOIId(NetIdType poi_id); 163 | 164 | private: 165 | NetIdType poi_id_{-1}; 166 | std::string name_; 167 | std::optional osm_way_id_; 168 | std::optional osm_relation_id_; 169 | std::string building_; 170 | std::string amenity_; 171 | std::string leisure_; 172 | std::vector osm_attributes_; 173 | 174 | std::unique_ptr geometry_; 175 | std::unique_ptr centroid_geometry_; 176 | std::unique_ptr geometry_utm_; 177 | }; 178 | 179 | class Zone { 180 | public: 181 | explicit Zone(NetIdType zone_id, std::unique_ptr geometry); 182 | 183 | [[nodiscard]] NetIdType zoneId() const; 184 | [[nodiscard]] const std::unique_ptr& geometry() const; 185 | 186 | private: 187 | NetIdType zone_id_; 188 | std::unique_ptr geometry_; 189 | }; 190 | 191 | class Intersection { 192 | public: 193 | explicit Intersection(NetIdType intersection_id, std::unique_ptr geometry); 194 | explicit Intersection(NetIdType intersection_id, std::unique_ptr geometry, float int_buffer); 195 | 196 | [[nodiscard]] NetIdType intersectionId() const; 197 | [[nodiscard]] const std::unique_ptr& geometry() const; 198 | [[nodiscard]] std::optional intBuffer() const; 199 | 200 | private: 201 | NetIdType intersection_id_; 202 | std::unique_ptr geometry_; 203 | std::optional int_buffer_; 204 | }; 205 | 206 | class Network { 207 | public: 208 | explicit Network(OsmNetwork* osmnet, absl::flat_hash_set link_types, 209 | absl::flat_hash_set connector_link_types, bool POI, float POI_sampling_ratio, 210 | const OsmParsingConfig* osm_parsing_config); 211 | ~Network(); 212 | Network(const Network&) = delete; 213 | Network& operator=(const Network&) = delete; 214 | Network(Network&&) = delete; 215 | Network& operator=(Network&&) = delete; 216 | 217 | [[nodiscard]] bool poi() const; 218 | [[nodiscard]] const OsmParsingConfig* osmParsingConfig() const; 219 | [[nodiscard]] size_t numberOfNodes() const; 220 | [[nodiscard]] size_t numberOfLinks() const; 221 | [[nodiscard]] const std::vector& nodeVector() const; 222 | [[nodiscard]] const std::vector& linkVector() const; 223 | [[nodiscard]] const std::vector& poiVector() const; 224 | 225 | void generateNodeActivityInfo(const std::vector& zone_vector = {}); 226 | void fillLinkAttributesWithDefaultValues(const absl::flat_hash_map& default_lanes_dict, 227 | const absl::flat_hash_map& default_speed_dict, 228 | const absl::flat_hash_map& default_capacity_dict); 229 | void consolidateComplexIntersections(bool auto_identify, const std::vector& intersection_vector, 230 | float int_buffer); 231 | 232 | private: 233 | void createNodesAndLinksFromOsmNetwork(); 234 | void createLinksFromWay(const OsmWay* osm_way, std::vector>& m_link_vector); 235 | [[nodiscard]] std::vector identifyConnectorWays() const; 236 | void createPOIsFromOsmNetwork(); 237 | void createPOIsFromOsmWays(std::vector>& m_poi_vector); 238 | void createPOIsFromOneOsmWay(const OsmWay* osm_way, std::vector>& m_poi_vector); 239 | void createPOIsFromOsmRelations(std::vector>& m_poi_vector); 240 | void createPOIsFromOneOsmRelation(const OsmRelation* osm_relation, std::vector>& m_poi_vector); 241 | 242 | void designateComplexIntersectionsFromIntFile(const std::vector& intersection_vector, 243 | float int_buffer); 244 | void identifyComplexIntersections(float int_buffer); 245 | 246 | OsmNetwork* osmnet_; 247 | std::optional> boundary_; 248 | geos::geom::GeometryFactory::Ptr factory_; 249 | absl::flat_hash_set link_types_; 250 | absl::flat_hash_set connector_link_types_; 251 | bool POI_; 252 | float POI_sampling_ratio_; 253 | const OsmParsingConfig* osm_parsing_config_; 254 | 255 | // absl::flat_hash_map node_dict_; 256 | // absl::flat_hash_map link_dict_; 257 | std::vector node_vector_; 258 | std::vector link_vector_; 259 | std::vector poi_vector_; 260 | 261 | NetIdType max_node_id_{1}; 262 | NetIdType max_link_id_{1}; 263 | NetIdType max_intersection_id_{1}; 264 | }; 265 | 266 | #endif // OSM2GMNS_NETWORKS_H 267 | -------------------------------------------------------------------------------- /src/osmconfig.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Lu, Jiawei on 6/3/24. 3 | // 4 | 5 | #include "osmconfig.h" 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | ModeType modeStringToModeType(const std::string& mode_type_str) { 14 | static const absl::flat_hash_map mode_type_map = {{"auto", ModeType::AUTO}, 15 | {"bike", ModeType::BIKE}, 16 | {"walk", ModeType::WALK}, 17 | {"railway", ModeType::RAILWAY}, 18 | {"aeroway", ModeType::AEROWAY}}; 19 | auto iter = mode_type_map.find(mode_type_str); 20 | if (iter != mode_type_map.end()) { 21 | return iter->second; 22 | } 23 | return ModeType::OTHER; 24 | } 25 | 26 | HighWayLinkType highwayStringToHighWayLinkType(const std::string& highway_type_str) { 27 | static const absl::flat_hash_map link_type_map = { 28 | {"motorway", HighWayLinkType::MOTORWAY}, 29 | {"motorway_link", HighWayLinkType::MOTORWAY}, 30 | {"trunk", HighWayLinkType::TRUNK}, 31 | {"trunk_link", HighWayLinkType::TRUNK}, 32 | {"primary", HighWayLinkType::PRIMARY}, 33 | {"primary_link", HighWayLinkType::PRIMARY}, 34 | {"secondary", HighWayLinkType::SECONDARY}, 35 | {"secondary_link", HighWayLinkType::SECONDARY}, 36 | {"tertiary", HighWayLinkType::TERTIARY}, 37 | {"tertiary_link", HighWayLinkType::TERTIARY}, 38 | {"residential", HighWayLinkType::RESIDENTIAL}, 39 | {"residential_link", HighWayLinkType::RESIDENTIAL}, 40 | {"living_street", HighWayLinkType::LIVING_STREET}, 41 | {"service", HighWayLinkType::SERVICE}, 42 | {"services", HighWayLinkType::SERVICE}, 43 | {"cycleway", HighWayLinkType::CYCLEWAY}, 44 | {"footway", HighWayLinkType::FOOTWAY}, 45 | {"pedestrian", HighWayLinkType::FOOTWAY}, 46 | {"steps", HighWayLinkType::FOOTWAY}, 47 | {"track", HighWayLinkType::TRACK}, 48 | {"unclassified", HighWayLinkType::UNCLASSIFIED}}; 49 | 50 | auto iter = link_type_map.find(highway_type_str); 51 | if (iter != link_type_map.end()) { 52 | return iter->second; 53 | } 54 | return HighWayLinkType::OTHER; 55 | } 56 | 57 | bool checkAllowedUsedAutoInMotor_Vehicle(const std::string& motor_vehicle) { 58 | static const absl::flat_hash_set value_set = {"yes"}; 59 | return value_set.find(motor_vehicle) != value_set.end(); 60 | } 61 | bool checkAllowedUsedAutoInMotorCar(const std::string& motorcar) { 62 | static const absl::flat_hash_set value_set = {"yes"}; 63 | return value_set.find(motorcar) != value_set.end(); 64 | } 65 | bool checkAllowedUsedBikeInBicycle(const std::string& bicycle) { 66 | static const absl::flat_hash_set value_set = {"yes"}; 67 | return value_set.find(bicycle) != value_set.end(); 68 | } 69 | bool checkAllowedUsedWalkInFoot(const std::string& foot) { 70 | static const absl::flat_hash_set value_set = {"yes"}; 71 | return value_set.find(foot) != value_set.end(); 72 | } 73 | 74 | bool checkAllowedUsedAutoExHighway(const std::string& highway) { 75 | static const absl::flat_hash_set value_set = {"cycleway", "footway", "pedestrian", "steps", 76 | "track", "corridor", "elevator", "escalator", 77 | "service", "living_street"}; 78 | return value_set.find(highway) != value_set.end(); 79 | } 80 | bool checkAllowedUsedAutoExMotor_Vehicle(const std::string& motor_vehicle) { 81 | static const absl::flat_hash_set value_set = {"no"}; 82 | return value_set.find(motor_vehicle) != value_set.end(); 83 | } 84 | bool checkAllowedUsedAutoExMotorCar(const std::string& motorcar) { 85 | static const absl::flat_hash_set value_set = {"no"}; 86 | return value_set.find(motorcar) != value_set.end(); 87 | } 88 | bool checkAllowedUsedAutoExAccess(const std::string& access) { 89 | static const absl::flat_hash_set value_set = {"private"}; 90 | return value_set.find(access) != value_set.end(); 91 | } 92 | bool checkAllowedUsedAutoExService(const std::string& service) { 93 | static const absl::flat_hash_set value_set = {"parking", "parking_aisle", "driveway", "private", 94 | "emergency_access"}; 95 | return value_set.find(service) != value_set.end(); 96 | } 97 | bool checkAllowedUsedBikeExHighway(const std::string& highway) { 98 | static const absl::flat_hash_set value_set = {"footway", "steps", "corridor", "elevator", 99 | "escalator", "motor", "motorway", "motorway_link"}; 100 | return value_set.find(highway) != value_set.end(); 101 | } 102 | bool checkAllowedUsedBikeExBicycle(const std::string& bicycle) { 103 | static const absl::flat_hash_set value_set = {"no"}; 104 | return value_set.find(bicycle) != value_set.end(); 105 | } 106 | bool checkAllowedUsedBikeExService(const std::string& service) { 107 | static const absl::flat_hash_set value_set = {"private"}; 108 | return value_set.find(service) != value_set.end(); 109 | } 110 | bool checkAllowedUsedBikeExAccess(const std::string& access) { 111 | static const absl::flat_hash_set value_set = {"private"}; 112 | return value_set.find(access) != value_set.end(); 113 | } 114 | bool checkAllowedUsedWalkExHighway(const std::string& highway) { 115 | static const absl::flat_hash_set value_set = {"cycleway", "motor", "motorway", "motorway_link"}; 116 | return value_set.find(highway) != value_set.end(); 117 | } 118 | bool checkAllowedUsedWalkExFoot(const std::string& foot) { 119 | static const absl::flat_hash_set value_set = {"no"}; 120 | return value_set.find(foot) != value_set.end(); 121 | } 122 | bool checkAllowedUsedWalkExService(const std::string& service) { 123 | static const absl::flat_hash_set value_set = {"private"}; 124 | return value_set.find(service) != value_set.end(); 125 | } 126 | bool checkAllowedUsedWalkExAccess(const std::string& access) { 127 | static const absl::flat_hash_set value_set = {"private"}; 128 | return value_set.find(access) != value_set.end(); 129 | } 130 | 131 | bool getDefaultOneWayFlag(HighWayLinkType highway_link_type) { 132 | static const absl::flat_hash_map default_oneway_dict = { 133 | {HighWayLinkType::MOTORWAY, true}, {HighWayLinkType::TRUNK, false}, 134 | {HighWayLinkType::PRIMARY, false}, {HighWayLinkType::SECONDARY, false}, 135 | {HighWayLinkType::TERTIARY, false}, {HighWayLinkType::RESIDENTIAL, false}, 136 | {HighWayLinkType::LIVING_STREET, false}, {HighWayLinkType::SERVICE, false}, 137 | {HighWayLinkType::CYCLEWAY, true}, {HighWayLinkType::FOOTWAY, true}, 138 | {HighWayLinkType::TRACK, true}, {HighWayLinkType::UNCLASSIFIED, false}, 139 | {HighWayLinkType::OTHER, false}}; 140 | return default_oneway_dict.at(highway_link_type); 141 | } 142 | 143 | const absl::flat_hash_map& getPresetDefaultLanesDict() { 144 | static const absl::flat_hash_map default_lanes_dict = { 145 | {HighWayLinkType::MOTORWAY, 4}, {HighWayLinkType::TRUNK, 3}, {HighWayLinkType::PRIMARY, 3}, 146 | {HighWayLinkType::SECONDARY, 2}, {HighWayLinkType::TERTIARY, 2}, {HighWayLinkType::RESIDENTIAL, 1}, 147 | {HighWayLinkType::LIVING_STREET, 1}, {HighWayLinkType::SERVICE, 1}, {HighWayLinkType::CYCLEWAY, 1}, 148 | {HighWayLinkType::FOOTWAY, 1}, {HighWayLinkType::TRACK, 1}, {HighWayLinkType::UNCLASSIFIED, 1}, 149 | {HighWayLinkType::OTHER, 1}}; 150 | return default_lanes_dict; 151 | } 152 | const absl::flat_hash_map& getPresetDefaultSpeedDict() { 153 | static const absl::flat_hash_map default_speed_dict = { 154 | {HighWayLinkType::MOTORWAY, 120}, {HighWayLinkType::TRUNK, 100}, {HighWayLinkType::PRIMARY, 80}, 155 | {HighWayLinkType::SECONDARY, 60}, {HighWayLinkType::TERTIARY, 40}, {HighWayLinkType::RESIDENTIAL, 30}, 156 | {HighWayLinkType::LIVING_STREET, 30}, {HighWayLinkType::SERVICE, 30}, {HighWayLinkType::CYCLEWAY, 5}, 157 | {HighWayLinkType::FOOTWAY, 5}, {HighWayLinkType::TRACK, 30}, {HighWayLinkType::UNCLASSIFIED, 30}, 158 | {HighWayLinkType::OTHER, 30}}; 159 | return default_speed_dict; 160 | } 161 | const absl::flat_hash_map& getPresetDefaultCapacityDict() { 162 | static const absl::flat_hash_map default_capacity_dict = { 163 | {HighWayLinkType::MOTORWAY, 2300}, {HighWayLinkType::TRUNK, 2200}, {HighWayLinkType::PRIMARY, 1800}, 164 | {HighWayLinkType::SECONDARY, 1600}, {HighWayLinkType::TERTIARY, 1200}, {HighWayLinkType::RESIDENTIAL, 1000}, 165 | {HighWayLinkType::LIVING_STREET, 1000}, {HighWayLinkType::SERVICE, 800}, {HighWayLinkType::CYCLEWAY, 800}, 166 | {HighWayLinkType::FOOTWAY, 800}, {HighWayLinkType::TRACK, 800}, {HighWayLinkType::UNCLASSIFIED, 800}, 167 | {HighWayLinkType::OTHER, 800}}; 168 | return default_capacity_dict; 169 | } 170 | 171 | bool isHighwayPoiType(const std::string& highway) { 172 | static const absl::flat_hash_set highway_poi_set = {"bus_stop", "platform"}; 173 | return highway_poi_set.find(highway) != highway_poi_set.end(); 174 | } 175 | bool isRailwayPoiType(const std::string& railway) { 176 | static const absl::flat_hash_set railway_poi_set = { 177 | "depot", "station", "workshop", "halt", "interlocking", "junction", "spur_junction", "terminal", "platform"}; 178 | return railway_poi_set.find(railway) != railway_poi_set.end(); 179 | } 180 | bool isAerowayPoiType(const std::string& aeroway) { 181 | static const absl::flat_hash_set aeroway_poi_set = {}; 182 | return aeroway_poi_set.find(aeroway) != aeroway_poi_set.end(); 183 | } 184 | 185 | bool isNegligibleHighwayType(const std::string& highway) { 186 | static const absl::flat_hash_set negligible_highway_type_set = { 187 | "path", "construction", "proposed", "raceway", "bridleway", "rest_area", "su", 188 | "road", "abandoned", "planned", "trailhead", "stairs", "dismantled", "disused", 189 | "razed", "access", "corridor", "stop", "elevator", "escape", "busway"}; 190 | return negligible_highway_type_set.find(highway) != negligible_highway_type_set.end(); 191 | } 192 | bool isNegligibleRailwayType(const std::string& railway) { 193 | static const absl::flat_hash_set negligible_railway_type_set = { 194 | "construction", "abandoned", "disused", "proposed", "planned", "dismantled", "razed", "ventilation_shaft"}; 195 | return negligible_railway_type_set.find(railway) != negligible_railway_type_set.end(); 196 | } 197 | bool isNegligibleAerowayType(const std::string& aeroway) { 198 | static const absl::flat_hash_set negligible_aeroway_type_set = {}; 199 | return negligible_aeroway_type_set.find(aeroway) != negligible_aeroway_type_set.end(); 200 | } -------------------------------------------------------------------------------- /src/osmconfig.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Lu, Jiawei on 5/31/24. 3 | // 4 | 5 | #ifndef OSM2GMNS_OSMCONFIG_H 6 | #define OSM2GMNS_OSMCONFIG_H 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | using OsmIdType = int64_t; 14 | 15 | enum class WayType : uint8_t { HIGHWAY, RAILWAY, AEROWAY, POI, POI_COMPONENT, OTHER }; 16 | 17 | enum class ModeType : uint8_t { AUTO, BIKE, WALK, RAILWAY, AEROWAY, OTHER }; 18 | 19 | enum class HighWayLinkType : uint8_t { 20 | MOTORWAY, 21 | TRUNK, 22 | PRIMARY, 23 | SECONDARY, 24 | TERTIARY, 25 | RESIDENTIAL, 26 | LIVING_STREET, 27 | SERVICE, 28 | CYCLEWAY, 29 | FOOTWAY, 30 | TRACK, 31 | UNCLASSIFIED, 32 | OTHER 33 | }; 34 | 35 | ModeType modeStringToModeType(const std::string& mode_type_str); 36 | HighWayLinkType highwayStringToHighWayLinkType(const std::string& highway_type_str); 37 | 38 | bool checkAllowedUsedAutoInMotor_Vehicle(const std::string& motor_vehicle); 39 | bool checkAllowedUsedAutoInMotorCar(const std::string& motorcar); 40 | bool checkAllowedUsedBikeInBicycle(const std::string& bicycle); 41 | bool checkAllowedUsedWalkInFoot(const std::string& foot); 42 | 43 | bool checkAllowedUsedAutoExHighway(const std::string& highway); 44 | bool checkAllowedUsedAutoExMotor_Vehicle(const std::string& motor_vehicle); 45 | bool checkAllowedUsedAutoExMotorCar(const std::string& motorcar); 46 | bool checkAllowedUsedAutoExAccess(const std::string& access); 47 | bool checkAllowedUsedAutoExService(const std::string& service); 48 | bool checkAllowedUsedBikeExHighway(const std::string& highway); 49 | bool checkAllowedUsedBikeExBicycle(const std::string& bicycle); 50 | bool checkAllowedUsedBikeExService(const std::string& service); 51 | bool checkAllowedUsedBikeExAccess(const std::string& access); 52 | bool checkAllowedUsedWalkExHighway(const std::string& highway); 53 | bool checkAllowedUsedWalkExFoot(const std::string& foot); 54 | bool checkAllowedUsedWalkExService(const std::string& service); 55 | bool checkAllowedUsedWalkExAccess(const std::string& access); 56 | 57 | bool getDefaultOneWayFlag(HighWayLinkType highway_link_type); 58 | 59 | const absl::flat_hash_map& getPresetDefaultLanesDict(); 60 | const absl::flat_hash_map& getPresetDefaultSpeedDict(); 61 | const absl::flat_hash_map& getPresetDefaultCapacityDict(); 62 | 63 | bool isHighwayPoiType(const std::string& highway); 64 | bool isRailwayPoiType(const std::string& railway); 65 | bool isAerowayPoiType(const std::string& aeroway); 66 | 67 | bool isNegligibleHighwayType(const std::string& highway); 68 | bool isNegligibleRailwayType(const std::string& railway); 69 | bool isNegligibleAerowayType(const std::string& aeroway); 70 | 71 | #endif // OSM2GMNS_OSMCONFIG_H 72 | -------------------------------------------------------------------------------- /src/osmnetwork.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Lu, Jiawei on 5/23/24. 3 | // 4 | 5 | #include "osmnetwork.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include // NOLINT 27 | #include // NOLINT 28 | #include // NOLINT 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include // NOLINT 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "config.h" 44 | #include "constants.h" 45 | #include "osmconfig.h" 46 | 47 | const char* getOSMTagValue(const osmium::TagList& tag_list, const char* tag_key) { 48 | const char* tag_value = tag_list[tag_key]; 49 | return tag_value != nullptr ? tag_value : ""; 50 | } 51 | 52 | OsmHandler::OsmHandler(const absl::flat_hash_set& mode_types, absl::flat_hash_set link_types, 53 | absl::flat_hash_set connector_link_types, bool POI, 54 | const OsmParsingConfig* osm_parsing_config) 55 | : link_types_(std::move(link_types)), 56 | connector_link_types_(std::move(connector_link_types)), 57 | POI_(POI), 58 | osm_parsing_config_(osm_parsing_config) { 59 | for (const ModeType mode_type : mode_types) { 60 | if (mode_type == ModeType::RAILWAY) { 61 | include_railway_ = true; 62 | } else if (mode_type == ModeType::AEROWAY) { 63 | include_aeroway_ = true; 64 | } else { 65 | highway_mode_types_.insert(mode_type); 66 | } 67 | } 68 | } 69 | void OsmHandler::node(const osmium::Node& node) { 70 | if (parse_node_ && nodes_used_in_ways_.find(node.id()) != nodes_used_in_ways_.end()) { 71 | osm_node_vector_.push_back(new OsmNode(node, osm_parsing_config_->osm_node_attributes)); 72 | } 73 | } 74 | void OsmHandler::way(const osmium::Way& way) { 75 | if (!parse_way_) { 76 | return; 77 | } 78 | auto* osm_way = new OsmWay(way); 79 | osm_way->identifyWayType(highway_mode_types_, include_railway_, include_aeroway_, link_types_, connector_link_types_, 80 | POI_, ways_used_in_relations_); 81 | if (!osm_way->includeTheWay()) { 82 | delete osm_way; 83 | return; 84 | } 85 | osm_way_vector_.push_back(osm_way); 86 | nodes_used_in_ways_.insert(osm_way->refNodeIdVector().begin(), osm_way->refNodeIdVector().end()); 87 | osm_way->updateOsmAttributes(way, osm_parsing_config_->osm_link_attributes, osm_parsing_config_->osm_poi_attributes); 88 | } 89 | void OsmHandler::relation(const osmium::Relation& relation) { 90 | if (!parse_relation_) { 91 | return; 92 | } 93 | auto* osm_relation = new OsmRelation(relation, osm_parsing_config_->osm_poi_attributes); 94 | if (osm_relation->building().empty() && osm_relation->amenity().empty() && osm_relation->leisure().empty()) { 95 | delete osm_relation; 96 | return; 97 | } 98 | osm_relation_vector_.push_back(osm_relation); 99 | const std::vector& member_id_vector = osm_relation->memberIdVector(); 100 | const std::vector& member_type_vector = osm_relation->memberTypeVector(); 101 | for (size_t idx = 0; idx < member_id_vector.size(); ++idx) { 102 | if (member_type_vector[idx] == osmium::item_type::way) { 103 | ways_used_in_relations_.emplace(member_id_vector[idx]); 104 | } 105 | } 106 | } 107 | 108 | void OsmHandler::updateParseTargets(bool parse_node, bool parse_way, bool parse_relation) { 109 | parse_node_ = parse_node; 110 | parse_way_ = parse_way; 111 | parse_relation_ = parse_relation; 112 | } 113 | 114 | std::vector& OsmHandler::osmNodeVector() { return osm_node_vector_; } 115 | std::vector& OsmHandler::osmWayVector() { return osm_way_vector_; } 116 | std::vector& OsmHandler::osmRelationVector() { return osm_relation_vector_; } 117 | 118 | OsmNode::OsmNode(const osmium::Node& node, const std::vector& osm_node_attributes) 119 | : osm_node_id_(node.id()), 120 | x(node.location().lon()), 121 | y(node.location().lat()), 122 | name_(getOSMTagValue(node.tags(), "name")), 123 | highway_(getOSMTagValue(node.tags(), "highway")) { 124 | if (absl::StrContains(highway_, "signal")) { 125 | is_signalized_ = true; 126 | } 127 | osm_attributes_.reserve(osm_node_attributes.size()); 128 | for (const std::string& attr : osm_node_attributes) { 129 | osm_attributes_.push_back(getOSMTagValue(node.tags(), attr.data())); 130 | } 131 | } 132 | 133 | OsmIdType OsmNode::osmNodeId() const { return osm_node_id_; } 134 | const std::string& OsmNode::name() const { return name_; } 135 | double OsmNode::getX() const { return x; } 136 | double OsmNode::getY() const { return y; } 137 | // const std::unique_ptr& OsmNode::geometry() const { return geometry_; } 138 | const std::vector& OsmNode::osmAttributes() const { return osm_attributes_; }; 139 | bool OsmNode::isSignalized() const { return is_signalized_; } 140 | int32_t OsmNode::usageCount() const { return usage_count_; } 141 | bool OsmNode::isTypologyNode() const { return is_typology_node_; } 142 | std::vector OsmNode::incomingWayVector() const { return incoming_way_vector_; } 143 | std::vector OsmNode::outgoingWayVector() const { return outgoing_way_vector_; } 144 | 145 | // void OsmNode::initOsmNode(const geos::geom::GeometryFactory* factory, const geos::geom::Polygon* boundary, 146 | // bool strict_boundary) { 147 | // geometry_ = factory->createPoint(geos::geom::Coordinate(x, y)); 148 | // if (strict_boundary && !boundary->covers(geometry_.get())) { 149 | // in_region_ = false; 150 | // } 151 | // } 152 | void OsmNode::changeUsageCount(int32_t usage_count_changes = 1) { usage_count_ += usage_count_changes; } 153 | void OsmNode::setIsEndingNode(bool is_ending_node) { is_ending_node_ = is_ending_node; } 154 | void OsmNode::setIsTypologyNode() { is_typology_node_ = is_ending_node_ || usage_count_ >= 2 || is_signalized_; } 155 | void OsmNode::addIncomingWay(OsmWay* osm_way) { incoming_way_vector_.push_back(osm_way); } 156 | void OsmNode::addOutgoingWay(OsmWay* osm_way) { outgoing_way_vector_.push_back(osm_way); } 157 | 158 | OsmWay::OsmWay(const osmium::Way& way) 159 | : osm_way_id_(way.id()), 160 | highway_(getOSMTagValue(way.tags(), "highway")), 161 | railway_(getOSMTagValue(way.tags(), "railway")), 162 | aeroway_(getOSMTagValue(way.tags(), "aeroway")), 163 | name_(getOSMTagValue(way.tags(), "name")), 164 | lanes_raw_(getOSMTagValue(way.tags(), "lanes")), 165 | forward_lanes_raw_(getOSMTagValue(way.tags(), "lanes:forward")), 166 | backward_lanes_raw_(getOSMTagValue(way.tags(), "lanes:backward")), 167 | oneway_raw_(getOSMTagValue(way.tags(), "oneway")), 168 | max_speed_raw_(getOSMTagValue(way.tags(), "maxspeed")), 169 | building_(getOSMTagValue(way.tags(), "building")), 170 | amenity_(getOSMTagValue(way.tags(), "amenity")), 171 | leisure_(getOSMTagValue(way.tags(), "leisure")), 172 | junction_(getOSMTagValue(way.tags(), "junction")), 173 | area_(getOSMTagValue(way.tags(), "area")), 174 | motor_vehicle_(getOSMTagValue(way.tags(), "motor_vehicle")), 175 | motorcar_(getOSMTagValue(way.tags(), "motorcar")), 176 | service_(getOSMTagValue(way.tags(), "service")), 177 | access_(getOSMTagValue(way.tags(), "access")), 178 | foot_(getOSMTagValue(way.tags(), "foot")), 179 | bicycle_(getOSMTagValue(way.tags(), "bicycle")) { 180 | for (const auto& way_node : way.nodes()) { 181 | ref_node_id_vector_.push_back(way_node.ref()); 182 | } 183 | } 184 | 185 | OsmIdType OsmWay::osmWayId() const { return osm_way_id_; } 186 | const std::string& OsmWay::railway() const { return railway_; } 187 | const std::string& OsmWay::aeroway() const { return aeroway_; } 188 | const std::string& OsmWay::name() const { return name_; } 189 | std::optional OsmWay::lanes() const { return lanes_; } 190 | std::optional OsmWay::forward_lanes() const { return forward_lanes_; } 191 | std::optional OsmWay::backward_lanes() const { return backward_lanes_; } 192 | const std::vector& OsmWay::refNodeVector() const { return ref_node_vector_; } 193 | OsmNode* OsmWay::fromNode() const { return from_node_; } 194 | OsmNode* OsmWay::toNode() const { return to_node_; } 195 | WayType OsmWay::wayType() const { return way_type_; }; 196 | HighWayLinkType OsmWay::highwayLinkType() const { return highway_link_type_; } 197 | bool OsmWay::isTargetLinkType() const { return is_target_link_type_; } 198 | bool OsmWay::isTargetConnectorLinkType() const { return is_target_connector_link_type_; } 199 | bool OsmWay::isTargetConnector() const { return is_target_connector_; } 200 | std::optional OsmWay::isOneway() const { return is_oneway_; } 201 | bool OsmWay::isReversed() const { return is_reversed_; } 202 | std::optional OsmWay::maxSpeed() const { return max_speed_; } 203 | const std::string& OsmWay::building() const { return building_; } 204 | const std::string& OsmWay::amenity() const { return amenity_; } 205 | const std::string& OsmWay::leisure() const { return leisure_; } 206 | const std::vector& OsmWay::osmLinkAttributes() const { return osm_link_attributes_; } 207 | const std::vector& OsmWay::osmPoiAttributes() const { return osm_poi_attributes_; } 208 | 209 | const std::vector& OsmWay::refNodeIdVector() const { return ref_node_id_vector_; } 210 | const std::vector& OsmWay::allowedModeTypes() const { return allowed_mode_types_; } 211 | bool OsmWay::includeTheWay() const { return include_the_way_; } 212 | const std::vector>& OsmWay::segmentNodesVector() const { return segment_nodes_vector_; } 213 | 214 | void OsmWay::identifyWayType(const absl::flat_hash_set& highway_mode_types, bool include_railway, 215 | bool include_aeroway, const absl::flat_hash_set& link_types, 216 | const absl::flat_hash_set& connector_link_types, bool POI, 217 | const absl::flat_hash_set& ways_used_in_relations) { 218 | if ((!(building_.empty() && amenity_.empty() && leisure_.empty())) || 219 | (!highway_.empty() && isHighwayPoiType(highway_)) || (!railway_.empty() && isRailwayPoiType(railway_)) || 220 | (!aeroway_.empty() && isAerowayPoiType(aeroway_))) { 221 | if (POI) { 222 | way_type_ = WayType::POI; 223 | include_the_way_ = true; 224 | } 225 | return; 226 | } 227 | 228 | if (area_.empty() || area_ == "no") { 229 | if (!highway_.empty()) { 230 | identifyHighwayType(highway_mode_types, link_types, connector_link_types); 231 | } else if (include_railway && !railway_.empty()) { 232 | identifyRailwayType(); 233 | } else if (include_aeroway && !aeroway_.empty()) { 234 | identifyAerowayType(); 235 | } 236 | } 237 | if (include_the_way_) { 238 | return; 239 | } 240 | 241 | if (POI && ways_used_in_relations.find(osm_way_id_) != ways_used_in_relations.end()) { 242 | way_type_ = WayType::POI_COMPONENT; 243 | include_the_way_ = true; 244 | } 245 | } 246 | 247 | void OsmWay::identifyHighwayType(const absl::flat_hash_set& highway_mode_types, 248 | const absl::flat_hash_set& link_types, 249 | const absl::flat_hash_set& connector_link_types) { 250 | highway_link_type_ = highwayStringToHighWayLinkType(highway_); 251 | if (highway_link_type_ == HighWayLinkType::OTHER) { 252 | if (!isNegligibleHighwayType(highway_)) { 253 | LOG(WARNING) << "way " << osm_way_id_ << " has a new highway value " << highway_; 254 | } 255 | return; 256 | } 257 | if (link_types.empty() || link_types.find(highway_link_type_) != link_types.end()) { 258 | is_target_link_type_ = true; 259 | } 260 | if (connector_link_types.find(highway_link_type_) != connector_link_types.end()) { 261 | is_target_connector_link_type_ = true; 262 | } 263 | if (!is_target_link_type_ && !is_target_connector_link_type_) { 264 | return; 265 | } 266 | generateHighwayAllowedModeTypes(highway_mode_types); 267 | if (!allowed_mode_types_.empty()) { 268 | way_type_ = WayType::HIGHWAY; 269 | include_the_way_ = true; 270 | } 271 | } 272 | 273 | void OsmWay::identifyRailwayType() { 274 | if (isNegligibleRailwayType(railway_)) { 275 | return; 276 | } 277 | way_type_ = WayType::RAILWAY; 278 | allowed_mode_types_ = {ModeType::RAILWAY}; 279 | include_the_way_ = true; 280 | } 281 | 282 | void OsmWay::identifyAerowayType() { 283 | if (isNegligibleAerowayType(aeroway_)) { 284 | return; 285 | } 286 | way_type_ = WayType::AEROWAY; 287 | allowed_mode_types_ = {ModeType::AEROWAY}; 288 | include_the_way_ = true; 289 | } 290 | 291 | void OsmWay::updateOsmAttributes(const osmium::Way& way, const std::vector& osm_link_attributes, 292 | const std::vector& osm_poi_attributes) { 293 | if (way_type_ == WayType::HIGHWAY || way_type_ == WayType::RAILWAY || way_type_ == WayType::AEROWAY) { 294 | if (!osm_link_attributes.empty()) { 295 | updateOsmLinkAttributes(way, osm_link_attributes); 296 | } 297 | } else if (way_type_ == WayType::POI) { 298 | if (!osm_poi_attributes.empty()) { 299 | updateOsmPoiAttributes(way, osm_poi_attributes); 300 | } 301 | } 302 | } 303 | 304 | void OsmWay::updateOsmLinkAttributes(const osmium::Way& way, const std::vector& osm_link_attributes) { 305 | osm_link_attributes_.reserve(osm_link_attributes.size()); 306 | for (const std::string& attr : osm_link_attributes) { 307 | osm_link_attributes_.push_back(getOSMTagValue(way.tags(), attr.data())); 308 | } 309 | } 310 | void OsmWay::updateOsmPoiAttributes(const osmium::Way& way, const std::vector& osm_poi_attributes) { 311 | osm_poi_attributes_.reserve(osm_poi_attributes.size()); 312 | for (const std::string& attr : osm_poi_attributes) { 313 | osm_poi_attributes_.push_back(getOSMTagValue(way.tags(), attr.data())); 314 | } 315 | } 316 | 317 | void OsmWay::initOsmWay(const absl::flat_hash_map& osm_node_dict) { 318 | mapRefNodes(osm_node_dict); 319 | if (way_type_ == WayType::HIGHWAY || way_type_ == WayType::RAILWAY || way_type_ == WayType::AEROWAY) { 320 | configAttributes(); 321 | } 322 | } 323 | 324 | bool checkNodeConnecting(const std::vector& connecting_osm_ways) { 325 | return std::any_of(connecting_osm_ways.begin(), connecting_osm_ways.end(), [](const OsmWay* osm_way) { 326 | return osm_way->wayType() == WayType::HIGHWAY && osm_way->isTargetLinkType(); 327 | }); 328 | } 329 | bool checkConnectorNode(OsmNode* osm_node) { 330 | return checkNodeConnecting(osm_node->incomingWayVector()) || checkNodeConnecting(osm_node->outgoingWayVector()); 331 | } 332 | 333 | void OsmWay::identifyTargetConnector() { 334 | if (way_type_ != WayType::HIGHWAY || is_target_link_type_ || !is_target_connector_link_type_ || 335 | ref_node_vector_.empty()) { 336 | is_target_connector_ = false; 337 | return; 338 | } 339 | const bool from_node_connection = checkConnectorNode(from_node_); 340 | const bool to_node_connection = checkConnectorNode(to_node_); 341 | if (!(from_node_connection || to_node_connection) || (from_node_connection && to_node_connection)) { 342 | is_target_connector_ = false; 343 | return; 344 | } 345 | is_target_connector_ = true; 346 | } 347 | 348 | void OsmWay::splitIntoSegments() { 349 | const size_t number_of_ref_nodes = ref_node_vector_.size(); 350 | if (number_of_ref_nodes < 2) { 351 | return; 352 | } 353 | int last_idx = 0; 354 | int idx = 0; 355 | OsmNode* osmnode = nullptr; 356 | 357 | while (true) { 358 | std::vector m_segment_node_vector{ref_node_vector_[last_idx]}; 359 | for (idx = last_idx + 1; idx < number_of_ref_nodes; idx++) { 360 | osmnode = ref_node_vector_[idx]; 361 | m_segment_node_vector.push_back(osmnode); 362 | if (osmnode->isTypologyNode()) { 363 | last_idx = idx; 364 | break; 365 | } 366 | } 367 | 368 | segment_nodes_vector_.push_back(m_segment_node_vector); 369 | number_of_segments_++; 370 | 371 | if (idx == number_of_ref_nodes - 1) { 372 | break; 373 | } 374 | } 375 | } 376 | 377 | // void OsmWay::setUsedByRelation(bool used_by_relation) { used_by_relation_ = used_by_relation; } 378 | 379 | void OsmWay::generateHighwayAllowedModeTypes(const absl::flat_hash_set& highway_mode_types) { 380 | if (highway_mode_types.find(ModeType::AUTO) != highway_mode_types.end()) { 381 | if (checkAllowedUsedAutoInMotor_Vehicle(motor_vehicle_) || checkAllowedUsedAutoInMotorCar(motorcar_) || 382 | (!checkAllowedUsedAutoExHighway(highway_) && !checkAllowedUsedAutoExMotor_Vehicle(motor_vehicle_) && 383 | !checkAllowedUsedAutoExMotorCar(motorcar_) && !checkAllowedUsedAutoExAccess(access_) && 384 | !checkAllowedUsedAutoExService(service_))) { 385 | allowed_mode_types_.push_back(ModeType::AUTO); 386 | } 387 | } 388 | if (highway_mode_types.find(ModeType::BIKE) != highway_mode_types.end()) { 389 | if (checkAllowedUsedBikeInBicycle(bicycle_) || 390 | (!checkAllowedUsedBikeExHighway(highway_) && !checkAllowedUsedBikeExBicycle(bicycle_) && 391 | !checkAllowedUsedBikeExService(service_) && !checkAllowedUsedBikeExAccess(access_))) { 392 | allowed_mode_types_.push_back(ModeType::BIKE); 393 | } 394 | } 395 | if (highway_mode_types.find(ModeType::WALK) != highway_mode_types.end()) { 396 | if (checkAllowedUsedWalkInFoot(foot_) || 397 | (!checkAllowedUsedWalkExHighway(highway_) && !checkAllowedUsedWalkExFoot(foot_) && 398 | !checkAllowedUsedWalkExService(service_) && !checkAllowedUsedWalkExAccess(access_))) { 399 | allowed_mode_types_.push_back(ModeType::WALK); 400 | } 401 | } 402 | } 403 | 404 | void OsmWay::mapRefNodes(const absl::flat_hash_map& osm_node_dict) { 405 | if (ref_node_id_vector_.empty()) { 406 | return; 407 | } 408 | const size_t number_of_ref_nodes = ref_node_id_vector_.size(); 409 | ref_node_vector_.reserve(number_of_ref_nodes); 410 | for (const OsmIdType ref_node_id : ref_node_id_vector_) { 411 | auto iter = osm_node_dict.find(ref_node_id); 412 | if (iter == osm_node_dict.end()) { 413 | LOG(WARNING) << "unkown ref node " << ref_node_id << " in way " << osm_way_id_ 414 | << ", the way will not be imported"; 415 | ref_node_vector_.clear(); 416 | return; 417 | } 418 | ref_node_vector_.push_back(iter->second); 419 | } 420 | from_node_ = ref_node_vector_.at(0); 421 | to_node_ = ref_node_vector_.back(); 422 | } 423 | 424 | const std::regex& getFloatNumMatchingPattern() { 425 | static const std::regex pattern(R"(\d+\.?\d*)"); 426 | return pattern; 427 | } 428 | 429 | void OsmWay::configAttributes() { 430 | // lane info 431 | if (!lanes_raw_.empty()) { 432 | std::smatch match; 433 | if (std::regex_search(lanes_raw_, match, getFloatNumMatchingPattern())) { 434 | lanes_ = static_cast(std::round(std::stof(match.str()))); 435 | } 436 | } 437 | if (!forward_lanes_raw_.empty()) { 438 | std::smatch match; 439 | if (std::regex_search(forward_lanes_raw_, match, getFloatNumMatchingPattern())) { 440 | forward_lanes_ = static_cast(std::round(std::stof(match.str()))); 441 | } 442 | } 443 | if (!backward_lanes_raw_.empty()) { 444 | std::smatch match; 445 | if (std::regex_search(backward_lanes_raw_, match, getFloatNumMatchingPattern())) { 446 | backward_lanes_ = static_cast(std::round(std::stof(match.str()))); 447 | } 448 | } 449 | 450 | // speed info 451 | if (!max_speed_raw_.empty()) { 452 | std::smatch match; 453 | if (std::regex_search(max_speed_raw_, match, getFloatNumMatchingPattern())) { 454 | max_speed_ = absl::StrContains(max_speed_raw_, "mph") ? std::round(std::stof(match.str()) * MPH_TO_KPH) 455 | : std::stof(match.str()); 456 | } 457 | } 458 | 459 | // oneway info 460 | if (way_type_ == WayType::HIGHWAY) { 461 | if (!oneway_raw_.empty()) { 462 | if (oneway_raw_ == "yes" || oneway_raw_ == "1") { 463 | is_oneway_ = true; 464 | } else if (oneway_raw_ == "no" || oneway_raw_ == "0") { 465 | is_oneway_ = false; 466 | } else if (oneway_raw_ == "-1") { 467 | is_oneway_ = true; 468 | is_reversed_ = true; 469 | } else if (oneway_raw_ == "reversible" || oneway_raw_ == "alternating") { 470 | // todo: reversible, alternating: https://wiki.openstreetmap.org/wiki/Tag:oneway%3Dreversible 471 | is_oneway_ = false; 472 | } else { 473 | DLOG(WARNING) << "new oneway type detected at way " << osm_way_id_ << " " << oneway_raw_; 474 | } 475 | } 476 | if (!is_oneway_.has_value()) { 477 | if (junction_ == "circular" || junction_ == "roundabout") { 478 | is_oneway_ = true; 479 | } else { 480 | is_oneway_ = getDefaultOneWayFlag(highway_link_type_); 481 | } 482 | } 483 | } else { 484 | is_oneway_ = true; 485 | } 486 | } 487 | 488 | OsmRelation::OsmRelation(const osmium::Relation& relation, const std::vector& osm_poi_attributes) 489 | : osm_relation_id_(relation.id()), 490 | building_(getOSMTagValue(relation.tags(), "building")), 491 | amenity_(getOSMTagValue(relation.tags(), "amenity")), 492 | leisure_(getOSMTagValue(relation.tags(), "leisure")) { 493 | for (const osmium::RelationMember& member : relation.members()) { 494 | member_id_vector_.push_back(member.ref()); 495 | member_type_vector_.push_back(member.type()); 496 | member_role_vector_.emplace_back(member.role()); 497 | } 498 | osm_attributes_.reserve(osm_poi_attributes.size()); 499 | for (const std::string& attr : osm_poi_attributes) { 500 | osm_attributes_.push_back(getOSMTagValue(relation.tags(), attr.data())); 501 | } 502 | } 503 | 504 | void OsmRelation::initOsmRelation(const absl::flat_hash_map& osm_way_dict) { 505 | if (member_id_vector_.empty()) { 506 | return; 507 | } 508 | const size_t number_of_members = member_id_vector_.size(); 509 | member_way_vector_.reserve(number_of_members); 510 | for (size_t idx = 0; idx < number_of_members; ++idx) { 511 | if (member_type_vector_[idx] != osmium::item_type::way) { 512 | continue; 513 | } 514 | auto iter = osm_way_dict.find(member_id_vector_[idx]); 515 | if (iter == osm_way_dict.end()) { 516 | LOG(WARNING) << "unkown way member " << member_id_vector_[idx] << " in relation " << osm_relation_id_ 517 | << ", the relation will not be imported"; 518 | member_way_vector_.clear(); 519 | member_way_role_vector_.clear(); 520 | return; 521 | } 522 | member_way_vector_.push_back(iter->second); 523 | member_way_role_vector_.push_back(member_role_vector_[idx]); 524 | } 525 | } 526 | 527 | OsmIdType OsmRelation::osmRelationId() const { return osm_relation_id_; } 528 | const std::string& OsmRelation::name() const { return name_; } 529 | const std::vector& OsmRelation::memberIdVector() const { return member_id_vector_; } 530 | const std::vector& OsmRelation::memberTypeVector() const { return member_type_vector_; } 531 | const std::vector& OsmRelation::memberWayVector() const { return member_way_vector_; } 532 | const std::vector& OsmRelation::memberWayRoleVector() const { return member_way_role_vector_; } 533 | const std::string& OsmRelation::building() const { return building_; } 534 | const std::string& OsmRelation::amenity() const { return amenity_; } 535 | const std::string& OsmRelation::leisure() const { return leisure_; } 536 | const std::vector& OsmRelation::osmAttributes() const { return osm_attributes_; } 537 | 538 | OsmNetwork::OsmNetwork(const std::filesystem::path& osm_filepath, absl::flat_hash_set mode_types, 539 | absl::flat_hash_set link_types, 540 | absl::flat_hash_set connector_link_types, bool POI, 541 | const OsmParsingConfig* osm_parsing_config, bool strict_boundary) 542 | : mode_types_(std::move(mode_types)), 543 | link_types_(std::move(link_types)), 544 | connector_link_types_(std::move(connector_link_types)), 545 | POI_(POI), 546 | osm_parsing_config_(osm_parsing_config), 547 | strict_boundary_(strict_boundary) { 548 | if (!std::filesystem::exists(osm_filepath)) { 549 | LOG(FATAL) << "osm file " << osm_filepath << " does not exist"; 550 | return; 551 | } 552 | 553 | factory_ = geos::geom::GeometryFactory::create(); 554 | 555 | const auto time1 = std::chrono::high_resolution_clock::now(); 556 | OsmHandler handler(mode_types_, link_types_, connector_link_types_, POI_, osm_parsing_config_); 557 | osmium::thread::Pool pool{}; 558 | try { 559 | const osmium::io::File input_file{osm_filepath.string()}; 560 | 561 | osmium::io::Reader reader{input_file, pool}; 562 | const osmium::Box& box = reader.header().box(); 563 | if (reader.header().box().valid()) { 564 | boundary_ = factory_->createPolygon({geos::geom::Coordinate(box.bottom_left().lon(), box.bottom_left().lat()), 565 | geos::geom::Coordinate(box.top_right().lon(), box.bottom_left().lat()), 566 | geos::geom::Coordinate(box.top_right().lon(), box.top_right().lat()), 567 | geos::geom::Coordinate(box.bottom_left().lon(), box.top_right().lat()), 568 | geos::geom::Coordinate(box.bottom_left().lon(), box.bottom_left().lat())}); 569 | } else { 570 | LOG(INFO) << "no valid boundary information in the osm file"; 571 | } 572 | reader.close(); 573 | 574 | if (POI_) { 575 | osmium::io::Reader reader_relation{input_file, osmium::osm_entity_bits::relation, pool}; 576 | handler.updateParseTargets(false, false, true); 577 | osmium::apply(reader_relation, handler); 578 | reader_relation.close(); 579 | } 580 | 581 | osmium::io::Reader reader_way{input_file, osmium::osm_entity_bits::way, pool}; 582 | handler.updateParseTargets(false, true, false); 583 | osmium::apply(reader_way, handler); 584 | reader_way.close(); 585 | 586 | osmium::io::Reader reader_node{input_file, osmium::osm_entity_bits::node, pool}; 587 | handler.updateParseTargets(true, false, false); 588 | osmium::apply(reader_node, handler); 589 | reader_node.close(); 590 | } catch (const std::exception& e) { 591 | std::cerr << e.what() << '\n'; 592 | } 593 | const auto time2 = std::chrono::high_resolution_clock::now(); 594 | DLOG(INFO) << "parse osm " 595 | << (std::chrono::duration_cast(time2 - time1) * MICROSECONDS_TO_SECOND).count() 596 | << "seconds"; 597 | 598 | osm_node_vector_ = std::move(handler.osmNodeVector()); 599 | osm_way_vector_ = std::move(handler.osmWayVector()); 600 | osm_relation_vector_ = std::move(handler.osmRelationVector()); 601 | LOG(INFO) << "nodes: " << osm_node_vector_.size() << " ways: " << osm_way_vector_.size() 602 | << " relations: " << osm_relation_vector_.size(); 603 | 604 | const auto time3 = std::chrono::high_resolution_clock::now(); 605 | DLOG(INFO) << "pass osm " 606 | << (std::chrono::duration_cast(time3 - time2) * MICROSECONDS_TO_SECOND).count() 607 | << "seconds"; 608 | 609 | processOsmData(); 610 | 611 | const auto time4 = std::chrono::high_resolution_clock::now(); 612 | DLOG(INFO) << "process osm " 613 | << (std::chrono::duration_cast(time4 - time3) * MICROSECONDS_TO_SECOND).count() 614 | << "seconds"; 615 | } 616 | 617 | OsmNetwork::~OsmNetwork() { 618 | if (!osm_node_vector_.empty()) { 619 | const size_t number_of_osm_nodes = osm_node_vector_.size(); 620 | #pragma omp parallel for schedule(dynamic) default(none) shared(number_of_osm_nodes) 621 | for (int64_t idx = 0; idx < number_of_osm_nodes; ++idx) { 622 | delete osm_node_vector_[idx]; 623 | } 624 | } 625 | if (!osm_way_vector_.empty()) { 626 | const size_t number_of_osm_ways = osm_way_vector_.size(); 627 | #pragma omp parallel for schedule(dynamic) default(none) shared(number_of_osm_ways) 628 | for (int64_t idx = 0; idx < number_of_osm_ways; ++idx) { 629 | delete osm_way_vector_[idx]; 630 | } 631 | } 632 | if (!osm_relation_vector_.empty()) { 633 | const size_t number_of_osm_relations = osm_relation_vector_.size(); 634 | #pragma omp parallel for schedule(dynamic) default(none) shared(number_of_osm_relations) 635 | for (int64_t idx = 0; idx < number_of_osm_relations; ++idx) { 636 | delete osm_relation_vector_[idx]; 637 | } 638 | } 639 | } 640 | 641 | const std::optional>& OsmNetwork::boundary() const { return boundary_; } 642 | const std::vector& OsmNetwork::osmWayVector() const { return osm_way_vector_; } 643 | const std::vector& OsmNetwork::osmRelationVector() const { return osm_relation_vector_; } 644 | 645 | void OsmNetwork::processOsmData() { 646 | initializeElements(); 647 | createWaySegments(); 648 | } 649 | 650 | void OsmNetwork::initializeElements() { 651 | absl::flat_hash_map osm_node_dict; 652 | osm_node_dict.reserve(osm_node_vector_.size()); 653 | for (OsmNode* osm_node : osm_node_vector_) { 654 | osm_node_dict.emplace(osm_node->osmNodeId(), osm_node); 655 | } 656 | 657 | absl::flat_hash_map osm_way_dict; 658 | osm_way_dict.reserve(osm_way_vector_.size()); 659 | for (OsmWay* osm_way : osm_way_vector_) { 660 | osm_way_dict.emplace(osm_way->osmWayId(), osm_way); 661 | } 662 | 663 | /*================= OsmNode =================*/ 664 | // const size_t number_of_osm_nodes = osm_node_vector_.size(); 665 | // #pragma omp parallel for schedule(dynamic) default(none) shared(number_of_osm_nodes) 666 | // for (int64_t idx = 0; idx < number_of_osm_nodes; ++idx) { 667 | // osm_node_vector_[idx]->initOsmNode(factory_.get(), boundary_.get(), strict_boundary_); 668 | // } 669 | 670 | /*================= OsmWay =================*/ 671 | const size_t number_of_osm_ways = osm_way_vector_.size(); 672 | #pragma omp parallel for schedule(dynamic) default(none) shared(number_of_osm_ways, osm_node_dict) 673 | for (int64_t idx = 0; idx < number_of_osm_ways; ++idx) { 674 | osm_way_vector_[idx]->initOsmWay(osm_node_dict); 675 | } 676 | 677 | for (OsmWay* osm_way : osm_way_vector_) { 678 | if (osm_way->fromNode() != nullptr) { 679 | osm_way->fromNode()->addOutgoingWay(osm_way); 680 | } 681 | if (osm_way->toNode() != nullptr) { 682 | osm_way->toNode()->addIncomingWay(osm_way); 683 | } 684 | } 685 | 686 | #pragma omp parallel for schedule(dynamic) default(none) shared(number_of_osm_ways) 687 | for (int64_t idx = 0; idx < number_of_osm_ways; ++idx) { 688 | osm_way_vector_[idx]->identifyTargetConnector(); 689 | } 690 | 691 | /*================= OsmRelation =================*/ 692 | const size_t number_of_osm_relations = osm_relation_vector_.size(); 693 | #pragma omp parallel for schedule(dynamic) default(none) shared(number_of_osm_relations, osm_way_dict) 694 | for (int64_t idx = 0; idx < number_of_osm_relations; ++idx) { 695 | osm_relation_vector_[idx]->initOsmRelation(osm_way_dict); 696 | } 697 | } 698 | 699 | void OsmNetwork::createWaySegments() { 700 | // ToDo: run in parallel 701 | for (const OsmWay* osm_way : osm_way_vector_) { 702 | const std::vector& ref_node_vector = osm_way->refNodeVector(); 703 | if (ref_node_vector.empty()) { 704 | continue; 705 | } 706 | for (OsmNode* ref_node : ref_node_vector) { 707 | ref_node->changeUsageCount(); 708 | } 709 | ref_node_vector.at(0)->setIsEndingNode(true); 710 | ref_node_vector.back()->setIsEndingNode(true); 711 | } 712 | const size_t number_of_osm_nodes = osm_node_vector_.size(); 713 | #pragma omp parallel for schedule(dynamic) default(none) shared(number_of_osm_nodes) 714 | for (int64_t idx = 0; idx < number_of_osm_nodes; ++idx) { 715 | osm_node_vector_[idx]->setIsTypologyNode(); 716 | } 717 | 718 | const size_t number_of_osm_ways = osm_way_vector_.size(); 719 | #pragma omp parallel for schedule(dynamic) default(none) shared(number_of_osm_ways) 720 | for (int64_t idx = 0; idx < number_of_osm_ways; ++idx) { 721 | osm_way_vector_[idx]->splitIntoSegments(); 722 | } 723 | } -------------------------------------------------------------------------------- /src/osmnetwork.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Lu, Jiawei on 5/23/24. 3 | // 4 | 5 | #ifndef OSM2GMNS_OSMNETWORK_H 6 | #define OSM2GMNS_OSMNETWORK_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "config.h" 26 | #include "osmconfig.h" 27 | 28 | class OsmNode; 29 | class OsmWay; 30 | class OsmRelation; 31 | 32 | class OsmHandler : public osmium::handler::Handler { 33 | public: 34 | explicit OsmHandler(const absl::flat_hash_set& mode_types, absl::flat_hash_set link_types, 35 | absl::flat_hash_set connector_link_types, bool POI, 36 | const OsmParsingConfig* osm_parsing_config); 37 | 38 | void node(const osmium::Node& node); 39 | void way(const osmium::Way& way); 40 | void relation(const osmium::Relation& relation); 41 | 42 | void updateParseTargets(bool parse_node, bool parse_way, bool parse_relation); 43 | 44 | std::vector& osmNodeVector(); 45 | std::vector& osmWayVector(); 46 | std::vector& osmRelationVector(); 47 | 48 | private: 49 | bool parse_node_{false}; 50 | bool parse_way_{false}; 51 | bool parse_relation_{false}; 52 | absl::flat_hash_set nodes_used_in_ways_; 53 | absl::flat_hash_set ways_used_in_relations_; 54 | 55 | absl::flat_hash_set highway_mode_types_; 56 | bool include_railway_{false}; 57 | bool include_aeroway_{false}; 58 | absl::flat_hash_set link_types_; 59 | absl::flat_hash_set connector_link_types_; 60 | bool POI_{false}; 61 | const OsmParsingConfig* osm_parsing_config_; 62 | 63 | std::vector osm_node_vector_; 64 | std::vector osm_way_vector_; 65 | std::vector osm_relation_vector_; 66 | }; 67 | 68 | class OsmNode { 69 | public: 70 | explicit OsmNode(const osmium::Node& node, const std::vector& osm_node_attributes); 71 | 72 | [[nodiscard]] OsmIdType osmNodeId() const; 73 | [[nodiscard]] const std::string& name() const; 74 | [[nodiscard]] double getX() const; 75 | [[nodiscard]] double getY() const; 76 | // [[nodiscard]] const std::unique_ptr& geometry() const; 77 | [[nodiscard]] const std::vector& osmAttributes() const; 78 | [[nodiscard]] bool isSignalized() const; 79 | [[nodiscard]] int32_t usageCount() const; 80 | [[nodiscard]] bool isTypologyNode() const; 81 | [[nodiscard]] std::vector incomingWayVector() const; 82 | [[nodiscard]] std::vector outgoingWayVector() const; 83 | 84 | // void initOsmNode(const geos::geom::GeometryFactory* factory, const geos::geom::Polygon* boundary, 85 | // bool strict_boundary); 86 | void changeUsageCount(int32_t usage_count_changes); 87 | void setIsEndingNode(bool is_ending_node); 88 | void setIsTypologyNode(); 89 | void addIncomingWay(OsmWay* osm_way); 90 | void addOutgoingWay(OsmWay* osm_way); 91 | 92 | private: 93 | OsmIdType osm_node_id_; 94 | std::string name_; 95 | // std::unique_ptr geometry_; 96 | double x, y; 97 | std::string highway_; 98 | std::vector osm_attributes_; 99 | 100 | bool is_signalized_{false}; 101 | bool in_region_{true}; 102 | 103 | int32_t usage_count_{0}; 104 | bool is_ending_node_{false}; 105 | bool is_typology_node_{false}; 106 | std::vector incoming_way_vector_; 107 | std::vector outgoing_way_vector_; 108 | 109 | std::string notes_; 110 | }; 111 | 112 | class OsmWay { 113 | public: 114 | explicit OsmWay(const osmium::Way& way); 115 | 116 | [[nodiscard]] OsmIdType osmWayId() const; 117 | [[nodiscard]] const std::string& railway() const; 118 | [[nodiscard]] const std::string& aeroway() const; 119 | [[nodiscard]] const std::string& name() const; 120 | [[nodiscard]] std::optional lanes() const; 121 | [[nodiscard]] std::optional forward_lanes() const; 122 | [[nodiscard]] std::optional backward_lanes() const; 123 | [[nodiscard]] const std::vector& refNodeVector() const; 124 | [[nodiscard]] OsmNode* fromNode() const; 125 | [[nodiscard]] OsmNode* toNode() const; 126 | [[nodiscard]] WayType wayType() const; 127 | [[nodiscard]] HighWayLinkType highwayLinkType() const; 128 | [[nodiscard]] bool isTargetLinkType() const; 129 | [[nodiscard]] bool isTargetConnectorLinkType() const; 130 | [[nodiscard]] bool isTargetConnector() const; 131 | [[nodiscard]] std::optional isOneway() const; 132 | [[nodiscard]] bool isReversed() const; 133 | [[nodiscard]] std::optional maxSpeed() const; 134 | [[nodiscard]] const std::string& building() const; 135 | [[nodiscard]] const std::string& amenity() const; 136 | [[nodiscard]] const std::string& leisure() const; 137 | [[nodiscard]] const std::vector& osmLinkAttributes() const; 138 | [[nodiscard]] const std::vector& osmPoiAttributes() const; 139 | 140 | [[nodiscard]] const std::vector& refNodeIdVector() const; 141 | [[nodiscard]] const std::vector& allowedModeTypes() const; 142 | [[nodiscard]] bool includeTheWay() const; 143 | [[nodiscard]] const std::vector>& segmentNodesVector() const; 144 | 145 | void identifyWayType(const absl::flat_hash_set& highway_mode_types, bool include_railway, 146 | bool include_aeroway, const absl::flat_hash_set& link_types, 147 | const absl::flat_hash_set& connector_link_types, bool POI, 148 | const absl::flat_hash_set& ways_used_in_relations); 149 | void updateOsmAttributes(const osmium::Way& way, const std::vector& osm_link_attributes, 150 | const std::vector& osm_poi_attributes); 151 | void initOsmWay(const absl::flat_hash_map& osm_node_dict); 152 | void identifyTargetConnector(); 153 | void splitIntoSegments(); 154 | 155 | // void setUsedByRelation(bool used_by_relation); 156 | 157 | private: 158 | void identifyHighwayType(const absl::flat_hash_set& highway_mode_types, 159 | const absl::flat_hash_set& link_types, 160 | const absl::flat_hash_set& connector_link_types); 161 | void identifyRailwayType(); 162 | void identifyAerowayType(); 163 | void generateHighwayAllowedModeTypes(const absl::flat_hash_set& highway_mode_types); 164 | void updateOsmLinkAttributes(const osmium::Way& way, const std::vector& osm_link_attributes); 165 | void updateOsmPoiAttributes(const osmium::Way& way, const std::vector& osm_poi_attributes); 166 | void mapRefNodes(const absl::flat_hash_map& osm_node_dict); 167 | void configAttributes(); 168 | 169 | OsmIdType osm_way_id_; 170 | std::string highway_; 171 | std::string railway_; 172 | std::string aeroway_; 173 | std::string name_; 174 | std::string lanes_raw_; 175 | std::string forward_lanes_raw_; 176 | std::string backward_lanes_raw_; 177 | std::string oneway_raw_; 178 | std::string max_speed_raw_; 179 | std::optional lanes_; 180 | std::optional forward_lanes_; 181 | std::optional backward_lanes_; 182 | std::optional is_oneway_; 183 | bool is_reversed_{false}; // ToDo: use when generating segments 184 | std::optional max_speed_; 185 | 186 | std::string building_; 187 | std::string amenity_; 188 | std::string leisure_; 189 | std::string junction_; 190 | std::string area_; 191 | std::string motor_vehicle_; 192 | std::string motorcar_; 193 | std::string service_; 194 | std::string access_; 195 | std::string foot_; 196 | std::string bicycle_; 197 | 198 | std::vector osm_link_attributes_; 199 | std::vector osm_poi_attributes_; 200 | 201 | std::vector ref_node_id_vector_; 202 | std::vector ref_node_vector_; 203 | OsmNode* from_node_{nullptr}; 204 | OsmNode* to_node_{nullptr}; 205 | bool contains_unknown_ref_nodes_{false}; 206 | 207 | WayType way_type_{WayType::OTHER}; 208 | std::vector allowed_mode_types_; 209 | HighWayLinkType highway_link_type_{HighWayLinkType::OTHER}; 210 | bool is_target_link_type_{false}; 211 | bool is_target_connector_link_type_{false}; 212 | bool is_target_connector_{false}; 213 | bool include_the_way_{false}; 214 | // bool used_by_relation_{false}; 215 | 216 | int number_of_segments_{0}; 217 | std::vector> segment_nodes_vector_; 218 | }; 219 | 220 | class OsmRelation { 221 | public: 222 | explicit OsmRelation(const osmium::Relation& relation, const std::vector& osm_poi_attributes); 223 | 224 | void initOsmRelation(const absl::flat_hash_map& osm_way_dict); 225 | 226 | [[nodiscard]] OsmIdType osmRelationId() const; 227 | [[nodiscard]] const std::string& name() const; 228 | [[nodiscard]] const std::vector& memberIdVector() const; 229 | [[nodiscard]] const std::vector& memberTypeVector() const; 230 | [[nodiscard]] const std::vector& memberWayVector() const; 231 | [[nodiscard]] const std::vector& memberWayRoleVector() const; 232 | [[nodiscard]] const std::string& building() const; 233 | [[nodiscard]] const std::string& amenity() const; 234 | [[nodiscard]] const std::string& leisure() const; 235 | [[nodiscard]] const std::vector& osmAttributes() const; 236 | 237 | private: 238 | OsmIdType osm_relation_id_; 239 | std::string name_; 240 | std::vector member_id_vector_; 241 | std::vector member_type_vector_; 242 | std::vector member_role_vector_; 243 | std::vector member_way_vector_; 244 | std::vector member_way_role_vector_; 245 | std::string building_; 246 | std::string amenity_; 247 | std::string leisure_; 248 | 249 | std::vector osm_attributes_; 250 | }; 251 | 252 | class OsmNetwork { 253 | public: 254 | explicit OsmNetwork(const std::filesystem::path& osm_filepath, absl::flat_hash_set mode_types, 255 | absl::flat_hash_set link_types, 256 | absl::flat_hash_set connector_link_types, bool POI, 257 | const OsmParsingConfig* osm_parsing_config, bool strict_boundary); 258 | ~OsmNetwork(); 259 | OsmNetwork(const OsmNetwork&) = delete; 260 | OsmNetwork& operator=(const OsmNetwork&) = delete; 261 | OsmNetwork(OsmNetwork&&) = delete; 262 | OsmNetwork& operator=(OsmNetwork&&) = delete; 263 | 264 | [[nodiscard]] const std::optional>& boundary() const; 265 | [[nodiscard]] const std::vector& osmWayVector() const; 266 | [[nodiscard]] const std::vector& osmRelationVector() const; 267 | 268 | private: 269 | void processOsmData(); 270 | void initializeElements(); 271 | void createWaySegments(); 272 | 273 | absl::flat_hash_set mode_types_; 274 | absl::flat_hash_set link_types_; 275 | absl::flat_hash_set connector_link_types_; 276 | bool POI_; 277 | const OsmParsingConfig* osm_parsing_config_; 278 | bool strict_boundary_; 279 | 280 | geos::geom::GeometryFactory::Ptr factory_; 281 | std::optional> boundary_; 282 | 283 | std::vector osm_node_vector_; 284 | std::vector osm_way_vector_; 285 | std::vector osm_relation_vector_; 286 | 287 | // std::vector link_way_vector{}; 288 | 289 | // geos::geom::Geometry* bounds{}; 290 | }; 291 | 292 | #endif // OSM2GMNS_OSMNETWORK_H 293 | -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jiawei Lu on 2/16/23. 3 | // 4 | 5 | #include "utils.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | constexpr double EARTH_RADIUS = 6371000.0; 25 | 26 | // void initializeAbslLogging() { 27 | // absl::InitializeLog(); 28 | // absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo); 29 | // }; 30 | 31 | // VerboseLevel verboseLevel(bool update, VerboseLevel new_level) { 32 | // static VerboseLevel verbose_level = VerboseLevel::Information; 33 | // if (update) { 34 | // verbose_level = new_level; 35 | // } 36 | // return verbose_level; 37 | // } 38 | 39 | double toRadians(double degrees) { 40 | return degrees * 3.14159265 / 180.0; // NOLINT 41 | } 42 | 43 | double haversineDistance(double lon1, double lat1, double lon2, double lat2) { 44 | const double dLat = toRadians(lat2 - lat1); 45 | const double dLon = toRadians(lon2 - lon1); 46 | const double para_a = std::sin(dLat / 2) * std::sin(dLat / 2) + 47 | std::cos(toRadians(lat1)) * std::cos(toRadians(lat2)) * std::sin(dLon / 2) * std::sin(dLon / 2); 48 | const double para_c = 2 * std::atan2(std::sqrt(para_a), std::sqrt(1 - para_a)); 49 | return EARTH_RADIUS * para_c; 50 | } 51 | 52 | double calculateLineStringLength(const geos::geom::LineString* lineString) { 53 | double totalLength = 0.0; 54 | // const geos::geom::CoordinateSequence* coords = lineString->getCoordinateSequence(); 55 | const std::unique_ptr coords = lineString->getCoordinates(); 56 | for (std::size_t i = 1; i < coords->size(); ++i) { 57 | const geos::geom::Coordinate& point_1 = coords->getAt(i - 1); 58 | const geos::geom::Coordinate& point_2 = coords->getAt(i); 59 | totalLength += haversineDistance(point_1.x, point_1.y, point_2.x, point_2.y); 60 | } 61 | return totalLength; 62 | } 63 | 64 | double calculateDistanceBetweenTwoPoints(const geos::geom::Point* point1, const geos::geom::Point* point2) { 65 | const GeographicLib::Geodesic& geod = GeographicLib::Geodesic::WGS84(); 66 | double distance = 0.0; 67 | geod.Inverse(point1->getY(), point1->getX(), point2->getY(), point2->getX(), distance); 68 | return distance; 69 | } 70 | 71 | std::unique_ptr projectPointToUTM(const geos::geom::Point* point, 72 | const geos::geom::GeometryFactory* factory) { 73 | double utm_x = 0.0; // Easting (x) in meters 74 | double utm_y = 0.0; // Northing (y) in meters 75 | int zone = 0; // UTM zone 76 | bool northp = true; // Whether coordinate is in northern hemisphere 77 | GeographicLib::UTMUPS::Forward(point->getY(), point->getX(), // Input latitude, longitude 78 | zone, northp, // Output zone and hemisphere 79 | utm_x, utm_y); // Output easting, northing 80 | return factory->createPoint(geos::geom::Coordinate(utm_x, utm_y)); 81 | } 82 | 83 | std::unique_ptr projectLineStringToUTM(const geos::geom::LineString* line, 84 | const geos::geom::GeometryFactory* factory) { 85 | double utm_x = 0.0; // Easting (x) in meters 86 | double utm_y = 0.0; // Northing (y) in meters 87 | int zone = 0; // UTM zone 88 | bool northp = true; // Whether coordinate is in northern hemisphere 89 | geos::geom::CoordinateSequence coord_seq; 90 | for (size_t pnt_idx = 0; pnt_idx < line->getNumPoints(); ++pnt_idx) { 91 | const geos::geom::Coordinate& coord = line->getCoordinateN(pnt_idx); 92 | GeographicLib::UTMUPS::Forward(coord.y, coord.x, // Input latitude, longitude 93 | zone, northp, // Output zone and hemisphere 94 | utm_x, utm_y); // Output easting, northing 95 | coord_seq.add(utm_x, utm_y); 96 | } 97 | return factory->createLineString(coord_seq); 98 | } 99 | 100 | std::unique_ptr projectPolygonToUTM(const geos::geom::Polygon* poly, 101 | const geos::geom::GeometryFactory* factory) { 102 | double utm_x = 0.0; // Easting (x) in meters 103 | double utm_y = 0.0; // Northing (y) in meters 104 | int zone = 0; // UTM zone 105 | bool northp = true; // Whether coordinate is in northern hemisphere 106 | geos::geom::CoordinateSequence coord_seq; 107 | const geos::geom::LinearRing* ring = poly->getExteriorRing(); 108 | for (size_t pnt_idx = 0; pnt_idx < ring->getNumPoints(); ++pnt_idx) { 109 | const geos::geom::Coordinate& coord = ring->getCoordinateN(pnt_idx); 110 | GeographicLib::UTMUPS::Forward(coord.y, coord.x, // Input latitude, longitude 111 | zone, northp, // Output zone and hemisphere 112 | utm_x, utm_y); // Output easting, northing 113 | coord_seq.add(utm_x, utm_y); 114 | } 115 | return factory->createPolygon(std::move(coord_seq)); 116 | } 117 | 118 | std::unique_ptr projectGeometryToUTM(const geos::geom::Geometry* geometry, 119 | const geos::geom::GeometryFactory* factory) { 120 | const geos::geom::GeometryTypeId geometry_type_id = geometry->getGeometryTypeId(); 121 | if (geometry_type_id == geos::geom::GEOS_POINT) { 122 | return projectPointToUTM(dynamic_cast(geometry), factory); 123 | } 124 | if (geometry_type_id == geos::geom::GEOS_LINESTRING) { 125 | return projectLineStringToUTM(dynamic_cast(geometry), factory); 126 | } 127 | if (geometry_type_id == geos::geom::GEOS_POLYGON) { 128 | return projectPolygonToUTM(dynamic_cast(geometry), factory); 129 | } 130 | 131 | // if (geometry->isCollection()) 132 | if (geometry_type_id == geos::geom::GEOS_MULTIPOLYGON) { 133 | const size_t number_of_geometries = geometry->getNumGeometries(); 134 | std::vector> geo_vector; 135 | geo_vector.reserve(number_of_geometries); 136 | for (size_t geo_idx = 0; geo_idx < number_of_geometries; ++geo_idx) { 137 | geo_vector.push_back( 138 | projectPolygonToUTM(dynamic_cast(geometry->getGeometryN(geo_idx)), factory)); 139 | } 140 | return factory->createMultiPolygon(std::move(geo_vector)); 141 | } 142 | return nullptr; 143 | } 144 | 145 | // const GeographicLib::Geodesic& geod = GeographicLib::Geodesic::WGS84(); 146 | // // Distance from JFK to LHR 147 | // double 148 | // lat1 = 40.6, lon1 = -73.8, // JFK Airport 149 | // lat2 = 51.6, lon2 = -0.5; // LHR Airport 150 | // double s12; 151 | // geod.Inverse(lat1, lon1, lat2, lon2, s12); 152 | // std::cout << s12 / 1000 << " km\n"; 153 | // std::cout << std::endl; -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jiawei Lu on 2/16/23. 3 | // 4 | 5 | #ifndef OSM2GMNS_UTILS_H 6 | #define OSM2GMNS_UTILS_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | constexpr double MIN_LAT = -90.0; 16 | constexpr double MAX_LAT = 90.0; 17 | constexpr double MIN_LON = -180.0; 18 | constexpr double MAX_LON = 180.0; 19 | 20 | // enum class VerboseLevel : uint8_t { OFF, Information, Trace }; 21 | 22 | // void initializeAbslLogging(); 23 | 24 | // [[maybe_unused]] VerboseLevel verboseLevel(bool update = false, VerboseLevel new_level = VerboseLevel::Information); 25 | 26 | double calculateLineStringLength(const geos::geom::LineString* lineString); 27 | // std::unique_ptr getPolygonFromOsmNodes(const std::vector& osm_nodes, 28 | // const geos::geom::GeometryFactory* factory); 29 | 30 | double calculateDistanceBetweenTwoPoints(const geos::geom::Point* point1, const geos::geom::Point* point2); 31 | 32 | std::unique_ptr projectGeometryToUTM(const geos::geom::Geometry* geometry, 33 | const geos::geom::GeometryFactory* factory); 34 | #endif // OSM2GMNS_UTILS_H 35 | --------------------------------------------------------------------------------