pyroutingkit 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. pyroutingkit-0.1.0/.github/workflows/publish.yml +71 -0
  2. pyroutingkit-0.1.0/.gitignore +8 -0
  3. pyroutingkit-0.1.0/.gitmodules +3 -0
  4. pyroutingkit-0.1.0/CMakeLists.txt +40 -0
  5. pyroutingkit-0.1.0/PKG-INFO +103 -0
  6. pyroutingkit-0.1.0/README.md +87 -0
  7. pyroutingkit-0.1.0/pyproject.toml +36 -0
  8. pyroutingkit-0.1.0/src/pyroutingkit/__init__.py +17 -0
  9. pyroutingkit-0.1.0/src/routingkit_module.cpp +263 -0
  10. pyroutingkit-0.1.0/tests/test_cch.py +85 -0
  11. pyroutingkit-0.1.0/uv.lock +312 -0
  12. pyroutingkit-0.1.0/vendor/RoutingKit/.gitignore +3 -0
  13. pyroutingkit-0.1.0/vendor/RoutingKit/.travis.yml +98 -0
  14. pyroutingkit-0.1.0/vendor/RoutingKit/LICENSE +22 -0
  15. pyroutingkit-0.1.0/vendor/RoutingKit/Makefile +486 -0
  16. pyroutingkit-0.1.0/vendor/RoutingKit/README.md +99 -0
  17. pyroutingkit-0.1.0/vendor/RoutingKit/doc/ContractionHierarchy.md +439 -0
  18. pyroutingkit-0.1.0/vendor/RoutingKit/doc/CoordinatesToNodeID.md +36 -0
  19. pyroutingkit-0.1.0/vendor/RoutingKit/doc/CustomizableContractionHierarchy.md +139 -0
  20. pyroutingkit-0.1.0/vendor/RoutingKit/doc/OpenStreetMap.md +371 -0
  21. pyroutingkit-0.1.0/vendor/RoutingKit/doc/Setup.md +102 -0
  22. pyroutingkit-0.1.0/vendor/RoutingKit/doc/SupportFunctions.md +274 -0
  23. pyroutingkit-0.1.0/vendor/RoutingKit/generate_make_file +316 -0
  24. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/all.h +32 -0
  25. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/bit_vector.h +154 -0
  26. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/constants.h +14 -0
  27. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/contraction_hierarchy.h +759 -0
  28. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/customizable_contraction_hierarchy.h +180 -0
  29. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/dijkstra.h +176 -0
  30. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/filter.h +56 -0
  31. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/geo_dist.h +128 -0
  32. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/geo_position_to_node.h +43 -0
  33. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/google_polyline.h +76 -0
  34. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/graph_util.h +68 -0
  35. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/id_mapper.h +65 -0
  36. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/id_queue.h +181 -0
  37. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/id_set_queue.h +234 -0
  38. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/inverse_vector.h +58 -0
  39. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/min_max.h +70 -0
  40. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/nested_dissection.h +122 -0
  41. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/osm_decoder.h +50 -0
  42. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/osm_graph_builder.h +122 -0
  43. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/osm_profile.h +29 -0
  44. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/osm_simple.h +94 -0
  45. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/permutation.h +165 -0
  46. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/sort.h +380 -0
  47. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/strongly_connected_component.h +25 -0
  48. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/tag_map.h +127 -0
  49. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/timer.h +10 -0
  50. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/timestamp_flag.h +42 -0
  51. pyroutingkit-0.1.0/vendor/RoutingKit/include/routingkit/vector_io.h +177 -0
  52. pyroutingkit-0.1.0/vendor/RoutingKit/src/bit_select.cpp +101 -0
  53. pyroutingkit-0.1.0/vendor/RoutingKit/src/bit_select.h +17 -0
  54. pyroutingkit-0.1.0/vendor/RoutingKit/src/bit_vector.cpp +529 -0
  55. pyroutingkit-0.1.0/vendor/RoutingKit/src/buffered_asynchronous_reader.cpp +186 -0
  56. pyroutingkit-0.1.0/vendor/RoutingKit/src/buffered_asynchronous_reader.h +35 -0
  57. pyroutingkit-0.1.0/vendor/RoutingKit/src/compare_vector.cpp +137 -0
  58. pyroutingkit-0.1.0/vendor/RoutingKit/src/compute_contraction_hierarchy.cpp +71 -0
  59. pyroutingkit-0.1.0/vendor/RoutingKit/src/compute_geographic_distance_weights.cpp +74 -0
  60. pyroutingkit-0.1.0/vendor/RoutingKit/src/compute_nested_dissection_order.cpp +61 -0
  61. pyroutingkit-0.1.0/vendor/RoutingKit/src/contraction_hierarchy.cpp +2217 -0
  62. pyroutingkit-0.1.0/vendor/RoutingKit/src/convert_road_dimacs_coordinates.cpp +105 -0
  63. pyroutingkit-0.1.0/vendor/RoutingKit/src/convert_road_dimacs_graph.cpp +114 -0
  64. pyroutingkit-0.1.0/vendor/RoutingKit/src/customizable_contraction_hierarchy.cpp +1920 -0
  65. pyroutingkit-0.1.0/vendor/RoutingKit/src/decode_vector.cpp +87 -0
  66. pyroutingkit-0.1.0/vendor/RoutingKit/src/emulate_gcc_builtin.h +62 -0
  67. pyroutingkit-0.1.0/vendor/RoutingKit/src/encode_vector.cpp +115 -0
  68. pyroutingkit-0.1.0/vendor/RoutingKit/src/examine_ch.cpp +46 -0
  69. pyroutingkit-0.1.0/vendor/RoutingKit/src/expect.cpp +5 -0
  70. pyroutingkit-0.1.0/vendor/RoutingKit/src/expect.h +38 -0
  71. pyroutingkit-0.1.0/vendor/RoutingKit/src/export_road_dimacs_graph.cpp +77 -0
  72. pyroutingkit-0.1.0/vendor/RoutingKit/src/file_data_source.cpp +188 -0
  73. pyroutingkit-0.1.0/vendor/RoutingKit/src/file_data_source.h +53 -0
  74. pyroutingkit-0.1.0/vendor/RoutingKit/src/generate_constant_vector.cpp +43 -0
  75. pyroutingkit-0.1.0/vendor/RoutingKit/src/generate_dijkstra_rank_test_queries.cpp +110 -0
  76. pyroutingkit-0.1.0/vendor/RoutingKit/src/generate_random_node_list.cpp +54 -0
  77. pyroutingkit-0.1.0/vendor/RoutingKit/src/generate_random_source_times.cpp +54 -0
  78. pyroutingkit-0.1.0/vendor/RoutingKit/src/generate_test_queries.cpp +56 -0
  79. pyroutingkit-0.1.0/vendor/RoutingKit/src/geo_position_to_node.cpp +232 -0
  80. pyroutingkit-0.1.0/vendor/RoutingKit/src/google_polyline.cpp +88 -0
  81. pyroutingkit-0.1.0/vendor/RoutingKit/src/graph_to_dot.cpp +59 -0
  82. pyroutingkit-0.1.0/vendor/RoutingKit/src/graph_to_svg.cpp +85 -0
  83. pyroutingkit-0.1.0/vendor/RoutingKit/src/graph_util.cpp +167 -0
  84. pyroutingkit-0.1.0/vendor/RoutingKit/src/id_mapper.cpp +129 -0
  85. pyroutingkit-0.1.0/vendor/RoutingKit/src/nested_dissection.cpp +883 -0
  86. pyroutingkit-0.1.0/vendor/RoutingKit/src/osm_decoder.cpp +765 -0
  87. pyroutingkit-0.1.0/vendor/RoutingKit/src/osm_extract.cpp +144 -0
  88. pyroutingkit-0.1.0/vendor/RoutingKit/src/osm_graph_builder.cpp +834 -0
  89. pyroutingkit-0.1.0/vendor/RoutingKit/src/osm_profile.cpp +803 -0
  90. pyroutingkit-0.1.0/vendor/RoutingKit/src/osm_simple.cpp +155 -0
  91. pyroutingkit-0.1.0/vendor/RoutingKit/src/osmpbfformat.proto +78 -0
  92. pyroutingkit-0.1.0/vendor/RoutingKit/src/protobuf.cpp +37 -0
  93. pyroutingkit-0.1.0/vendor/RoutingKit/src/protobuf.h +54 -0
  94. pyroutingkit-0.1.0/vendor/RoutingKit/src/randomly_permute_nodes.cpp +98 -0
  95. pyroutingkit-0.1.0/vendor/RoutingKit/src/run_contraction_hierarchy_query.cpp +78 -0
  96. pyroutingkit-0.1.0/vendor/RoutingKit/src/run_dijkstra.cpp +117 -0
  97. pyroutingkit-0.1.0/vendor/RoutingKit/src/show_path.cpp +76 -0
  98. pyroutingkit-0.1.0/vendor/RoutingKit/src/strongly_connected_component.cpp +99 -0
  99. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_basic_features.cpp +783 -0
  100. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_bit_vector.cpp +484 -0
  101. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_buffered_asynchronous_reader.cpp +116 -0
  102. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_contraction_hierarchy_extra_weight.cpp +862 -0
  103. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_contraction_hierarchy_path_query.cpp +165 -0
  104. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_contraction_hierarchy_pinned_query.cpp +169 -0
  105. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_customizable_contraction_hierarchy.cpp +60 -0
  106. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_customizable_contraction_hierarchy_customization.cpp +148 -0
  107. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_customizable_contraction_hierarchy_path_query.cpp +171 -0
  108. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_customizable_contraction_hierarchy_perfect_customization.cpp +172 -0
  109. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_customizable_contraction_hierarchy_pinned_query.cpp +204 -0
  110. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_customizable_contraction_hierarchy_reset.cpp +316 -0
  111. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_dijkstra.cpp +170 -0
  112. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_geo_dist.cpp +138 -0
  113. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_google_polyline.cpp +100 -0
  114. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_id_mapper.cpp +335 -0
  115. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_id_set_queue.cpp +63 -0
  116. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_inverse_vector.cpp +42 -0
  117. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_nearest_neighbor.cpp +167 -0
  118. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_nested_dissection.cpp +421 -0
  119. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_osm_simple.cpp +74 -0
  120. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_permutation.cpp +73 -0
  121. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_protobuf.cpp +117 -0
  122. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_sort.cpp +453 -0
  123. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_strongly_connected_component.cpp +73 -0
  124. pyroutingkit-0.1.0/vendor/RoutingKit/src/test_tag_map.cpp +49 -0
  125. pyroutingkit-0.1.0/vendor/RoutingKit/src/timer.cpp +18 -0
  126. pyroutingkit-0.1.0/vendor/RoutingKit/src/vector_io.cpp +64 -0
  127. pyroutingkit-0.1.0/vendor/RoutingKit/src/verify.cpp +95 -0
  128. pyroutingkit-0.1.0/vendor/RoutingKit/src/verify.h +29 -0
