sameer-graph-lib 0.1.0__tar.gz → 0.2.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 (27) hide show
  1. {sameer_graph_lib-0.1.0/src/sameer_graph_lib.egg-info → sameer_graph_lib-0.2.0}/PKG-INFO +5 -1
  2. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/pyproject.toml +4 -2
  3. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/src/sameer_graph_lib/__init__.py +26 -1
  4. sameer_graph_lib-0.2.0/src/sameer_graph_lib/motion.py +295 -0
  5. sameer_graph_lib-0.2.0/src/sameer_graph_lib/plylinedecoding.py +43 -0
  6. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0/src/sameer_graph_lib.egg-info}/PKG-INFO +5 -1
  7. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/src/sameer_graph_lib.egg-info/SOURCES.txt +2 -0
  8. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/src/sameer_graph_lib.egg-info/requires.txt +5 -0
  9. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/LICENSE +0 -0
  10. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/README.md +0 -0
  11. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/setup.cfg +0 -0
  12. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/src/sameer_graph_lib/_h3.py +0 -0
  13. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/src/sameer_graph_lib/affinity_graph.py +0 -0
  14. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/src/sameer_graph_lib/corridor_extractor.py +0 -0
  15. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/src/sameer_graph_lib/geometry.py +0 -0
  16. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/src/sameer_graph_lib/hex_graph.py +0 -0
  17. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/src/sameer_graph_lib/plotting.py +0 -0
  18. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/src/sameer_graph_lib/spatial_ingestor.py +0 -0
  19. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/src/sameer_graph_lib/topology_analyzer.py +0 -0
  20. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/src/sameer_graph_lib.egg-info/dependency_links.txt +0 -0
  21. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/src/sameer_graph_lib.egg-info/top_level.txt +0 -0
  22. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/tests/test_geometry.py +0 -0
  23. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/tests/test_h3_cell_plotting.py +0 -0
  24. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/tests/test_h3_cells_map_plotting.py +0 -0
  25. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/tests/test_h3_grid_distance.py +0 -0
  26. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/tests/test_plotting.py +0 -0
  27. {sameer_graph_lib-0.1.0 → sameer_graph_lib-0.2.0}/tests/test_smoke.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sameer-graph-lib
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: H3 and NetworkX based route affinity graph toolkit.
5
5
  Author-email: Sameer <sameerkumarroy073@gmail.com>
6
6
  License-Expression: MIT
@@ -20,12 +20,16 @@ Description-Content-Type: text/markdown
20
20
  License-File: LICENSE
21
21
  Requires-Dist: h3>=4.0.0
22
22
  Requires-Dist: networkx>=3.0
23
+ Requires-Dist: polyline>=2.0
23
24
  Provides-Extra: plot
24
25
  Requires-Dist: matplotlib>=3.7; extra == "plot"
25
26
  Provides-Extra: geo
26
27
  Requires-Dist: contextily>=1.5; extra == "geo"
27
28
  Requires-Dist: geopandas>=0.14; extra == "geo"
28
29
  Requires-Dist: shapely>=2.0; extra == "geo"
30
+ Provides-Extra: motion
31
+ Requires-Dist: numpy>=1.24; extra == "motion"
32
+ Requires-Dist: pyproj>=3.5; extra == "motion"
29
33
  Provides-Extra: dev
30
34
  Requires-Dist: pytest>=8.0; extra == "dev"
31
35
  Requires-Dist: build>=1.2; extra == "dev"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sameer-graph-lib"
7
- version = "0.1.0"
7
+ version = "0.2.0"
8
8
  description = "H3 and NetworkX based route affinity graph toolkit."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -14,7 +14,8 @@ authors = [
14
14
  ]
15
15
  dependencies = [
16
16
  "h3>=4.0.0",
17
- "networkx>=3.0"
17
+ "networkx>=3.0",
18
+ "polyline>=2.0"
18
19
  ]
19
20
  keywords = ["h3", "networkx", "geospatial", "routing", "graph"]
