sameer-graph-lib 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.
- sameer_graph_lib-0.1.0/LICENSE +21 -0
- sameer_graph_lib-0.1.0/PKG-INFO +197 -0
- sameer_graph_lib-0.1.0/README.md +163 -0
- sameer_graph_lib-0.1.0/pyproject.toml +41 -0
- sameer_graph_lib-0.1.0/setup.cfg +4 -0
- sameer_graph_lib-0.1.0/src/sameer_graph_lib/__init__.py +28 -0
- sameer_graph_lib-0.1.0/src/sameer_graph_lib/_h3.py +123 -0
- sameer_graph_lib-0.1.0/src/sameer_graph_lib/affinity_graph.py +775 -0
- sameer_graph_lib-0.1.0/src/sameer_graph_lib/corridor_extractor.py +82 -0
- sameer_graph_lib-0.1.0/src/sameer_graph_lib/geometry.py +75 -0
- sameer_graph_lib-0.1.0/src/sameer_graph_lib/hex_graph.py +15 -0
- sameer_graph_lib-0.1.0/src/sameer_graph_lib/plotting.py +211 -0
- sameer_graph_lib-0.1.0/src/sameer_graph_lib/spatial_ingestor.py +170 -0
- sameer_graph_lib-0.1.0/src/sameer_graph_lib/topology_analyzer.py +174 -0
- sameer_graph_lib-0.1.0/src/sameer_graph_lib.egg-info/PKG-INFO +197 -0
- sameer_graph_lib-0.1.0/src/sameer_graph_lib.egg-info/SOURCES.txt +23 -0
- sameer_graph_lib-0.1.0/src/sameer_graph_lib.egg-info/dependency_links.txt +1 -0
- sameer_graph_lib-0.1.0/src/sameer_graph_lib.egg-info/requires.txt +15 -0
- sameer_graph_lib-0.1.0/src/sameer_graph_lib.egg-info/top_level.txt +1 -0
- sameer_graph_lib-0.1.0/tests/test_geometry.py +42 -0
- sameer_graph_lib-0.1.0/tests/test_h3_cell_plotting.py +44 -0
- sameer_graph_lib-0.1.0/tests/test_h3_cells_map_plotting.py +45 -0
- sameer_graph_lib-0.1.0/tests/test_h3_grid_distance.py +12 -0
- sameer_graph_lib-0.1.0/tests/test_plotting.py +33 -0
- sameer_graph_lib-0.1.0/tests/test_smoke.py +62 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sameer Kumar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sameer-graph-lib
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: H3 and NetworkX based route affinity graph toolkit.
|
|
5
|
+
Author-email: Sameer <sameerkumarroy073@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/iams31/sameer_graph_lib
|
|
8
|
+
Project-URL: Repository, https://github.com/iams31/sameer_graph_lib
|
|
9
|
+
Project-URL: Issues, https://github.com/iams31/sameer_graph_lib/issues
|
|
10
|
+
Keywords: h3,networkx,geospatial,routing,graph
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: h3>=4.0.0
|
|
22
|
+
Requires-Dist: networkx>=3.0
|
|
23
|
+
Provides-Extra: plot
|
|
24
|
+
Requires-Dist: matplotlib>=3.7; extra == "plot"
|
|
25
|
+
Provides-Extra: geo
|
|
26
|
+
Requires-Dist: contextily>=1.5; extra == "geo"
|
|
27
|
+
Requires-Dist: geopandas>=0.14; extra == "geo"
|
|
28
|
+
Requires-Dist: shapely>=2.0; extra == "geo"
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
31
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
32
|
+
Requires-Dist: twine>=5.1; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# sameer-graph-lib
|
|
36
|
+
|
|
37
|
+
`sameer-graph-lib` is an editable Python library for building H3-based route affinity graphs with NetworkX.
|
|
38
|
+
|
|
39
|
+
It turns H3 arrays, latitude/longitude sequences, and encoded polylines into connected hex chains, inserts them into a weighted graph, extracts high-affinity corridors, and decomposes the graph into a main trunk plus minor branches.
|
|
40
|
+
|
|
41
|
+
## Editable install
|
|
42
|
+
|
|
43
|
+
```powershell
|
|
44
|
+
python -m pip install -e ".[dev,plot]"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Because the install is editable, changes you make inside `src/sameer_graph_lib` are picked up immediately by Python without reinstalling.
|
|
48
|
+
|
|
49
|
+
If your machine uses `uv`, run commands through the managed environment:
|
|
50
|
+
|
|
51
|
+
```powershell
|
|
52
|
+
uv run --extra dev --extra plot python -c "import sameer_graph_lib; print(sameer_graph_lib.__version__)"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Install
|
|
56
|
+
|
|
57
|
+
From PyPI after publication:
|
|
58
|
+
|
|
59
|
+
```powershell
|
|
60
|
+
pip install sameer-graph-lib
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
With optional plotting and geospatial extras:
|
|
64
|
+
|
|
65
|
+
```powershell
|
|
66
|
+
pip install "sameer-graph-lib[plot,geo]"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Quick start
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from sameer_graph_lib import HexGraph
|
|
73
|
+
|
|
74
|
+
graph = HexGraph(hex_resolution=9)
|
|
75
|
+
|
|
76
|
+
route = graph.add_latlng_sequence([
|
|
77
|
+
(12.9716, 77.5946),
|
|
78
|
+
(12.9760, 77.5990),
|
|
79
|
+
])
|
|
80
|
+
|
|
81
|
+
print(graph.get_graph_stats())
|
|
82
|
+
selected = graph.get_appropriate_hexes(cutoff=0.8)
|
|
83
|
+
fig = graph.visualize_graph(title="80% compact cluster", highlight_hexes=selected)
|
|
84
|
+
print(graph.decompose_branches())
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Main APIs
|
|
88
|
+
|
|
89
|
+
- `SpatialIngestor`: converts H3 arrays, lat/lng sequences, and encoded polylines into contiguous H3 chains.
|
|
90
|
+
- `AffinityGraph`: NetworkX wrapper for array-based insertion, nearest attachment, affinity scoring, editing, and JSON persistence.
|
|
91
|
+
- `CorridorExtractor`: uses exact all-node Dijkstra selection to extract the most compact cluster covering a target percentage of graph traversal volume.
|
|
92
|
+
- `TopologyAnalyzer`: separates the main branch from residual minor branches.
|
|
93
|
+
- `HexGraph`: backwards-compatible convenience class for your original code style.
|
|
94
|
+
|
|
95
|
+
Graph creation follows the original per-node procedure: H3 arrays are normalized, then each hex is inserted with `add_node`/`add_hex`. Lat/lng sequences and encoded polylines are first converted into H3 arrays at the requested resolution, then inserted the same way.
|
|
96
|
+
|
|
97
|
+
For QC, use:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
fig = graph.visualize_graph(highlight_hexes=selected)
|
|
101
|
+
fig.savefig("graph_qc.png", dpi=150, bbox_inches="tight")
|
|
102
|
+
|
|
103
|
+
fig = graph.visualize_step_by_step(route[:5], labels=["A", "B", "C", "D", "E"])
|
|
104
|
+
fig.savefig("insertion_steps.png", dpi=150, bbox_inches="tight")
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
To plot actual H3 hex boundaries as geospatial polygons:
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from sameer_graph_lib import plot_h3_cells, plot_h3_cells_map
|
|
111
|
+
|
|
112
|
+
fig = plot_h3_cells("88618c4f29fffff", label_full_hex=True)
|
|
113
|
+
fig.savefig("single_h3_cell.png", dpi=150, bbox_inches="tight")
|
|
114
|
+
|
|
115
|
+
fig = graph.plot_h3_cells(highlight_hexes=selected, show_labels=False)
|
|
116
|
+
fig.savefig("h3_cell_footprint.png", dpi=150, bbox_inches="tight")
|
|
117
|
+
|
|
118
|
+
fig = plot_h3_cells_map(route, selected_cells=selected)
|
|
119
|
+
fig.savefig("h3_cell_basemap.png", dpi=150, bbox_inches="tight")
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`plot_h3_cells_map` uses GeoPandas + Contextily. Install it with:
|
|
123
|
+
|
|
124
|
+
```powershell
|
|
125
|
+
python -m pip install -e ".[plot,geo]"
|
|
126
|
+
uv run --extra plot --extra geo python -c "from sameer_graph_lib import plot_h3_cells_map"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
To get H3 centers and a convex hull:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from sameer_graph_lib import getLatLng, h3_convex_hull
|
|
133
|
+
|
|
134
|
+
points = getLatLng(route) # [(lat, lng), ...]
|
|
135
|
+
hull = h3_convex_hull(route) # Shapely geometry in (lng, lat)
|
|
136
|
+
graph_hull = graph.convex_hull() # Same, using graph nodes
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Useful commands
|
|
140
|
+
|
|
141
|
+
```powershell
|
|
142
|
+
python -m pytest
|
|
143
|
+
python -m build
|
|
144
|
+
uv run --extra dev pytest -q
|
|
145
|
+
uv run --extra dev python -m build
|
|
146
|
+
uv run --extra dev python -m twine check dist/*
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Build artifacts will appear in `dist/` after `python -m build`.
|
|
150
|
+
|
|
151
|
+
## Publish To PyPI
|
|
152
|
+
|
|
153
|
+
1. Build the package:
|
|
154
|
+
|
|
155
|
+
```powershell
|
|
156
|
+
uv run --extra dev python -m build
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
2. Validate the package metadata:
|
|
160
|
+
|
|
161
|
+
```powershell
|
|
162
|
+
uv run --extra dev python -m twine check dist/*
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
3. Upload to PyPI:
|
|
166
|
+
|
|
167
|
+
```powershell
|
|
168
|
+
uv run --extra dev python -m twine upload dist/*
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
After upload, users can install it with:
|
|
172
|
+
|
|
173
|
+
```powershell
|
|
174
|
+
pip install sameer-graph-lib
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Publish From GitHub
|
|
178
|
+
|
|
179
|
+
This repo also includes a Trusted Publishing workflow in
|
|
180
|
+
[.github/workflows/publish.yml](C:/Users/rrran/Desktop/sameer_graph_lib/.github/workflows/publish.yml:1).
|
|
181
|
+
|
|
182
|
+
To finish that setup:
|
|
183
|
+
|
|
184
|
+
1. Create the project on PyPI, or reserve the name `sameer-graph-lib`.
|
|
185
|
+
2. On PyPI, open the project settings and add a Trusted Publisher for:
|
|
186
|
+
`owner`: `iams31`
|
|
187
|
+
`repository`: `sameer_graph_lib`
|
|
188
|
+
`workflow`: `publish.yml`
|
|
189
|
+
`environment`: `pypi`
|
|
190
|
+
3. Create a GitHub Release, or run the workflow manually from the Actions tab.
|
|
191
|
+
|
|
192
|
+
After that, GitHub Actions can publish without storing a long-lived PyPI token.
|
|
193
|
+
|
|
194
|
+
Official references:
|
|
195
|
+
|
|
196
|
+
- PyPI Trusted Publishing: https://docs.pypi.org/trusted-publishers/
|
|
197
|
+
- Packaging guide upload flow: https://packaging.python.org/tutorials/packaging-projects/
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# sameer-graph-lib
|
|
2
|
+
|
|
3
|
+
`sameer-graph-lib` is an editable Python library for building H3-based route affinity graphs with NetworkX.
|
|
4
|
+
|
|
5
|
+
It turns H3 arrays, latitude/longitude sequences, and encoded polylines into connected hex chains, inserts them into a weighted graph, extracts high-affinity corridors, and decomposes the graph into a main trunk plus minor branches.
|
|
6
|
+
|
|
7
|
+
## Editable install
|
|
8
|
+
|
|
9
|
+
```powershell
|
|
10
|
+
python -m pip install -e ".[dev,plot]"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Because the install is editable, changes you make inside `src/sameer_graph_lib` are picked up immediately by Python without reinstalling.
|
|
14
|
+
|
|
15
|
+
If your machine uses `uv`, run commands through the managed environment:
|
|
16
|
+
|
|
17
|
+
```powershell
|
|
18
|
+
uv run --extra dev --extra plot python -c "import sameer_graph_lib; print(sameer_graph_lib.__version__)"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
From PyPI after publication:
|
|
24
|
+
|
|
25
|
+
```powershell
|
|
26
|
+
pip install sameer-graph-lib
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
With optional plotting and geospatial extras:
|
|
30
|
+
|
|
31
|
+
```powershell
|
|
32
|
+
pip install "sameer-graph-lib[plot,geo]"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from sameer_graph_lib import HexGraph
|
|
39
|
+
|
|
40
|
+
graph = HexGraph(hex_resolution=9)
|
|
41
|
+
|
|
42
|
+
route = graph.add_latlng_sequence([
|
|
43
|
+
(12.9716, 77.5946),
|
|
44
|
+
(12.9760, 77.5990),
|
|
45
|
+
])
|
|
46
|
+
|
|
47
|
+
print(graph.get_graph_stats())
|
|
48
|
+
selected = graph.get_appropriate_hexes(cutoff=0.8)
|
|
49
|
+
fig = graph.visualize_graph(title="80% compact cluster", highlight_hexes=selected)
|
|
50
|
+
print(graph.decompose_branches())
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Main APIs
|
|
54
|
+
|
|
55
|
+
- `SpatialIngestor`: converts H3 arrays, lat/lng sequences, and encoded polylines into contiguous H3 chains.
|
|
56
|
+
- `AffinityGraph`: NetworkX wrapper for array-based insertion, nearest attachment, affinity scoring, editing, and JSON persistence.
|
|
57
|
+
- `CorridorExtractor`: uses exact all-node Dijkstra selection to extract the most compact cluster covering a target percentage of graph traversal volume.
|
|
58
|
+
- `TopologyAnalyzer`: separates the main branch from residual minor branches.
|
|
59
|
+
- `HexGraph`: backwards-compatible convenience class for your original code style.
|
|
60
|
+
|
|
61
|
+
Graph creation follows the original per-node procedure: H3 arrays are normalized, then each hex is inserted with `add_node`/`add_hex`. Lat/lng sequences and encoded polylines are first converted into H3 arrays at the requested resolution, then inserted the same way.
|
|
62
|
+
|
|
63
|
+
For QC, use:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
fig = graph.visualize_graph(highlight_hexes=selected)
|
|
67
|
+
fig.savefig("graph_qc.png", dpi=150, bbox_inches="tight")
|
|
68
|
+
|
|
69
|
+
fig = graph.visualize_step_by_step(route[:5], labels=["A", "B", "C", "D", "E"])
|
|
70
|
+
fig.savefig("insertion_steps.png", dpi=150, bbox_inches="tight")
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
To plot actual H3 hex boundaries as geospatial polygons:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from sameer_graph_lib import plot_h3_cells, plot_h3_cells_map
|
|
77
|
+
|
|
78
|
+
fig = plot_h3_cells("88618c4f29fffff", label_full_hex=True)
|
|
79
|
+
fig.savefig("single_h3_cell.png", dpi=150, bbox_inches="tight")
|
|
80
|
+
|
|
81
|
+
fig = graph.plot_h3_cells(highlight_hexes=selected, show_labels=False)
|
|
82
|
+
fig.savefig("h3_cell_footprint.png", dpi=150, bbox_inches="tight")
|
|
83
|
+
|
|
84
|
+
fig = plot_h3_cells_map(route, selected_cells=selected)
|
|
85
|
+
fig.savefig("h3_cell_basemap.png", dpi=150, bbox_inches="tight")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`plot_h3_cells_map` uses GeoPandas + Contextily. Install it with:
|
|
89
|
+
|
|
90
|
+
```powershell
|
|
91
|
+
python -m pip install -e ".[plot,geo]"
|
|
92
|
+
uv run --extra plot --extra geo python -c "from sameer_graph_lib import plot_h3_cells_map"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
To get H3 centers and a convex hull:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from sameer_graph_lib import getLatLng, h3_convex_hull
|
|
99
|
+
|
|
100
|
+
points = getLatLng(route) # [(lat, lng), ...]
|
|
101
|
+
hull = h3_convex_hull(route) # Shapely geometry in (lng, lat)
|
|
102
|
+
graph_hull = graph.convex_hull() # Same, using graph nodes
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Useful commands
|
|
106
|
+
|
|
107
|
+
```powershell
|
|
108
|
+
python -m pytest
|
|
109
|
+
python -m build
|
|
110
|
+
uv run --extra dev pytest -q
|
|
111
|
+
uv run --extra dev python -m build
|
|
112
|
+
uv run --extra dev python -m twine check dist/*
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Build artifacts will appear in `dist/` after `python -m build`.
|
|
116
|
+
|
|
117
|
+
## Publish To PyPI
|
|
118
|
+
|
|
119
|
+
1. Build the package:
|
|
120
|
+
|
|
121
|
+
```powershell
|
|
122
|
+
uv run --extra dev python -m build
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
2. Validate the package metadata:
|
|
126
|
+
|
|
127
|
+
```powershell
|
|
128
|
+
uv run --extra dev python -m twine check dist/*
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
3. Upload to PyPI:
|
|
132
|
+
|
|
133
|
+
```powershell
|
|
134
|
+
uv run --extra dev python -m twine upload dist/*
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
After upload, users can install it with:
|
|
138
|
+
|
|
139
|
+
```powershell
|
|
140
|
+
pip install sameer-graph-lib
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Publish From GitHub
|
|
144
|
+
|
|
145
|
+
This repo also includes a Trusted Publishing workflow in
|
|
146
|
+
[.github/workflows/publish.yml](C:/Users/rrran/Desktop/sameer_graph_lib/.github/workflows/publish.yml:1).
|
|
147
|
+
|
|
148
|
+
To finish that setup:
|
|
149
|
+
|
|
150
|
+
1. Create the project on PyPI, or reserve the name `sameer-graph-lib`.
|
|
151
|
+
2. On PyPI, open the project settings and add a Trusted Publisher for:
|
|
152
|
+
`owner`: `iams31`
|
|
153
|
+
`repository`: `sameer_graph_lib`
|
|
154
|
+
`workflow`: `publish.yml`
|
|
155
|
+
`environment`: `pypi`
|
|
156
|
+
3. Create a GitHub Release, or run the workflow manually from the Actions tab.
|
|
157
|
+
|
|
158
|
+
After that, GitHub Actions can publish without storing a long-lived PyPI token.
|
|
159
|
+
|
|
160
|
+
Official references:
|
|
161
|
+
|
|
162
|
+
- PyPI Trusted Publishing: https://docs.pypi.org/trusted-publishers/
|
|
163
|
+
- Packaging guide upload flow: https://packaging.python.org/tutorials/packaging-projects/
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sameer-graph-lib"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "H3 and NetworkX based route affinity graph toolkit."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Sameer", email = "sameerkumarroy073@gmail.com" }
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"h3>=4.0.0",
|
|
17
|
+
"networkx>=3.0"
|
|
18
|
+
]
|
|
19
|
+
keywords = ["h3", "networkx", "geospatial", "routing", "graph"]
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 3 - Alpha",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Topic :: Scientific/Engineering :: GIS"
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://github.com/iams31/sameer_graph_lib"
|
|
32
|
+
Repository = "https://github.com/iams31/sameer_graph_lib"
|
|
33
|
+
Issues = "https://github.com/iams31/sameer_graph_lib/issues"
|
|
34
|
+
|
|
35
|
+
[project.optional-dependencies]
|
|
36
|
+
plot = ["matplotlib>=3.7"]
|
|
37
|
+
geo = ["contextily>=1.5", "geopandas>=0.14", "shapely>=2.0"]
|
|
38
|
+
dev = ["pytest>=8.0", "build>=1.2", "twine>=5.1"]
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.packages.find]
|
|
41
|
+
where = ["src"]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Public API for sameer_graph_lib."""
|
|
2
|
+
|
|
3
|
+
from .affinity_graph import AffinityGraph
|
|
4
|
+
from .corridor_extractor import CorridorExtractor
|
|
5
|
+
from .geometry import getLatLng, get_latlng, h3_convex_hull, h3_convex_hull_geojson, makingHull, making_hull
|
|
6
|
+
from .hex_graph import HexGraph
|
|
7
|
+
from .plotting import cells_to_geodataframe, plot_h3_cells, plot_h3_cells_map
|
|
8
|
+
from .spatial_ingestor import SpatialIngestor
|
|
9
|
+
from .topology_analyzer import TopologyAnalyzer
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"AffinityGraph",
|
|
13
|
+
"CorridorExtractor",
|
|
14
|
+
"HexGraph",
|
|
15
|
+
"cells_to_geodataframe",
|
|
16
|
+
"getLatLng",
|
|
17
|
+
"get_latlng",
|
|
18
|
+
"h3_convex_hull",
|
|
19
|
+
"h3_convex_hull_geojson",
|
|
20
|
+
"makingHull",
|
|
21
|
+
"making_hull",
|
|
22
|
+
"plot_h3_cells",
|
|
23
|
+
"plot_h3_cells_map",
|
|
24
|
+
"SpatialIngestor",
|
|
25
|
+
"TopologyAnalyzer",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""Small compatibility layer for h3-py 3.x and 4.x APIs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
from typing import List, Tuple
|
|
7
|
+
|
|
8
|
+
import h3
|
|
9
|
+
|
|
10
|
+
LatLng = Tuple[float, float]
|
|
11
|
+
|
|
12
|
+
EARTH_RADIUS_KM = 6371.0088
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def is_valid_cell(cell: str) -> bool:
|
|
16
|
+
fn = getattr(h3, "is_valid_cell", None) or getattr(h3, "h3_is_valid", None)
|
|
17
|
+
return bool(fn(cell)) if fn else False
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def latlng_to_cell(lat: float, lng: float, resolution: int) -> str:
|
|
21
|
+
fn = getattr(h3, "latlng_to_cell", None)
|
|
22
|
+
if fn:
|
|
23
|
+
return fn(lat, lng, resolution)
|
|
24
|
+
return h3.geo_to_h3(lat, lng, resolution)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def cell_to_latlng(cell: str) -> LatLng:
|
|
28
|
+
fn = getattr(h3, "cell_to_latlng", None)
|
|
29
|
+
if fn:
|
|
30
|
+
lat, lng = fn(cell)
|
|
31
|
+
else:
|
|
32
|
+
lat, lng = h3.h3_to_geo(cell)
|
|
33
|
+
return float(lat), float(lng)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def cell_to_boundary(cell: str) -> List[LatLng]:
|
|
37
|
+
fn = getattr(h3, "cell_to_boundary", None)
|
|
38
|
+
if fn:
|
|
39
|
+
return [(float(lat), float(lng)) for lat, lng in fn(cell)]
|
|
40
|
+
|
|
41
|
+
legacy_fn = getattr(h3, "h3_to_geo_boundary", None)
|
|
42
|
+
if legacy_fn:
|
|
43
|
+
return [(float(lat), float(lng)) for lat, lng in legacy_fn(cell, geo_json=False)]
|
|
44
|
+
|
|
45
|
+
raise RuntimeError("Installed h3 package does not expose cell boundary helpers.")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_resolution(cell: str) -> int:
|
|
49
|
+
fn = getattr(h3, "get_resolution", None) or getattr(h3, "h3_get_resolution", None)
|
|
50
|
+
return int(fn(cell))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def grid_path_cells(start: str, end: str) -> List[str]:
|
|
54
|
+
fn = getattr(h3, "grid_path_cells", None) or getattr(h3, "h3_line", None)
|
|
55
|
+
if not fn:
|
|
56
|
+
raise RuntimeError("Installed h3 package does not expose grid path helpers.")
|
|
57
|
+
return list(fn(start, end))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def average_hexagon_edge_length(resolution: int, unit: str = "km") -> float:
|
|
61
|
+
fn = getattr(h3, "average_hexagon_edge_length", None)
|
|
62
|
+
if fn:
|
|
63
|
+
return float(fn(resolution, unit=unit))
|
|
64
|
+
|
|
65
|
+
fn = getattr(h3, "edge_length", None)
|
|
66
|
+
if fn:
|
|
67
|
+
try:
|
|
68
|
+
return float(fn(resolution, unit=unit))
|
|
69
|
+
except TypeError:
|
|
70
|
+
return float(fn(res=resolution, unit=unit))
|
|
71
|
+
|
|
72
|
+
raise RuntimeError("Installed h3 package does not expose edge length helpers.")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def cell_area(cell: str, unit: str = "km^2") -> float:
|
|
76
|
+
fn = getattr(h3, "cell_area", None)
|
|
77
|
+
if not fn:
|
|
78
|
+
raise RuntimeError("Installed h3 package does not expose cell_area.")
|
|
79
|
+
return float(fn(cell, unit=unit))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def haversine_km(a: LatLng, b: LatLng) -> float:
|
|
83
|
+
lat1, lng1 = map(math.radians, a)
|
|
84
|
+
lat2, lng2 = map(math.radians, b)
|
|
85
|
+
dlat = lat2 - lat1
|
|
86
|
+
dlng = lng2 - lng1
|
|
87
|
+
sin_dlat = math.sin(dlat / 2.0)
|
|
88
|
+
sin_dlng = math.sin(dlng / 2.0)
|
|
89
|
+
h = sin_dlat**2 + math.cos(lat1) * math.cos(lat2) * sin_dlng**2
|
|
90
|
+
return 2.0 * EARTH_RADIUS_KM * math.asin(min(1.0, math.sqrt(h)))
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def approximate_grid_distance(start: str, end: str) -> int:
|
|
94
|
+
if start == end:
|
|
95
|
+
return 0
|
|
96
|
+
|
|
97
|
+
resolution = get_resolution(start)
|
|
98
|
+
edge_km = average_hexagon_edge_length(resolution, unit="km")
|
|
99
|
+
center_spacing_km = max(edge_km * math.sqrt(3), 1e-9)
|
|
100
|
+
distance_km = haversine_km(cell_to_latlng(start), cell_to_latlng(end))
|
|
101
|
+
return max(1, int(round(distance_km / center_spacing_km)))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def grid_distance(start: str, end: str) -> int:
|
|
105
|
+
"""Return native H3 grid distance between two cells."""
|
|
106
|
+
if start == end:
|
|
107
|
+
return 0
|
|
108
|
+
|
|
109
|
+
fn = getattr(h3, "grid_distance", None)
|
|
110
|
+
if fn:
|
|
111
|
+
try:
|
|
112
|
+
return int(fn(start, end))
|
|
113
|
+
except Exception:
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
legacy_fn = getattr(h3, "h3_distance", None)
|
|
117
|
+
if legacy_fn:
|
|
118
|
+
try:
|
|
119
|
+
return int(legacy_fn(start, end))
|
|
120
|
+
except Exception:
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
return approximate_grid_distance(start, end)
|