@@ -0,0 +1,71 @@
1
+ name: Build & Publish
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ wheels:
10
+ name: Build wheels (${{ matrix.os }})
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ matrix:
14
+ os: [ubuntu-latest, macos-14]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ with:
19
+ submodules: recursive
20
+
21
+ - name: Build wheels
22
+ uses: pypa/cibuildwheel@v2.22
23
+ env:
24
+ CIBW_BUILD: cp310-* cp311-* cp312-* cp313-*
25
+ CIBW_SKIP: "*-win32 *-manylinux_i686 *musllinux* pp*"
26
+ CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
27
+ CIBW_TEST_COMMAND: python -c "from pyroutingkit import CCH, INF_WEIGHT; print('OK')"
28
+
29
+ - uses: actions/upload-artifact@v4
30
+ with:
31
+ name: wheels-${{ matrix.os }}
32
+ path: wheelhouse/*.whl
33
+
34
+ sdist:
35
+ name: Build sdist
36
+ runs-on: ubuntu-latest
37
+ steps:
38
+ - uses: actions/checkout@v4
39
+ with:
40
+ submodules: recursive
41
+
42
+ - name: Install uv
43
+ uses: astral-sh/setup-uv@v4
44
+
45
+ - name: Build sdist
46
+ run: uv build --sdist
47
+
48
+ - uses: actions/upload-artifact@v4
49
+ with:
50
+ name: sdist
51
+ path: dist/*.tar.gz
52
+
53
+ publish:
54
+ name: Publish to PyPI
55
+ runs-on: ubuntu-latest
56
+ needs: [wheels, sdist]
57
+ permissions:
58
+ id-token: write # Required for trusted publishing
59
+
60
+ steps:
61
+ - name: Download all artifacts
62
+ uses: actions/download-artifact@v4
63
+ with:
64
+ path: dist
65
+ merge-multiple: true
66
+
67
+ - name: Publish to PyPI
68
+ uses: pypa/gh-action-pypi-publish@release/v1
69
+ # Uses trusted publishing (OIDC) — no API token needed.
70
+ # Configure at: https://pypi.org/manage/account/publishing/
71
+ # Publisher: GitHub, repo: nullbutt/pyroutingkit, workflow: publish.yml
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ *.py[oc]
3
+ *.so
4
+ *.dylib
5
+ build/
6
+ dist/
7
+ *.egg-info
8
+ .venv
@@ -0,0 +1,3 @@
1
+ [submodule "vendor/RoutingKit"]
2
+ path = vendor/RoutingKit
3
+ url = https://github.com/RoutingKit/RoutingKit.git
@@ -0,0 +1,40 @@
1
+ cmake_minimum_required(VERSION 3.20)
2
+ project(pyroutingkit LANGUAGES CXX)
3
+
4
+ set(CMAKE_CXX_STANDARD 17)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
7
+
8
+ find_package(pybind11 REQUIRED)
9
+
10
+ # RoutingKit source files (only what we need for CCH)
11
+ set(ROUTINGKIT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/vendor/RoutingKit")
12
+
13
+ set(ROUTINGKIT_SOURCES
14
+ ${ROUTINGKIT_DIR}/src/bit_select.cpp
15
+ ${ROUTINGKIT_DIR}/src/bit_vector.cpp
16
+ ${ROUTINGKIT_DIR}/src/customizable_contraction_hierarchy.cpp
17
+ ${ROUTINGKIT_DIR}/src/graph_util.cpp
18
+ ${ROUTINGKIT_DIR}/src/id_mapper.cpp
19
+ ${ROUTINGKIT_DIR}/src/nested_dissection.cpp
20
+ ${ROUTINGKIT_DIR}/src/contraction_hierarchy.cpp
21
+ ${ROUTINGKIT_DIR}/src/strongly_connected_component.cpp
22
+ ${ROUTINGKIT_DIR}/src/timer.cpp
23
+ ${ROUTINGKIT_DIR}/src/geo_position_to_node.cpp
24
+ )
25
+
26
+ # Build RoutingKit as a static library
27
+ add_library(routingkit_static STATIC ${ROUTINGKIT_SOURCES})
28
+ target_include_directories(routingkit_static PUBLIC ${ROUTINGKIT_DIR}/include)
29
+ target_compile_options(routingkit_static PRIVATE -O3)
30
+
31
+ if(APPLE)
32
+ target_compile_options(routingkit_static PRIVATE -Wno-deprecated -Wno-unused-parameter)
33
+ endif()
34
+
35
+ # pybind11 module
36
+ pybind11_add_module(_core src/routingkit_module.cpp)
37
+ target_link_libraries(_core PRIVATE routingkit_static)
38
+ target_include_directories(_core PRIVATE ${ROUTINGKIT_DIR}/include)
39
+
40
+ install(TARGETS _core DESTINATION pyroutingkit)
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyroutingkit
3
+ Version: 0.1.0
4
+ Summary: Python bindings for RoutingKit Customizable Contraction Hierarchies (CCH)
5
+ Author: Ryan Fisk
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: C++
10
+ Classifier: Topic :: Scientific/Engineering :: GIS
11
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
12
+ Project-URL: Repository, https://github.com/nullbutt/pyroutingkit
13
+ Requires-Python: >=3.10
14
+ Requires-Dist: numpy>=1.24
15
+ Description-Content-Type: text/markdown
16
+
17
+ # pyroutingkit
18
+
19
+ Python bindings for [RoutingKit](https://github.com/RoutingKit/RoutingKit) Customizable Contraction Hierarchies (CCH).
20
+
21
+ Provides **microsecond-scale shortest-path queries** on large road networks (millions of nodes). Pre-compiled wheels for Linux (manylinux) and macOS — no C++ toolchain needed at install time.
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ pip install pyroutingkit
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ```python
32
+ import numpy as np
33
+ from pyroutingkit import CCH, INF_WEIGHT
34
+
35
+ # Build topology (one-time, expensive — can be saved/loaded)
36
+ cch = CCH()
37
+ tail = np.array([0, 1, 2], dtype=np.uint32)
38
+ head = np.array([1, 2, 0], dtype=np.uint32)
39
+ lat = np.array([9.0, 9.5, 9.2], dtype=np.float32)
40
+ lon = np.array([38.7, 39.0, 38.5], dtype=np.float32)
41
+ cch.build_topology(tail, head, lat, lon, node_count=3)
42
+
43
+ # Customize with weights (fast, can be called multiple times)
44
+ weights = np.array([100, 150, 120], dtype=np.uint32)
45
+ cch.customize_weights(weights)
46
+
47
+ # Query (microseconds)
48
+ distance, path = cch.query(0, 2)
49
+ print(f"Distance: {distance}, Path: {path}")
50
+
51
+ # Many-to-many matrix
52
+ sources = np.array([0, 1], dtype=np.uint32)
53
+ targets = np.array([1, 2], dtype=np.uint32)
54
+ matrix = cch.distances_many_to_many(sources, targets)
55
+
56
+ # Save/load topology (skip expensive build on next run)
57
+ cch.save_topology("/tmp/cch_cache")
58
+ cch2 = CCH()
59
+ cch2.load_topology("/tmp/cch_cache")
60
+ cch2.customize_weights(weights) # Must re-customize after load
61
+ ```
62
+
63
+ ## API
64
+
65
+ ### `CCH` class
66
+
67
+ | Method | Description |
68
+ |--------|-------------|
69
+ | `build_topology(tail, head, lat, lon, node_count)` | Build CCH from graph arrays. Uses inertial flow for node ordering. |
70
+ | `customize_weights(weights)` | Set edge weights (uint32). Can be called multiple times. |
71
+ | `query(source, target)` | Point-to-point query. Returns `(distance, path)`. |
72
+ | `distances_many_to_many(sources, targets)` | NxM distance matrix. |
73
+ | `save_topology(dir_path)` | Save topology to disk (binary format). |
74
+ | `load_topology(dir_path)` | Load topology from disk (skips build). |
75
+
76
+ ### Properties
77
+
78
+ | Property | Type | Description |
79
+ |----------|------|-------------|
80
+ | `node_count` | `int` | Number of nodes |
81
+ | `arc_count` | `int` | Number of directed edges |
82
+ | `is_built` | `bool` | Topology has been built or loaded |
83
+ | `is_customized` | `bool` | Weights have been set |
84
+
85
+ ### Constants
86
+
87
+ | Constant | Description |
88
+ |----------|-------------|
89
+ | `INF_WEIGHT` | Sentinel value for unreachable pairs |
90
+
91
+ ## Building from source
92
+
93
+ Requires CMake 3.20+ and a C++17 compiler.
94
+
95
+ ```bash
96
+ git clone --recurse-submodules https://github.com/nullbutt/pyroutingkit
97
+ cd pyroutingkit
98
+ uv build
99
+ ```
100
+
101
+ ## License
102
+
103
+ MIT. RoutingKit is also MIT-licensed.
@@ -0,0 +1,87 @@
1
+ # pyroutingkit
2
+
3
+ Python bindings for [RoutingKit](https://github.com/RoutingKit/RoutingKit) Customizable Contraction Hierarchies (CCH).
4
+
5
+ Provides **microsecond-scale shortest-path queries** on large road networks (millions of nodes). Pre-compiled wheels for Linux (manylinux) and macOS — no C++ toolchain needed at install time.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install pyroutingkit
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```python
16
+ import numpy as np
17
+ from pyroutingkit import CCH, INF_WEIGHT
18
+
19
+ # Build topology (one-time, expensive — can be saved/loaded)
20
+ cch = CCH()
21
+ tail = np.array([0, 1, 2], dtype=np.uint32)
22
+ head = np.array([1, 2, 0], dtype=np.uint32)
23
+ lat = np.array([9.0, 9.5, 9.2], dtype=np.float32)
24
+ lon = np.array([38.7, 39.0, 38.5], dtype=np.float32)
25
+ cch.build_topology(tail, head, lat, lon, node_count=3)
26
+
27
+ # Customize with weights (fast, can be called multiple times)
28
+ weights = np.array([100, 150, 120], dtype=np.uint32)
29
+ cch.customize_weights(weights)
30
+
31
+ # Query (microseconds)
32
+ distance, path = cch.query(0, 2)
33
+ print(f"Distance: {distance}, Path: {path}")
34
+
35
+ # Many-to-many matrix
36
+ sources = np.array([0, 1], dtype=np.uint32)
37
+ targets = np.array([1, 2], dtype=np.uint32)
38
+ matrix = cch.distances_many_to_many(sources, targets)
39
+
40
+ # Save/load topology (skip expensive build on next run)
41
+ cch.save_topology("/tmp/cch_cache")
42
+ cch2 = CCH()
43
+ cch2.load_topology("/tmp/cch_cache")
44
+ cch2.customize_weights(weights) # Must re-customize after load
45
+ ```
46
+
47
+ ## API
48
+
49
+ ### `CCH` class
50
+
51
+ | Method | Description |
52
+ |--------|-------------|
53
+ | `build_topology(tail, head, lat, lon, node_count)` | Build CCH from graph arrays. Uses inertial flow for node ordering. |
54
+ | `customize_weights(weights)` | Set edge weights (uint32). Can be called multiple times. |
55
+ | `query(source, target)` | Point-to-point query. Returns `(distance, path)`. |
56
+ | `distances_many_to_many(sources, targets)` | NxM distance matrix. |
57
+ | `save_topology(dir_path)` | Save topology to disk (binary format). |
58
+ | `load_topology(dir_path)` | Load topology from disk (skips build). |
59
+
60
+ ### Properties
61
+
62
+ | Property | Type | Description |
63
+ |----------|------|-------------|
64
+ | `node_count` | `int` | Number of nodes |
65
+ | `arc_count` | `int` | Number of directed edges |
66
+ | `is_built` | `bool` | Topology has been built or loaded |
67
+ | `is_customized` | `bool` | Weights have been set |
68
+
69
+ ### Constants
70
+
71
+ | Constant | Description |
72
+ |----------|-------------|
73
+ | `INF_WEIGHT` | Sentinel value for unreachable pairs |
74
+
75
+ ## Building from source
76
+
77
+ Requires CMake 3.20+ and a C++17 compiler.
78
+
79
+ ```bash
80
+ git clone --recurse-submodules https://github.com/nullbutt/pyroutingkit
81
+ cd pyroutingkit
82
+ uv build
83
+ ```
84
+
85
+ ## License
86
+
87
+ MIT. RoutingKit is also MIT-licensed.
@@ -0,0 +1,36 @@
1
+ [project]
2
+ name = "pyroutingkit"
3
+ version = "0.1.0"
4
+ description = "Python bindings for RoutingKit Customizable Contraction Hierarchies (CCH)"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = "MIT"
8
+ authors = [
9
+ { name = "Ryan Fisk" },
10
+ ]
11
+ classifiers = [
12
+ "Development Status :: 4 - Beta",
13
+ "Programming Language :: Python :: 3",
14
+ "Programming Language :: C++",
15
+ "Topic :: Scientific/Engineering :: GIS",
16
+ "Topic :: Scientific/Engineering :: Information Analysis",
17
+ ]
18
+ dependencies = [
19
+ "numpy>=1.24",
20
+ ]
21
+
22
+ [project.urls]
23
+ Repository = "https://github.com/nullbutt/pyroutingkit"
24
+
25
+ [build-system]
26
+ requires = ["scikit-build-core>=0.10", "pybind11>=2.13"]
27
+ build-backend = "scikit_build_core.build"
28
+
29
+ [tool.scikit-build]
30
+ cmake.source-dir = "."
31
+ wheel.packages = ["src/pyroutingkit"]
32
+
33
+ [dependency-groups]
34
+ dev = [
35
+ "pytest>=9.0.2",
36
+ ]
@@ -0,0 +1,17 @@
1
+ """
2
+ pyroutingkit: Python bindings for RoutingKit Customizable Contraction Hierarchies.
3
+
4
+ Provides microsecond-scale shortest-path queries on large road networks.
5
+
6
+ Usage:
7
+ from pyroutingkit import CCH, INF_WEIGHT
8
+
9
+ cch = CCH()
10
+ cch.build_topology(tail, head, latitude, longitude, node_count)
11
+ cch.customize_weights(weights_uint32)
12
+ distance, path = cch.query(source, target)
13
+ """
14
+
15
+ from pyroutingkit._core import CCH, INF_WEIGHT
16
+
17
+ __all__ = ["CCH", "INF_WEIGHT"]
@@ -0,0 +1,263 @@
1
+ /**
2
+ * pybind11 wrapper for RoutingKit's Customizable Contraction Hierarchy (CCH).
3
+ *
4
+ * Exposes:
5
+ * - CCH topology construction (compute nested dissection order + build CCH)
6
+ * - Metric customization (uint32 weights)
7
+ * - Point-to-point query (distance + node path)
8
+ * - Many-to-many distance matrix
9
+ * - Topology serialization (save/load as binary vectors)
10
+ */
11
+
12
+ #include <pybind11/pybind11.h>
13
+ #include <pybind11/numpy.h>
14
+ #include <pybind11/stl.h>
15
+
16
+ #include <routingkit/customizable_contraction_hierarchy.h>
17
+ #include <routingkit/nested_dissection.h>
18
+ #include <routingkit/constants.h>
19
+ #include <routingkit/vector_io.h>
20
+
21
+ #include <vector>
22
+ #include <string>
23
+ #include <stdexcept>
24
+ #include <fstream>
25
+ #include <cstdint>
26
+
27
+ namespace py = pybind11;
28
+
29
+ class CCHWrapper {
30
+ public:
31
+ CCHWrapper() : node_count_(0), built_(false), customized_(false) {}
32
+
33
+ /**
34
+ * Build CCH topology from graph arrays.
35
+ *
36
+ * Uses compute_nested_node_dissection_order_using_inertial_flow for the
37
+ * elimination order (requires node coordinates).
38
+ */
39
+ void build_topology(
40
+ py::array_t<uint32_t> tail_arr,
41
+ py::array_t<uint32_t> head_arr,
42
+ py::array_t<float> latitude_arr,
43
+ py::array_t<float> longitude_arr,
44
+ uint32_t node_count
45
+ ) {
46
+ auto tail_buf = tail_arr.request();
47
+ auto head_buf = head_arr.request();
48
+ auto lat_buf = latitude_arr.request();
49
+ auto lon_buf = longitude_arr.request();
50
+
51
+ if (tail_buf.size != head_buf.size) {
52
+ throw std::invalid_argument("tail and head must have same length");
53
+ }
54
+ if (lat_buf.size != (ssize_t)node_count || lon_buf.size != (ssize_t)node_count) {
55
+ throw std::invalid_argument("latitude/longitude must have length == node_count");
56
+ }
57
+
58
+ node_count_ = node_count;
59
+
60
+ // Copy into std::vector
61
+ auto* tail_ptr = static_cast<uint32_t*>(tail_buf.ptr);
62
+ auto* head_ptr = static_cast<uint32_t*>(head_buf.ptr);
63
+ auto* lat_ptr = static_cast<float*>(lat_buf.ptr);
64
+ auto* lon_ptr = static_cast<float*>(lon_buf.ptr);
65
+
66
+ tail_.assign(tail_ptr, tail_ptr + tail_buf.size);
67
+ head_.assign(head_ptr, head_ptr + head_buf.size);
68
+ std::vector<float> latitude(lat_ptr, lat_ptr + lat_buf.size);
69
+ std::vector<float> longitude(lon_ptr, lon_ptr + lon_buf.size);
70
+
71
+ // Compute nested dissection order using inertial flow
72
+ auto order = RoutingKit::compute_nested_node_dissection_order_using_inertial_flow(
73
+ node_count, tail_, head_, latitude, longitude
74
+ );
75
+
76
+ // Build CCH
77
+ cch_ = RoutingKit::CustomizableContractionHierarchy(order, tail_, head_);
78
+ built_ = true;
79
+ customized_ = false;
80
+ }
81
+
82
+ /**
83
+ * Customize weights (quantized uint32).
84
+ * Must be called after build_topology and before query.
85
+ */
86
+ void customize_weights(py::array_t<uint32_t> weights_arr) {
87
+ if (!built_) {
88
+ throw std::runtime_error("Must call build_topology before customize_weights");
89
+ }
90
+
91
+ auto buf = weights_arr.request();
92
+ if (buf.size != (ssize_t)tail_.size()) {
93
+ throw std::invalid_argument(
94
+ "weights length (" + std::to_string(buf.size) +
95
+ ") must equal arc count (" + std::to_string(tail_.size()) + ")"
96
+ );
97
+ }
98
+
99
+ auto* ptr = static_cast<uint32_t*>(buf.ptr);
100
+ weights_.assign(ptr, ptr + buf.size);
101
+
102
+ metric_ = RoutingKit::CustomizableContractionHierarchyMetric(cch_, weights_);
103
+ metric_.customize();
104
+ query_ = RoutingKit::CustomizableContractionHierarchyQuery(metric_);
105
+ customized_ = true;
106
+ }
107
+
108
+ /**
109
+ * Point-to-point query.
110
+ * Returns (distance, node_path) where distance is uint32
111
+ * and node_path is the list of original node indices.
112
+ */
113
+ py::tuple query(uint32_t source, uint32_t target) {
114
+ if (!customized_) {
115
+ throw std::runtime_error("Must call customize_weights before query");
116
+ }
117
+
118
+ query_.reset().add_source(source).add_target(target).run();
119
+ unsigned dist = query_.get_distance();
120
+
121
+ if (dist == RoutingKit::inf_weight) {
122
+ // Unreachable
123
+ return py::make_tuple(dist, py::list());
124
+ }
125
+
126
+ auto path = query_.get_node_path();
127
+ return py::make_tuple(dist, py::cast(path));
128
+ }
129
+
130
+ /**
131
+ * Many-to-many distance matrix.
132
+ * Uses one-to-many queries for each source.
133
+ * Returns a flattened uint32 array of shape (len(sources) * len(targets)).
134
+ */
135
+ py::array_t<uint32_t> distances_many_to_many(
136
+ py::array_t<uint32_t> sources_arr,
137
+ py::array_t<uint32_t> targets_arr
138
+ ) {
139
+ if (!customized_) {
140
+ throw std::runtime_error("Must call customize_weights before distances_many_to_many");
141
+ }
142
+
143
+ auto src_buf = sources_arr.request();
144
+ auto tgt_buf = targets_arr.request();
145
+ auto* src_ptr = static_cast<uint32_t*>(src_buf.ptr);
146
+ auto* tgt_ptr = static_cast<uint32_t*>(tgt_buf.ptr);
147
+
148
+ size_t n_src = src_buf.size;
149
+ size_t n_tgt = tgt_buf.size;
150
+
151
+ std::vector<uint32_t> targets(tgt_ptr, tgt_ptr + n_tgt);
152
+
153
+ // Result matrix
154
+ std::vector<uint32_t> result(n_src * n_tgt);
155
+
156
+ // Pin targets once, then iterate sources
157
+ query_.reset().pin_targets(targets);
158
+
159
+ for (size_t i = 0; i < n_src; i++) {
160
+ query_.reset_source().add_source(src_ptr[i]).run_to_pinned_targets();
161
+ auto dists = query_.get_distances_to_targets();
162
+ for (size_t j = 0; j < n_tgt; j++) {
163
+ result[i * n_tgt + j] = dists[j];
164
+ }
165
+ }
166
+
167
+ // Return as numpy array with shape (n_src, n_tgt)
168
+ auto result_arr = py::array_t<uint32_t>({(ssize_t)n_src, (ssize_t)n_tgt});
169
+ auto result_buf = result_arr.request();
170
+ std::memcpy(result_buf.ptr, result.data(), result.size() * sizeof(uint32_t));
171
+ return result_arr;
172
+ }
173
+
174
+ /**
175
+ * Save CCH topology + edge arrays to a directory.
176
+ */
177
+ void save_topology(const std::string& dir_path) {
178
+ if (!built_) {
179
+ throw std::runtime_error("Must call build_topology before save_topology");
180
+ }
181
+
182
+ RoutingKit::save_vector(dir_path + "/tail", tail_);
183
+ RoutingKit::save_vector(dir_path + "/head", head_);
184
+ RoutingKit::save_vector(dir_path + "/order", cch_.order);
185
+ RoutingKit::save_vector(dir_path + "/rank", cch_.rank);
186
+
187
+ // Save node count as a single-element vector
188
+ std::vector<uint32_t> nc = {node_count_};
189
+ RoutingKit::save_vector(dir_path + "/node_count", nc);
190
+ }
191
+
192
+ /**
193
+ * Load CCH topology from a directory (skips the expensive nested dissection).
194
+ */
195
+ void load_topology(const std::string& dir_path) {
196
+ tail_ = RoutingKit::load_vector<unsigned>(dir_path + "/tail");
197
+ head_ = RoutingKit::load_vector<unsigned>(dir_path + "/head");
198
+ auto order = RoutingKit::load_vector<unsigned>(dir_path + "/order");
199
+ auto nc = RoutingKit::load_vector<unsigned>(dir_path + "/node_count");
200
+
201
+ if (nc.empty()) {
202
+ throw std::runtime_error("Invalid topology: missing node_count");
203
+ }
204
+ node_count_ = nc[0];
205
+
206
+ cch_ = RoutingKit::CustomizableContractionHierarchy(order, tail_, head_);
207
+ built_ = true;
208
+ customized_ = false;
209
+ }
210
+
211
+ uint32_t get_node_count() const { return node_count_; }
212
+ size_t get_arc_count() const { return tail_.size(); }
213
+ bool is_built() const { return built_; }
214
+ bool is_customized() const { return customized_; }
215
+
216
+ private:
217
+ uint32_t node_count_;
218
+ bool built_;
219
+ bool customized_;
220
+
221
+ std::vector<unsigned> tail_;
222
+ std::vector<unsigned> head_;
223
+ std::vector<unsigned> weights_;
224
+
225
+ RoutingKit::CustomizableContractionHierarchy cch_;
226
+ RoutingKit::CustomizableContractionHierarchyMetric metric_;
227
+ RoutingKit::CustomizableContractionHierarchyQuery query_;
228
+ };
229
+
230
+
231
+ PYBIND11_MODULE(_core, m) {
232
+ m.doc() = "RoutingKit CCH wrapper for Python";
233
+
234
+ py::class_<CCHWrapper>(m, "CCH")
235
+ .def(py::init<>())
236
+ .def("build_topology", &CCHWrapper::build_topology,
237
+ py::arg("tail"), py::arg("head"),
238
+ py::arg("latitude"), py::arg("longitude"),
239
+ py::arg("node_count"),
240
+ "Build CCH topology from graph arrays with inertial flow ordering")
241
+ .def("customize_weights", &CCHWrapper::customize_weights,
242
+ py::arg("weights"),
243
+ "Customize CCH with uint32 weights")
244
+ .def("query", &CCHWrapper::query,
245
+ py::arg("source"), py::arg("target"),
246
+ "Point-to-point query. Returns (distance, node_path)")
247
+ .def("distances_many_to_many", &CCHWrapper::distances_many_to_many,
248
+ py::arg("sources"), py::arg("targets"),
249
+ "Many-to-many distance matrix")
250
+ .def("save_topology", &CCHWrapper::save_topology,
251
+ py::arg("dir_path"),
252
+ "Save CCH topology to directory")
253
+ .def("load_topology", &CCHWrapper::load_topology,
254
+ py::arg("dir_path"),
255
+ "Load CCH topology from directory")
256
+ .def_property_readonly("node_count", &CCHWrapper::get_node_count)
257
+ .def_property_readonly("arc_count", &CCHWrapper::get_arc_count)
258
+ .def_property_readonly("is_built", &CCHWrapper::is_built)
259
+ .def_property_readonly("is_customized", &CCHWrapper::is_customized);
260
+
261
+ // Expose inf_weight constant
262
+ m.attr("INF_WEIGHT") = RoutingKit::inf_weight;
263
+ }