20
21
  classifiers = [
@@ -35,6 +36,7 @@ Issues = "https://github.com/iams31/sameer_graph_lib/issues"
35
36
  [project.optional-dependencies]
36
37
  plot = ["matplotlib>=3.7"]
37
38
  geo = ["contextily>=1.5", "geopandas>=0.14", "shapely>=2.0"]
39
+ motion = ["numpy>=1.24", "pyproj>=3.5"]
38
40
  dev = ["pytest>=8.0", "build>=1.2", "twine>=5.1"]
39
41
 
40
42
  [tool.setuptools.packages.find]
@@ -4,7 +4,20 @@ from .affinity_graph import AffinityGraph
4
4
  from .corridor_extractor import CorridorExtractor
5
5
  from .geometry import getLatLng, get_latlng, h3_convex_hull, h3_convex_hull_geojson, makingHull, making_hull
6
6
  from .hex_graph import HexGraph
7
+ from .motion import (
8
+ compute_accelerations,
9
+ compute_accelerations_from_timestamps,
10
+ compute_speeds,
11
+ compute_speeds_from_timestamps,
12
+ detect_sudden_braking_from_timestamps,
13
+ dominant_frequency_hz,
14
+ get_max_speed,
15
+ max_accelaration,
16
+ resample_polyline,
17
+ smooth_speeds_median,
18
+ )
7
19
  from .plotting import cells_to_geodataframe, plot_h3_cells, plot_h3_cells_map
20
+ from .plylinedecoding import decode_polyline, get_hexes_from_polyline
8
21
  from .spatial_ingestor import SpatialIngestor
9
22
  from .topology_analyzer import TopologyAnalyzer
10
23
 
@@ -13,16 +26,28 @@ __all__ = [
13
26
  "CorridorExtractor",
14
27
  "HexGraph",
15
28
  "cells_to_geodataframe",
29
+ "compute_accelerations",
30
+ "compute_accelerations_from_timestamps",
31
+ "compute_speeds",
32
+ "compute_speeds_from_timestamps",
33
+ "decode_polyline",
34
+ "detect_sudden_braking_from_timestamps",
35
+ "dominant_frequency_hz",
16
36
  "getLatLng",
37
+ "get_hexes_from_polyline",
17
38
  "get_latlng",
39
+ "get_max_speed",
18
40
  "h3_convex_hull",
19
41
  "h3_convex_hull_geojson",
20
42
  "makingHull",
21
43
  "making_hull",
44
+ "max_accelaration",
22
45
  "plot_h3_cells",
23
46
  "plot_h3_cells_map",
47
+ "resample_polyline",
48
+ "smooth_speeds_median",
24
49
  "SpatialIngestor",
25
50
  "TopologyAnalyzer",
26
51
  ]
27
52
 
28
- __version__ = "0.1.0"
53
+ __version__ = "0.2.0"
@@ -0,0 +1,295 @@
1
+ """Motion analysis for polylines sampled at a fixed time interval.
2
+
3
+ All inputs use the convention ``polyline = [(lat, lng), (lat, lng), ...]`` and
4
+ ``interval_seconds`` is the time between consecutive points. Distances are
5
+ computed with the haversine formula on a sphere of radius 6_371_000 m.
6
+ """
7
+
8
+ from __future__ import annotations
9
+ from .plylinedecoding import decode_polyline
10
+ from collections.abc import Sequence
11
+ Point = tuple[float, float]
12
+ TimedPoint = tuple[float, float, float]
13
+ def _require_pyproj():
14
+ try:
15
+ import pyproj
16
+ except ImportError as exc:
17
+ raise ImportError(
18
+ "Motion helpers require pyproj: pip install 'sameer-graph-lib[motion]'"
19
+ ) from exc
20
+ return pyproj
21
+ def _require_numpy():
22
+ try:
23
+ import numpy as np
24
+ except ImportError as exc:
25
+ raise ImportError(
26
+ "Motion helpers require numpy: pip install 'sameer-graph-lib[motion]'"
27
+ ) from exc
28
+ return np
29
+ def _haversine_m(p1, p2,geojson=True) -> float:
30
+ pyproj=_require_pyproj()
31
+ geodesic = pyproj.Geod(ellps='WGS84')
32
+ if geojson:
33
+ u,v, distance_m = geodesic.inv(p1[0], p1[1], p2[0], p2[1])
34
+ else:
35
+ u,v, distance_m = geodesic.inv(p1[1], p1[0], p2[1], p2[0])
36
+ return distance_m/1000.0
37
+ def get_max_speed(polyline, interval_seconds: float, smooth: bool = True,geojson=True,precision=5,max_bucket=5) -> float:
38
+ decoded=decode_polyline(polyline_str=polyline, precision=precision, geojson=geojson)
39
+ if len(decoded) < 2:
40
+ return {}
41
+ speed_array=[]
42
+ max_speed={}
43
+ if len(decoded) <= max_bucket + 1:
44
+ for j in range(len(decoded)-1):
45
+ dist=_haversine_m(decoded[j], decoded[j+1],geojson=geojson)
46
+ speed=dist*3600/interval_seconds
47
+ speed_array.append(speed)
48
+ max_speed[1]=max(speed_array)
49
+ max_speed['all']=max(speed_array)
50
+ return max_speed
51
+ for i in range(1,max_bucket+1):
52
+ speed_array1=[]
53
+ for j in range(len(decoded)-1-i):
54
+ dist=_haversine_m(decoded[j], decoded[j+i],geojson=geojson)
55
+ speed=dist*3600/(interval_seconds*i)
56
+ speed_array1.append(speed)
57
+ speed_array.append(speed)
58
+ max_speed[i]=max(speed_array1)
59
+ max_speed['all']=max(speed_array)
60
+ return max_speed
61
+
62
+ def max_accelaration(polyline, interval_seconds: float, smooth: bool = True,geojson=True,precision=5) -> float:
63
+ accelaration_array=[]
64
+ decoded=decode_polyline(polyline_str=polyline, precision=precision, geojson=geojson)
65
+ if len(decoded) < 3:
66
+ return {}
67
+ for i in range(len(decoded)-2):
68
+ dist1=_haversine_m(decoded[i], decoded[i+1],geojson=geojson)
69
+ dist2=_haversine_m(decoded[i+1], decoded[i+2],geojson=geojson)
70
+ speed1=dist1*3600/interval_seconds
71
+ speed2=dist2*3600/interval_seconds
72
+ accelaration=(speed2-speed1)/interval_seconds
73
+ accelaration_array.append(accelaration)
74
+ return {'max':max(accelaration_array),'min':min(accelaration_array)}
75
+
76
+
77
+ # ---------------------------------------------------------------------------
78
+ # New helpers below: m/s and m/s^2 outputs. They use _geodesic_m (true
79
+ # meters) so they stay independent of _haversine_m's km return value.
80
+ # ---------------------------------------------------------------------------
81
+
82
+
83
+ def _geodesic_m(p1, p2, geojson: bool = True) -> float:
84
+ """Geodesic distance in meters between two points."""
85
+ pyproj = _require_pyproj()
86
+ geodesic = pyproj.Geod(ellps="WGS84")
87
+ if geojson:
88
+ _, _, d = geodesic.inv(p1[0], p1[1], p2[0], p2[1])
89
+ else:
90
+ _, _, d = geodesic.inv(p1[1], p1[0], p2[1], p2[0])
91
+ return d
92
+
93
+
94
+ def _decode_or_passthrough(polyline, geojson: bool, precision: int) -> list:
95
+ """Accept either an encoded polyline string or a sequence of points."""
96
+ if isinstance(polyline, str):
97
+ return decode_polyline(polyline_str=polyline, precision=precision, geojson=geojson)
98
+ return list(polyline)
99
+
100
+
101
+ def _validate_timed(points: Sequence[TimedPoint], min_points: int) -> None:
102
+ if len(points) < min_points:
103
+ raise ValueError(
104
+ f"timestamped polyline needs at least {min_points} points, "
105
+ f"got {len(points)}"
106
+ )
107
+ for i in range(len(points) - 1):
108
+ if points[i + 1][2] <= points[i][2]:
109
+ raise ValueError(
110
+ f"timestamps must be strictly increasing; "
111
+ f"got t[{i}]={points[i][2]} >= t[{i+1}]={points[i + 1][2]}"
112
+ )
113
+
114
+
115
+ def compute_speeds(
116
+ polyline,
117
+ interval_seconds: float,
118
+ geojson: bool = True,
119
+ precision: int = 5,
120
+ ) -> list[float]:
121
+ """Return instantaneous speeds (m/s) between consecutive points.
122
+
123
+ Length is ``n - 1``. ``polyline`` may be an encoded string or a sequence
124
+ of points already in the order set by ``geojson``.
125
+ """
126
+ if interval_seconds <= 0:
127
+ raise ValueError("interval_seconds must be positive")
128
+ decoded = _decode_or_passthrough(polyline, geojson, precision)
129
+ if len(decoded) < 2:
130
+ raise ValueError(f"polyline needs at least 2 points, got {len(decoded)}")
131
+ return [
132
+ _geodesic_m(decoded[i], decoded[i + 1], geojson=geojson) / interval_seconds
133
+ for i in range(len(decoded) - 1)
134
+ ]
135
+
136
+
137
+ def compute_accelerations(
138
+ polyline,
139
+ interval_seconds: float,
140
+ geojson: bool = True,
141
+ precision: int = 5,
142
+ ) -> list[float]:
143
+ """Return accelerations (m/s^2) between consecutive speed samples.
144
+
145
+ Length is ``n - 2``. Positive values are acceleration, negative values
146
+ are deceleration.
147
+ """
148
+ speeds = compute_speeds(polyline, interval_seconds, geojson=geojson, precision=precision)
149
+ if len(speeds) < 2:
150
+ raise ValueError("polyline needs at least 3 points to compute acceleration")
151
+ return [
152
+ (speeds[i + 1] - speeds[i]) / interval_seconds
153
+ for i in range(len(speeds) - 1)
154
+ ]
155
+
156
+
157
+ def smooth_speeds_median(
158
+ polyline,
159
+ interval_seconds: float,
160
+ window_size: int = 7,
161
+ geojson: bool = True,
162
+ precision: int = 5,
163
+ ) -> list[float]:
164
+ """Return speeds (m/s) with outliers nullified via sliding-window median.
165
+
166
+ Each sample is replaced by the median of itself and its
167
+ ``window_size - 1`` neighbours (edge-padded at the boundaries). A median
168
+ is robust: a single bad GPS reading is replaced by its neighbourhood's
169
+ middle value, instead of being smeared across surrounding samples
170
+ (which is what FFT or polynomial smoothers would do).
171
+
172
+ Default ``window_size=7`` handles route-shape polylines where outliers
173
+ can come in clusters of 2–3 consecutive samples; for clean GPS traces
174
+ where outliers are isolated, ``window_size=3`` or ``5`` is enough.
175
+ """
176
+ np = _require_numpy()
177
+ if window_size < 3 or window_size % 2 == 0:
178
+ raise ValueError("window_size must be an odd integer >= 3")
179
+
180
+ speeds = compute_speeds(polyline, interval_seconds, geojson=geojson, precision=precision)
181
+ if len(speeds) < window_size:
182
+ return speeds
183
+
184
+ from numpy.lib.stride_tricks import sliding_window_view
185
+
186
+ half = window_size // 2
187
+ arr = np.asarray(speeds, dtype=float)
188
+ padded = np.pad(arr, half, mode="edge")
189
+ windows = sliding_window_view(padded, window_size)
190
+ return np.median(windows, axis=1).tolist()
191
+
192
+
193
+ def dominant_frequency_hz(
194
+ polyline,
195
+ interval_seconds: float,
196
+ geojson: bool = True,
197
+ precision: int = 5,
198
+ ) -> float:
199
+ """Return the dominant non-DC frequency (Hz) of the speed signal."""
200
+ np = _require_numpy()
201
+ speeds = compute_speeds(polyline, interval_seconds, geojson=geojson, precision=precision)
202
+ if len(speeds) < 2:
203
+ return 0.0
204
+
205
+ spectrum = np.abs(np.fft.rfft(speeds))
206
+ freqs = np.fft.rfftfreq(len(speeds), d=interval_seconds)
207
+ spectrum[0] = 0
208
+ if spectrum.max() == 0:
209
+ return 0.0
210
+ return float(freqs[int(spectrum.argmax())])
211
+
212
+
213
+ # ---------------------------------------------------------------------------
214
+ # Timestamped polylines: each point is (lat, lng, t_seconds)
215
+ # ---------------------------------------------------------------------------
216
+
217
+
218
+ def compute_speeds_from_timestamps(points: Sequence[TimedPoint]) -> list[float]:
219
+ """Return speeds (m/s) using each segment's actual delta-t."""
220
+ _validate_timed(points, min_points=2)
221
+ return [
222
+ _geodesic_m(points[i], points[i + 1], geojson=False)
223
+ / (points[i + 1][2] - points[i][2])
224
+ for i in range(len(points) - 1)
225
+ ]
226
+
227
+
228
+ def compute_accelerations_from_timestamps(points: Sequence[TimedPoint]) -> list[float]:
229
+ """Return accelerations (m/s^2) for a non-uniformly-sampled trace.
230
+
231
+ For each interior point ``i+1`` we take ``(v[i+1] - v[i]) / dt`` where
232
+ ``dt = (segment_i_duration + segment_{i+1}_duration) / 2``.
233
+ """
234
+ _validate_timed(points, min_points=3)
235
+ speeds = compute_speeds_from_timestamps(points)
236
+ out = []
237
+ for i in range(len(speeds) - 1):
238
+ dt_i = points[i + 1][2] - points[i][2]
239
+ dt_next = points[i + 2][2] - points[i + 1][2]
240
+ out.append((speeds[i + 1] - speeds[i]) / ((dt_i + dt_next) / 2))
241
+ return out
242
+
243
+
244
+ def detect_sudden_braking_from_timestamps(
245
+ points: Sequence[TimedPoint],
246
+ threshold_mps2: float = -3.0,
247
+ ) -> list[dict]:
248
+ """Return sudden-braking events for a timestamped trace.
249
+
250
+ Each event is ``{index, time_s, deceleration_mps2}`` where ``index``
251
+ is the index in ``points`` of the boundary sample.
252
+ """
253
+ if threshold_mps2 >= 0:
254
+ raise ValueError("threshold_mps2 must be negative (it is a deceleration)")
255
+ accels = compute_accelerations_from_timestamps(points)
256
+ return [
257
+ {
258
+ "index": i + 1,
259
+ "time_s": points[i + 1][2],
260
+ "deceleration_mps2": a,
261
+ }
262
+ for i, a in enumerate(accels)
263
+ if a <= threshold_mps2
264
+ ]
265
+
266
+
267
+ def resample_polyline(
268
+ points: Sequence[TimedPoint],
269
+ interval_seconds: float,
270
+ ) -> list[Point]:
271
+ """Resample a timestamped polyline to a uniform time grid (linear interp).
272
+
273
+ Returns ``[(lat, lng), ...]``. Pass to :func:`compute_speeds` with
274
+ ``geojson=False`` to use FFT smoothing on real GPS logs.
275
+ """
276
+ if interval_seconds <= 0:
277
+ raise ValueError("interval_seconds must be positive")
278
+ _validate_timed(points, min_points=2)
279
+
280
+ t_start = points[0][2]
281
+ t_end = points[-1][2]
282
+ n_steps = int((t_end - t_start) / interval_seconds) + 1
283
+
284
+ out: list[Point] = []
285
+ j = 0
286
+ for k in range(n_steps):
287
+ t = t_start + k * interval_seconds
288
+ while j < len(points) - 2 and points[j + 1][2] < t:
289
+ j += 1
290
+ t1, t2 = points[j][2], points[j + 1][2]
291
+ alpha = 0.0 if t2 == t1 else (t - t1) / (t2 - t1)
292
+ lat = points[j][0] + alpha * (points[j + 1][0] - points[j][0])
293
+ lng = points[j][1] + alpha * (points[j + 1][1] - points[j][1])
294
+ out.append((lat, lng))
295
+ return out
@@ -0,0 +1,43 @@
1
+ """Google encoded-polyline helpers."""
2
+
3
+ from __future__ import annotations
4
+ def _require_polyline():
5
+ try:
6
+ import polyline
7
+ except ImportError as exc:
8
+ raise ImportError(
9
+ "Motion helpers require polyline: pip install 'sameer-graph-lib[motion]'"
10
+ ) from exc
11
+ return polyline
12
+ def _require_h3():
13
+ try:
14
+ import h3
15
+ except ImportError as exc:
16
+ raise ImportError(
17
+ "Motion helpers require polyline: pip install 'sameer-graph-lib[motion]'"
18
+ ) from exc
19
+ return h3
20
+
21
+ def decode_polyline(
22
+ polyline_str: str,
23
+ precision: int = 5,
24
+ geojson: bool = False,
25
+ ) -> list[tuple[float, float]]:
26
+ """Decode a Google encoded polyline string.
27
+
28
+ Returns ``[(lat, lng), ...]`` by default. Pass ``geojson=True`` to get
29
+ GeoJSON-order ``[(lng, lat), ...]`` instead.
30
+ """
31
+ polyline=_require_polyline()
32
+ return polyline.decode(polyline_str, precision=precision, geojson=geojson)
33
+
34
+
35
+ def get_hexes_from_polyline(
36
+ polyline_str: str,
37
+ precision: int = 5,
38
+ res: int = 9,
39
+ ) -> list[str]:
40
+ """Decode a polyline and return the H3 cell at ``res`` for each point."""
41
+ h3=_require_h3()
42
+ coordinates = decode_polyline(polyline_str, precision=precision, geojson=False)
43
+ return [h3.latlng_to_cell(lat, lng, res) for lat, lng in coordinates]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sameer-graph-lib
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: H3 and NetworkX based route affinity graph toolkit.
5
5
  Author-email: Sameer <sameerkumarroy073@gmail.com>
6
6
  License-Expression: MIT
@@ -20,12 +20,16 @@ Description-Content-Type: text/markdown
20
20
  License-File: LICENSE
21
21
  Requires-Dist: h3>=4.0.0
22
22
  Requires-Dist: networkx>=3.0
23
+ Requires-Dist: polyline>=2.0
23
24
  Provides-Extra: plot
24
25
  Requires-Dist: matplotlib>=3.7; extra == "plot"
25
26
  Provides-Extra: geo
26
27
  Requires-Dist: contextily>=1.5; extra == "geo"
27
28
  Requires-Dist: geopandas>=0.14; extra == "geo"
28
29
  Requires-Dist: shapely>=2.0; extra == "geo"
30
+ Provides-Extra: motion
31
+ Requires-Dist: numpy>=1.24; extra == "motion"
32
+ Requires-Dist: pyproj>=3.5; extra == "motion"
29
33
  Provides-Extra: dev
30
34
  Requires-Dist: pytest>=8.0; extra == "dev"
31
35
  Requires-Dist: build>=1.2; extra == "dev"
@@ -7,7 +7,9 @@ src/sameer_graph_lib/affinity_graph.py
7
7
  src/sameer_graph_lib/corridor_extractor.py
8
8
  src/sameer_graph_lib/geometry.py
9
9
  src/sameer_graph_lib/hex_graph.py
10
+ src/sameer_graph_lib/motion.py
10
11
  src/sameer_graph_lib/plotting.py
12
+ src/sameer_graph_lib/plylinedecoding.py
11
13
  src/sameer_graph_lib/spatial_ingestor.py
12
14
  src/sameer_graph_lib/topology_analyzer.py
13
15
  src/sameer_graph_lib.egg-info/PKG-INFO
@@ -1,5 +1,6 @@
1
1
  h3>=4.0.0
2
2
  networkx>=3.0
3
+ polyline>=2.0
3
4
 
4
5
  [dev]
5
6
  pytest>=8.0
@@ -11,5 +12,9 @@ contextily>=1.5
11
12
  geopandas>=0.14
12
13
  shapely>=2.0
13
14
 
15
+ [motion]
16
+ numpy>=1.24
17
+ pyproj>=3.5
18
+
14
19
  [plot]
15
20
  matplotlib>=3.7