pysnapping 0.2.3__tar.gz → 0.2.4__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 (37) hide show
  1. {pysnapping-0.2.3 → pysnapping-0.2.4}/.gitignore +0 -4
  2. pysnapping-0.2.4/.gitlab-ci.yml +71 -0
  3. pysnapping-0.2.4/.pre-commit-config.yaml +43 -0
  4. pysnapping-0.2.4/.python-version +1 -0
  5. {pysnapping-0.2.3 → pysnapping-0.2.4}/PKG-INFO +25 -15
  6. {pysnapping-0.2.3 → pysnapping-0.2.4}/README.md +23 -13
  7. {pysnapping-0.2.3 → pysnapping-0.2.4}/pyproject.toml +28 -4
  8. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/linear_referencing.py +4 -5
  9. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/snap.py +31 -36
  10. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/test/test_snap.py +9 -9
  11. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/test/test_util.py +1 -1
  12. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/util.py +8 -10
  13. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping.egg-info/PKG-INFO +25 -15
  14. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping.egg-info/SOURCES.txt +2 -3
  15. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping.egg-info/scm_file_list.json +12 -13
  16. pysnapping-0.2.4/pysnapping.egg-info/scm_version.json +8 -0
  17. pysnapping-0.2.4/uv.lock +721 -0
  18. pysnapping-0.2.3/.gitlab-ci.yml +0 -66
  19. pysnapping-0.2.3/.pre-commit-config.yaml +0 -38
  20. pysnapping-0.2.3/dev-requirements.in +0 -7
  21. pysnapping-0.2.3/dev-requirements.txt +0 -76
  22. pysnapping-0.2.3/pysnapping.egg-info/scm_version.json +0 -8
  23. pysnapping-0.2.3/requirements.txt +0 -12
  24. {pysnapping-0.2.3 → pysnapping-0.2.4}/.editorconfig +0 -0
  25. {pysnapping-0.2.3 → pysnapping-0.2.4}/LICENSE +0 -0
  26. {pysnapping-0.2.3 → pysnapping-0.2.4}/MANIFEST.in +0 -0
  27. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/__init__.py +1 -1
  28. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/py.typed +0 -0
  29. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/stats.py +3 -3
  30. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/test/__init__.py +0 -0
  31. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/test/complex_trip.geojson +0 -0
  32. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/test/conftest.py +1 -1
  33. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/test/test_linear_referencing.py +5 -5
  34. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping.egg-info/dependency_links.txt +0 -0
  35. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping.egg-info/requires.txt +0 -0
  36. {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping.egg-info/top_level.txt +0 -0
  37. {pysnapping-0.2.3 → pysnapping-0.2.4}/setup.cfg +0 -0
@@ -1,6 +1,5 @@
1
1
  # Configuration
2
2
  /*.ini
3
- uv.lock
4
3
 
5
4
  # Byte-compiled / optimized / DLL files
6
5
  __pycache__/
@@ -75,9 +74,6 @@ target/
75
74
  # Jupyter Notebook
76
75
  .ipynb_checkpoints
77
76
 
78
- # pyenv
79
- .python-version
80
-
81
77
  # celery beat schedule file
82
78
  celerybeat-schedule
83
79
 
@@ -0,0 +1,71 @@
1
+ image: registry.geops.com/geops/gitlab-ci-dockerfiles/trixie-python:latest
2
+
3
+ stages:
4
+ - test
5
+
6
+ variables:
7
+ # Fail instead of updating uv.lock if it is out of sync with pyproject.toml.
8
+ UV_LOCKED: "1"
9
+ UV_NO_EDITABLE: "1"
10
+ # use uv python to get more recent patch releases than from the debian image
11
+ UV_PYTHON_PREFERENCE: "only-managed"
12
+ XDG_CACHE_HOME: "${CI_PROJECT_DIR}/.cache/"
13
+ # keep managed pythons in the cached directory (uv stores them outside its cache by default)
14
+ UV_PYTHON_INSTALL_DIR: "${XDG_CACHE_HOME}/uv-python"
15
+ LC_ALL: "C.UTF-8"
16
+ LANG: "C.UTF-8"
17
+ # for printing version
18
+ PACKAGE_NAME: pysnapping
19
+
20
+ cache:
21
+ # key just that cache does not grow unbounded
22
+ key:
23
+ files:
24
+ - uv.lock
25
+ - .pre-commit-config.yaml
26
+ paths:
27
+ - .cache
28
+
29
+ default:
30
+ before_script:
31
+ - eval $(ssh-agent -s)
32
+ - mkdir -p ~/.ssh
33
+ - echo -e "Host *\nStrictHostKeyChecking no\n\n" > ~/.ssh/config
34
+ - echo "$GITLAB_PIPELINES_KEY" | tr -d '\r' | ssh-add - > /dev/null
35
+ - uv --version
36
+ - uv cache dir
37
+ - uv --preview-features=cache-size cache size --human
38
+ # upgrade cached python to latest patch release (if not cached, other uv commands
39
+ # will install the latest patch release and cache it)
40
+ - uv python upgrade
41
+ # install package and print the version (as seen be the CI) that is actually used in
42
+ # the job (might come from uv cache) for debugging possible cache issues
43
+ - uv run python -c "from importlib.metadata import version; print('${PACKAGE_NAME} version as seen by CI (might miss tags that have not been pulled):', version('${PACKAGE_NAME}'))"
44
+
45
+ after_script:
46
+ - eval "$(ssh-agent -k)" # to stop ssh-agent from running forever
47
+
48
+ run_tests:
49
+ stage: test
50
+ needs: []
51
+ script:
52
+ - uv run pytest -s -vvv --log-cli-level INFO --cov=pysnapping --cov-report term --cov-report xml:cobertura_coverage.xml --cov-fail-under=85 --junitxml=junit_coverage.xml pysnapping
53
+ coverage: '/^TOTAL.+?(\d+\%)$/'
54
+ artifacts:
55
+ reports:
56
+ coverage_report:
57
+ coverage_format: cobertura
58
+ path: cobertura_coverage.xml
59
+ junit: junit_coverage.xml
60
+
61
+ run_pre_commit_checks:
62
+ stage: test
63
+ needs: []
64
+ script:
65
+ # We have to uv run here since we still have a system mypy hook that needs installed
66
+ # dependencies. Could be fixed by using ty and calling it without installing
67
+ # anything since ty can handle dependencies on its own (mypy and pyright can't do
68
+ # that).
69
+ - uv run pre-commit run --all-files
70
+ except:
71
+ - master
@@ -0,0 +1,43 @@
1
+ fail_fast: true
2
+ repos:
3
+ - repo: https://github.com/pre-commit/pre-commit-hooks
4
+ rev: v6.0.0
5
+ hooks:
6
+ - id: no-commit-to-branch
7
+ - id: check-merge-conflict
8
+ - id: check-symlinks
9
+ - id: mixed-line-ending
10
+ args: ['--fix=no']
11
+ - id: check-ast
12
+ - id: check-builtin-literals
13
+ - id: check-case-conflict
14
+ - id: check-docstring-first
15
+ - repo: https://github.com/astral-sh/ruff-pre-commit
16
+ rev: v0.17.4
17
+ hooks:
18
+ - id: ruff
19
+ args: [ --fix ]
20
+ - id: ruff-format
21
+ - repo: https://github.com/astral-sh/uv-pre-commit
22
+ rev: 0.13.7
23
+ hooks:
24
+ - id: uv-lock
25
+ files: ^pyproject.toml|uv.lock$
26
+ - repo: local
27
+ hooks:
28
+ - id: mypy
29
+ name: mypy
30
+ # gitlab job syncs explicitly without building extensions
31
+ # and for local dev we assume synced
32
+ entry: uv run --no-sync mypy
33
+ # We need installed dependencies for mypy, so we can't use an isolated env. This
34
+ # could be fixed by using ty instead which is capable of resolving dependencies on
35
+ # its own (mypy and pyright can't do that).
36
+ language: system
37
+ # don't pass file names, so we detect errors in B when only A changed but B
38
+ # imports A
39
+ pass_filenames: false
40
+ # always run instead of a python file filter, so we detect errors in B when A gets
41
+ # deleted but B imports A
42
+ always_run: true
43
+ args: ["-p", "pysnapping"]
@@ -0,0 +1 @@
1
+ 3.13
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pysnapping
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: Snap points to a line string keeping a given order or spacing intact
5
5
  Author-email: Alexander Held | geOps <alexander.held@geops.com>
6
6
  Maintainer-email: Alexander Held | geOps <alexander.held@geops.com>
@@ -36,7 +36,7 @@ Classifier: License :: OSI Approved :: MIT License
36
36
  Classifier: Topic :: Scientific/Engineering :: GIS
37
37
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
38
38
  Classifier: Typing :: Typed
39
- Requires-Python: >=3.9
39
+ Requires-Python: >=3.11
40
40
  Description-Content-Type: text/markdown
41
41
  License-File: LICENSE
42
42
  Requires-Dist: shapely
@@ -66,10 +66,10 @@ those that respect the order and minimum spacing between points.
66
66
 
67
67
  ## Installation
68
68
 
69
- Install the latest stable release from PyPI with pip:
69
+ Add the latest stable release from PyPI to your project with uv:
70
70
 
71
71
  ```bash
72
- pip install pysnapping
72
+ uv add pysnapping
73
73
  ```
74
74
 
75
75
  ## Modules
@@ -126,31 +126,25 @@ See [our GitHub page](https://github.com/geops) for contact details.
126
126
 
127
127
  ### Editable Installation
128
128
 
129
- Clone this repo and enter the corresponding directory.
130
- Create a virtual environment, then install frozen requirements, dev-requirements
131
- and this library in editable mode:
129
+ Clone this repo and enter the corresponding directory. Then run:
132
130
 
133
131
  ```bash
134
- python3.9 -m venv env
135
- . env/bin/activate
136
- pip install -U pip
137
- pip install -r requirements.txt -r dev-requirements.txt -e .
132
+ uv sync
138
133
  ```
139
134
 
140
- Keep env activated for all following instructions.
141
135
 
142
136
  ### Pre-Commit Hooks
143
137
 
144
138
  Enable pre-commit hooks:
145
139
 
146
140
  ```bash
147
- pre-commit install
141
+ uv run pre-commit install
148
142
  ```
149
143
 
150
144
  From time to time (not automated yet) run
151
145
 
152
146
  ```bash
153
- pre-commit autoupdate
147
+ uv run pre-commit autoupdate
154
148
  ```
155
149
 
156
150
  to update frozen revs.
@@ -160,7 +154,23 @@ to update frozen revs.
160
154
  Run tests and analyze code coverage:
161
155
 
162
156
  ```bash
163
- pytest --cov=pysnapping --cov-report term --cov-fail-under=85 pysnapping
157
+ uv run pytest --cov=pysnapping --cov-report term --cov-fail-under=85 pysnapping
158
+ ```
159
+
160
+ ### Build release for PyPI
161
+
162
+ Make sure to add a git tag first and push it to geOps gitlab and github.
163
+
164
+ Install `build` once:
165
+
166
+ ```bash
167
+ uv tool install build
168
+ ```
169
+
170
+ Then run
171
+
172
+ ```bash
173
+ pyproject-build
164
174
  ```
165
175
 
166
176
  ## Changelog
@@ -20,10 +20,10 @@ those that respect the order and minimum spacing between points.
20
20
 
21
21
  ## Installation
22
22
 
23
- Install the latest stable release from PyPI with pip:
23
+ Add the latest stable release from PyPI to your project with uv:
24
24
 
25
25
  ```bash
26
- pip install pysnapping
26
+ uv add pysnapping
27
27
  ```
28
28
 
29
29
  ## Modules
@@ -80,31 +80,25 @@ See [our GitHub page](https://github.com/geops) for contact details.
80
80
 
81
81
  ### Editable Installation
82
82
 
83
- Clone this repo and enter the corresponding directory.
84
- Create a virtual environment, then install frozen requirements, dev-requirements
85
- and this library in editable mode:
83
+ Clone this repo and enter the corresponding directory. Then run:
86
84
 
87
85
  ```bash
88
- python3.9 -m venv env
89
- . env/bin/activate
90
- pip install -U pip
91
- pip install -r requirements.txt -r dev-requirements.txt -e .
86
+ uv sync
92
87
  ```
93
88
 
94
- Keep env activated for all following instructions.
95
89
 
96
90
  ### Pre-Commit Hooks
97
91
 
98
92
  Enable pre-commit hooks:
99
93
 
100
94
  ```bash
101
- pre-commit install
95
+ uv run pre-commit install
102
96
  ```
103
97
 
104
98
  From time to time (not automated yet) run
105
99
 
106
100
  ```bash
107
- pre-commit autoupdate
101
+ uv run pre-commit autoupdate
108
102
  ```
109
103
 
110
104
  to update frozen revs.
@@ -114,7 +108,23 @@ to update frozen revs.
114
108
  Run tests and analyze code coverage:
115
109
 
116
110
  ```bash
117
- pytest --cov=pysnapping --cov-report term --cov-fail-under=85 pysnapping
111
+ uv run pytest --cov=pysnapping --cov-report term --cov-fail-under=85 pysnapping
112
+ ```
113
+
114
+ ### Build release for PyPI
115
+
116
+ Make sure to add a git tag first and push it to geOps gitlab and github.
117
+
118
+ Install `build` once:
119
+
120
+ ```bash
121
+ uv tool install build
122
+ ```
123
+
124
+ Then run
125
+
126
+ ```bash
127
+ pyproject-build
118
128
  ```
119
129
 
120
130
  ## Changelog
@@ -7,7 +7,7 @@ dependencies = [
7
7
  "numpy~=2.0", # for vectorized operations
8
8
  "pyproj>=3.2.0", # for defining custom CRS
9
9
  ]
10
- requires-python = ">=3.9"
10
+ requires-python = ">=3.11"
11
11
  license = {file = "LICENSE"}
12
12
  authors = [{name = "Alexander Held | geOps", email = "alexander.held@geops.com"}]
13
13
  maintainers = [{name = "Alexander Held | geOps", email = "alexander.held@geops.com"}]
@@ -34,6 +34,14 @@ classifiers = [
34
34
  ]
35
35
  dynamic = ["version"]
36
36
 
37
+ [dependency-groups]
38
+ dev = [
39
+ "pytest",
40
+ "pytest-cov",
41
+ "pre-commit",
42
+ "mypy",
43
+ ]
44
+
37
45
  [project.urls]
38
46
  homepage = "https://github.com/geops/pysnapping"
39
47
  geOps = "https://geops.com/en"
@@ -47,6 +55,15 @@ build-backend = "setuptools.build_meta"
47
55
  [tool.setuptools.packages.find]
48
56
  include = ["pysnapping", "pysnapping.*"]
49
57
 
58
+ [tool.uv]
59
+ cache-keys = [
60
+ # default
61
+ { file = "pyproject.toml"},
62
+ # for non-editable installs (dynamic setuptools_scm versioning)
63
+ { git = { commit = true } },
64
+ ]
65
+ environments = ["sys_platform == 'linux'"]
66
+
50
67
  [tool.ruff.lint]
51
68
  select = [
52
69
  "F", # pyflakes
@@ -61,10 +78,12 @@ select = [
61
78
  "SLOT", # str, tuple, namedtuple subclasses need __slots__
62
79
  "FIX003", # line contains XXX
63
80
  "NPY201", # numpy 2.0 deprecations
81
+ "SIM", # flake8-simplify
82
+ "I", # sort imports
83
+ "T100", # breakpoints
64
84
  ]
65
85
  ignore = [
66
86
  "E501", # line too long
67
- "UP038", # see https://github.com/astral-sh/ruff/issues/7871
68
87
  ]
69
88
 
70
89
  [tool.ruff.lint.mccabe]
@@ -72,8 +91,13 @@ max-complexity = 12
72
91
 
73
92
  [tool.mypy]
74
93
  ignore_missing_imports = true
75
- install_types = true
76
- non_interactive = true
94
+ # If we allow to install types, mypy will use pip to alter the venv with unpinned extra
95
+ # type stubs. We are on a pure uv setup and don't have pip. Also we want everything
96
+ # pinned. Thus all missing type stubs have to go to dev dependencies.
97
+ install_types = false
98
+ non_interactive = false
99
+
100
+ [tool.pytest]
77
101
 
78
102
  [tool.coverage.run]
79
103
  branch = true
@@ -9,7 +9,6 @@ Consider using np.moveaxis if your data is shaped differently.
9
9
  This module is rather low-level without sanity checks and conversion of array-like to array.
10
10
  """
11
11
 
12
- import typing
13
12
  from math import floor
14
13
 
15
14
  import numpy as np
@@ -318,7 +317,7 @@ class LineFractions:
318
317
  head_fraction: float = 0.0,
319
318
  tail_segment: int = -1,
320
319
  tail_fraction: float = 1.0,
321
- out: typing.Optional[ProjectedPoints] = None,
320
+ out: ProjectedPoints | None = None,
322
321
  ) -> ProjectedPoints:
323
322
  """Project points to the linestring or a substring thereof.
324
323
 
@@ -372,8 +371,8 @@ class LineFractions:
372
371
  segment_starts = self.target.segment_starts[np.newaxis, segment_slice, :]
373
372
  del self # protection against using unsliced data
374
373
 
375
- low: typing.Union[float, np.ndarray]
376
- high: typing.Union[float, np.ndarray]
374
+ low: float | np.ndarray
375
+ high: float | np.ndarray
377
376
  # different cases only for speeding up the default
378
377
  if head_fraction != 0.0:
379
378
  low = np.zeros((1, fractions.shape[1], 1))
@@ -430,7 +429,7 @@ class LineFractions:
430
429
  d_to: float,
431
430
  distances: np.ndarray,
432
431
  extrapolate: bool = False,
433
- out: typing.Optional[ProjectedPoints] = None,
432
+ out: ProjectedPoints | None = None,
434
433
  ) -> ProjectedPoints:
435
434
  """Convenience method to project between two distances.
436
435
 
@@ -1,40 +1,39 @@
1
+ import logging
1
2
  import typing
2
- from functools import partial
3
3
  from bisect import bisect_right
4
+ from functools import partial
4
5
  from operator import itemgetter
5
- import logging
6
6
 
7
- import pyproj
8
7
  import numpy as np
8
+ import pyproj
9
9
  from numpy.typing import ArrayLike
10
10
 
11
+ from . import (
12
+ EPSG4326,
13
+ EPSG4978,
14
+ BadShortestDistances,
15
+ ExtrapolationError,
16
+ SnappingError,
17
+ SnappingMethod,
18
+ )
11
19
  from .linear_referencing import (
20
+ LineFractions,
12
21
  Locations,
13
- substrings,
14
- locate,
15
- interpolate,
16
- ProjectionTarget,
17
22
  ProjectedPoints,
18
- LineFractions,
23
+ ProjectionTarget,
24
+ interpolate,
25
+ locate,
19
26
  resample,
27
+ substrings,
20
28
  )
21
29
  from .util import (
22
- get_trafo,
23
- transform_coords,
24
30
  array_chk,
25
- simplify_2d_keep_rest,
26
31
  cumulative_min_and_argmin,
27
- )
28
- from . import (
29
- SnappingError,
30
- BadShortestDistances,
31
- ExtrapolationError,
32
- EPSG4326,
33
- EPSG4978,
34
- SnappingMethod,
32
+ get_trafo,
33
+ simplify_2d_keep_rest,
34
+ transform_coords,
35
35
  )
36
36
 
37
-
38
37
  logger = logging.getLogger(__name__)
39
38
 
40
39
 
@@ -207,7 +206,7 @@ class DubiousTrajectory(XYZDMixin):
207
206
  xyzd: ArrayLike,
208
207
  crs: pyproj.CRS = EPSG4326,
209
208
  strict_axis_order: bool = False,
210
- simplify_tolerance: typing.Optional[float] = None,
209
+ simplify_tolerance: float | None = None,
211
210
  ) -> None:
212
211
  """Initialize DubiousTrajectory.
213
212
 
@@ -223,9 +222,7 @@ class DubiousTrajectory(XYZDMixin):
223
222
  raise ValueError("coords have to be finite")
224
223
 
225
224
  @classmethod
226
- def from_xyd_and_z(
227
- cls, xyd: ArrayLike, z: typing.Union[float, ArrayLike] = 0.0, **kwargs
228
- ):
225
+ def from_xyd_and_z(cls, xyd: ArrayLike, z: float | ArrayLike = 0.0, **kwargs):
229
226
  xyd_arr = np.asarray(xyd, dtype=float)
230
227
  xyzd = np.empty((xyd_arr.shape[0], 4))
231
228
  xyzd[:, :2] = xyd_arr[:, :2]
@@ -329,7 +326,7 @@ class Trajectory(XYZDMixin):
329
326
  __slots__ = ("_target",)
330
327
 
331
328
  # lazy data calculated when first needed
332
- _target: typing.Optional[ProjectionTarget]
329
+ _target: ProjectionTarget | None
333
330
 
334
331
  def __init__(self, xyz: ArrayLike) -> None:
335
332
  xyz = array_chk(xyz, ((2, None), 3), chk_finite=True, dtype=float, copy=False)
@@ -410,8 +407,8 @@ class TrajectoryTrip(XYZDMixin):
410
407
  trajectory: Trajectory
411
408
 
412
409
  # lazy data calculated when first needed
413
- _line_fractions: typing.Optional[LineFractions]
414
- _projected_points: typing.Optional[ProjectedPoints]
410
+ _line_fractions: LineFractions | None
411
+ _projected_points: ProjectedPoints | None
415
412
 
416
413
  def __init__(self, trajectory: Trajectory, xyzd: ArrayLike) -> None:
417
414
  self.trajectory = trajectory
@@ -490,7 +487,7 @@ class TrajectoryTrip(XYZDMixin):
490
487
  # list of (ppoints, cost, reverse_order) tuples for forward and backward
491
488
  # solution
492
489
  results: list[tuple[ProjectedPoints, float, bool]] = []
493
- forward_error: typing.Optional[SnappingError] = None
490
+ forward_error: SnappingError | None = None
494
491
  try:
495
492
  forward_result = self.route_candidates(params, ppoints_list) + (False,)
496
493
  except SnappingError as error:
@@ -531,9 +528,7 @@ class TrajectoryTrip(XYZDMixin):
531
528
  reverse_order=reverse_order,
532
529
  )
533
530
 
534
- def snap_trusted(
535
- self, params: SnappingParams
536
- ) -> list[typing.Optional[ProjectedPoints]]:
531
+ def snap_trusted(self, params: SnappingParams) -> list[ProjectedPoints | None]:
537
532
  """Determine which points are trusted and snap them to the trajectory.
538
533
 
539
534
  See `SnappingParams` for an explanation of trusted/untrusted. The list contains
@@ -546,7 +541,7 @@ class TrajectoryTrip(XYZDMixin):
546
541
  d_max=self.trajectory.d_max,
547
542
  consider_sampling_accuracy=True,
548
543
  )
549
- ppoints_list: list[typing.Optional[ProjectedPoints]] = [None] * len(self)
544
+ ppoints_list: list[ProjectedPoints | None] = [None] * len(self)
550
545
  if traj_spacing_ok(self.dists):
551
546
  trusted_mask = np.logical_not(np.isnan(self.dists))
552
547
  n_non_nan = trusted_mask.sum()
@@ -606,7 +601,7 @@ class TrajectoryTrip(XYZDMixin):
606
601
  def set_untrusted_candidates_inplace(
607
602
  self,
608
603
  params: SnappingParams,
609
- ppoints_list: list[typing.Optional[ProjectedPoints]],
604
+ ppoints_list: list[ProjectedPoints | None],
610
605
  ) -> list[ProjectedPoints]:
611
606
  """Set untrusted snapping candidates inplace.
612
607
 
@@ -707,7 +702,7 @@ class TrajectoryTrip(XYZDMixin):
707
702
  reachable_costs = [[0.0]]
708
703
  # index into the lists from the previous layer for every entry in the current
709
704
  # layer for shortest path reconstruction
710
- reachable_predecessors: list[list[typing.Optional[int]]] = [[None]]
705
+ reachable_predecessors: list[list[int | None]] = [[None]]
711
706
 
712
707
  # artificial end layer connected to all reachable candidates of the real last
713
708
  # layer
@@ -767,7 +762,7 @@ class TrajectoryTrip(XYZDMixin):
767
762
  self,
768
763
  ppoints_list: list[ProjectedPoints],
769
764
  reachable_candidates: list[list[int]],
770
- reachable_predecessors: list[list[typing.Optional[int]]],
765
+ reachable_predecessors: list[list[int | None]],
771
766
  ) -> ProjectedPoints:
772
767
  winner_indices: list[int] = [-1] * len(ppoints_list)
773
768
  predecessor = 0
@@ -799,7 +794,7 @@ class SnappedTripPoints:
799
794
  snapped_points: ProjectedPoints,
800
795
  methods: ArrayLike,
801
796
  reverse_order: bool,
802
- distances: typing.Optional[np.ndarray] = None,
797
+ distances: np.ndarray | None = None,
803
798
  ):
804
799
  if len(snapped_points) != len(trip):
805
800
  raise ValueError("wrong number of snapped points for trip")
@@ -1,24 +1,24 @@
1
+ import json
2
+ import os
1
3
  import typing
4
+ from functools import partial
5
+ from itertools import product
6
+
2
7
  import numpy as np
3
- from numpy.testing import assert_allclose
4
- import pytest
5
8
  import pyproj
6
- from itertools import product
7
- from functools import partial
8
- import os
9
- import json
9
+ import pytest
10
+ from numpy.testing import assert_allclose
10
11
 
12
+ from pysnapping import EPSG4326, EPSG4978
13
+ from pysnapping.linear_referencing import interpolate, locate, resample
11
14
  from pysnapping.snap import (
12
15
  DubiousTrajectory,
13
16
  DubiousTrajectoryTrip,
14
17
  SnappingMethod,
15
18
  SnappingParams,
16
19
  )
17
- from pysnapping.linear_referencing import locate, interpolate, resample
18
20
  from pysnapping.stats import SnappingStats
19
21
  from pysnapping.util import get_trafo, transform_coords
20
- from pysnapping import EPSG4326, EPSG4978
21
-
22
22
 
23
23
  if typing.TYPE_CHECKING:
24
24
  from numpy.typing import ArrayLike
@@ -1,7 +1,7 @@
1
1
  import numpy as np
2
2
  from numpy.testing import assert_equal
3
3
 
4
- from pysnapping.util import simplify_2d_keep_rest, cumulative_min_and_argmin
4
+ from pysnapping.util import cumulative_min_and_argmin, simplify_2d_keep_rest
5
5
 
6
6
 
7
7
  def test_simplify() -> None:
@@ -1,15 +1,14 @@
1
- import typing
2
1
  import logging
2
+ import typing
3
3
  from functools import lru_cache
4
4
 
5
5
  import numpy as np
6
+ import pyproj
6
7
  from numpy.typing import ArrayLike
7
8
  from shapely.geometry import LineString
8
- import pyproj
9
9
 
10
10
  from . import EPSG4978
11
11
 
12
-
13
12
  logger = logging.getLogger(__name__)
14
13
 
15
14
 
@@ -33,7 +32,7 @@ def transform_coords(
33
32
  [np.ndarray, np.ndarray, np.ndarray],
34
33
  tuple[np.ndarray, np.ndarray, np.ndarray],
35
34
  ],
36
- out: typing.Optional[np.ndarray] = None,
35
+ out: np.ndarray | None = None,
37
36
  skip_z_output: bool = False,
38
37
  ) -> np.ndarray:
39
38
  coords_arr = np.asarray(coords, dtype=float)
@@ -86,7 +85,7 @@ def simplify_2d_keep_rest(coords: ArrayLike, tolerance) -> np.ndarray:
86
85
  def array_chk(
87
86
  data: ArrayLike,
88
87
  shape_template: tuple[
89
- typing.Union[None, int, tuple[typing.Optional[int], typing.Optional[int]]],
88
+ None | int | tuple[int | None, int | None],
90
89
  ...,
91
90
  ],
92
91
  chk_finite: bool = False,
@@ -112,11 +111,10 @@ def array_chk(
112
111
  raise ValueError(
113
112
  f"axis {i} has wrong length (expected at least {n_min}, got {n})"
114
113
  )
115
- elif n_max is not None:
116
- if n > n_max:
117
- raise ValueError(
118
- f"axis {i} has wrong length (expected at most {n_max}, got {n})"
119
- )
114
+ elif n_max is not None and n > n_max:
115
+ raise ValueError(
116
+ f"axis {i} has wrong length (expected at most {n_max}, got {n})"
117
+ )
120
118
  if chk_finite and not np.all(np.isfinite(arr)):
121
119
  raise ValueError("input not finite")
122
120
  return arr