pysnapping 0.2.2__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.
- {pysnapping-0.2.2 → pysnapping-0.2.4}/.gitignore +0 -3
- pysnapping-0.2.4/.gitlab-ci.yml +71 -0
- pysnapping-0.2.4/.pre-commit-config.yaml +43 -0
- pysnapping-0.2.4/.python-version +1 -0
- {pysnapping-0.2.2 → pysnapping-0.2.4}/PKG-INFO +28 -17
- {pysnapping-0.2.2 → pysnapping-0.2.4}/README.md +23 -13
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pyproject.toml +30 -8
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping/linear_referencing.py +10 -14
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping/snap.py +32 -37
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping/test/test_snap.py +19 -19
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping/test/test_util.py +1 -1
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping/util.py +8 -10
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping.egg-info/PKG-INFO +28 -17
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping.egg-info/SOURCES.txt +4 -3
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping.egg-info/requires.txt +1 -1
- pysnapping-0.2.4/pysnapping.egg-info/scm_file_list.json +26 -0
- pysnapping-0.2.4/pysnapping.egg-info/scm_version.json +8 -0
- pysnapping-0.2.4/uv.lock +721 -0
- pysnapping-0.2.2/.gitlab-ci.yml +0 -66
- pysnapping-0.2.2/.pre-commit-config.yaml +0 -38
- pysnapping-0.2.2/dev-requirements.in +0 -7
- pysnapping-0.2.2/dev-requirements.txt +0 -76
- pysnapping-0.2.2/requirements.txt +0 -12
- {pysnapping-0.2.2 → pysnapping-0.2.4}/.editorconfig +0 -0
- {pysnapping-0.2.2 → pysnapping-0.2.4}/LICENSE +0 -0
- {pysnapping-0.2.2 → pysnapping-0.2.4}/MANIFEST.in +0 -0
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping/__init__.py +1 -1
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping/py.typed +0 -0
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping/stats.py +3 -3
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping/test/__init__.py +0 -0
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping/test/complex_trip.geojson +0 -0
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping/test/conftest.py +1 -1
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping/test/test_linear_referencing.py +5 -5
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping.egg-info/dependency_links.txt +0 -0
- {pysnapping-0.2.2 → pysnapping-0.2.4}/pysnapping.egg-info/top_level.txt +0 -0
- {pysnapping-0.2.2 → pysnapping-0.2.4}/setup.cfg +0 -0
|
@@ -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
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pysnapping
|
|
3
|
-
Version: 0.2.
|
|
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,12 +36,13 @@ 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.
|
|
39
|
+
Requires-Python: >=3.11
|
|
40
40
|
Description-Content-Type: text/markdown
|
|
41
41
|
License-File: LICENSE
|
|
42
42
|
Requires-Dist: shapely
|
|
43
|
-
Requires-Dist: numpy
|
|
43
|
+
Requires-Dist: numpy~=2.0
|
|
44
44
|
Requires-Dist: pyproj>=3.2.0
|
|
45
|
+
Dynamic: license-file
|
|
45
46
|
|
|
46
47
|
# PySnapping
|
|
47
48
|
|
|
@@ -65,10 +66,10 @@ those that respect the order and minimum spacing between points.
|
|
|
65
66
|
|
|
66
67
|
## Installation
|
|
67
68
|
|
|
68
|
-
|
|
69
|
+
Add the latest stable release from PyPI to your project with uv:
|
|
69
70
|
|
|
70
71
|
```bash
|
|
71
|
-
|
|
72
|
+
uv add pysnapping
|
|
72
73
|
```
|
|
73
74
|
|
|
74
75
|
## Modules
|
|
@@ -125,31 +126,25 @@ See [our GitHub page](https://github.com/geops) for contact details.
|
|
|
125
126
|
|
|
126
127
|
### Editable Installation
|
|
127
128
|
|
|
128
|
-
Clone this repo and enter the corresponding directory.
|
|
129
|
-
Create a virtual environment, then install frozen requirements, dev-requirements
|
|
130
|
-
and this library in editable mode:
|
|
129
|
+
Clone this repo and enter the corresponding directory. Then run:
|
|
131
130
|
|
|
132
131
|
```bash
|
|
133
|
-
|
|
134
|
-
. env/bin/activate
|
|
135
|
-
pip install -U pip
|
|
136
|
-
pip install -r requirements.txt -r dev-requirements.txt -e .
|
|
132
|
+
uv sync
|
|
137
133
|
```
|
|
138
134
|
|
|
139
|
-
Keep env activated for all following instructions.
|
|
140
135
|
|
|
141
136
|
### Pre-Commit Hooks
|
|
142
137
|
|
|
143
138
|
Enable pre-commit hooks:
|
|
144
139
|
|
|
145
140
|
```bash
|
|
146
|
-
pre-commit install
|
|
141
|
+
uv run pre-commit install
|
|
147
142
|
```
|
|
148
143
|
|
|
149
144
|
From time to time (not automated yet) run
|
|
150
145
|
|
|
151
146
|
```bash
|
|
152
|
-
pre-commit autoupdate
|
|
147
|
+
uv run pre-commit autoupdate
|
|
153
148
|
```
|
|
154
149
|
|
|
155
150
|
to update frozen revs.
|
|
@@ -159,7 +154,23 @@ to update frozen revs.
|
|
|
159
154
|
Run tests and analyze code coverage:
|
|
160
155
|
|
|
161
156
|
```bash
|
|
162
|
-
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
|
|
163
174
|
```
|
|
164
175
|
|
|
165
176
|
## Changelog
|
|
@@ -20,10 +20,10 @@ those that respect the order and minimum spacing between points.
|
|
|
20
20
|
|
|
21
21
|
## Installation
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Add the latest stable release from PyPI to your project with uv:
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
|
|
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
|
-
|
|
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
|
|
@@ -4,10 +4,10 @@ description = "Snap points to a line string keeping a given order or spacing int
|
|
|
4
4
|
readme = "README.md"
|
|
5
5
|
dependencies = [
|
|
6
6
|
"shapely",
|
|
7
|
-
"numpy
|
|
7
|
+
"numpy~=2.0", # for vectorized operations
|
|
8
8
|
"pyproj>=3.2.0", # for defining custom CRS
|
|
9
9
|
]
|
|
10
|
-
requires-python = ">=3.
|
|
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
|
|
@@ -60,10 +77,13 @@ select = [
|
|
|
60
77
|
"PIE", # unnecessary statements
|
|
61
78
|
"SLOT", # str, tuple, namedtuple subclasses need __slots__
|
|
62
79
|
"FIX003", # line contains XXX
|
|
80
|
+
"NPY201", # numpy 2.0 deprecations
|
|
81
|
+
"SIM", # flake8-simplify
|
|
82
|
+
"I", # sort imports
|
|
83
|
+
"T100", # breakpoints
|
|
63
84
|
]
|
|
64
85
|
ignore = [
|
|
65
86
|
"E501", # line too long
|
|
66
|
-
"UP038", # see https://github.com/astral-sh/ruff/issues/7871
|
|
67
87
|
]
|
|
68
88
|
|
|
69
89
|
[tool.ruff.lint.mccabe]
|
|
@@ -71,11 +91,13 @@ max-complexity = 12
|
|
|
71
91
|
|
|
72
92
|
[tool.mypy]
|
|
73
93
|
ignore_missing_imports = true
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
install_types =
|
|
78
|
-
non_interactive =
|
|
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]
|
|
79
101
|
|
|
80
102
|
[tool.coverage.run]
|
|
81
103
|
branch = true
|
|
@@ -9,14 +9,10 @@ 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
|
|
16
15
|
|
|
17
|
-
# faster than np.clip
|
|
18
|
-
from numpy.core.umath import clip # type: ignore
|
|
19
|
-
|
|
20
16
|
from . import ExtrapolationError
|
|
21
17
|
|
|
22
18
|
|
|
@@ -321,7 +317,7 @@ class LineFractions:
|
|
|
321
317
|
head_fraction: float = 0.0,
|
|
322
318
|
tail_segment: int = -1,
|
|
323
319
|
tail_fraction: float = 1.0,
|
|
324
|
-
out:
|
|
320
|
+
out: ProjectedPoints | None = None,
|
|
325
321
|
) -> ProjectedPoints:
|
|
326
322
|
"""Project points to the linestring or a substring thereof.
|
|
327
323
|
|
|
@@ -375,8 +371,8 @@ class LineFractions:
|
|
|
375
371
|
segment_starts = self.target.segment_starts[np.newaxis, segment_slice, :]
|
|
376
372
|
del self # protection against using unsliced data
|
|
377
373
|
|
|
378
|
-
low:
|
|
379
|
-
high:
|
|
374
|
+
low: float | np.ndarray
|
|
375
|
+
high: float | np.ndarray
|
|
380
376
|
# different cases only for speeding up the default
|
|
381
377
|
if head_fraction != 0.0:
|
|
382
378
|
low = np.zeros((1, fractions.shape[1], 1))
|
|
@@ -389,7 +385,7 @@ class LineFractions:
|
|
|
389
385
|
else:
|
|
390
386
|
high = 1.0
|
|
391
387
|
|
|
392
|
-
fractions = clip(fractions, low, high)
|
|
388
|
+
fractions = np.clip(fractions, low, high)
|
|
393
389
|
|
|
394
390
|
projected_points = fractions * segment_dirs
|
|
395
391
|
projected_points += segment_starts
|
|
@@ -433,7 +429,7 @@ class LineFractions:
|
|
|
433
429
|
d_to: float,
|
|
434
430
|
distances: np.ndarray,
|
|
435
431
|
extrapolate: bool = False,
|
|
436
|
-
out:
|
|
432
|
+
out: ProjectedPoints | None = None,
|
|
437
433
|
) -> ProjectedPoints:
|
|
438
434
|
"""Convenience method to project between two distances.
|
|
439
435
|
|
|
@@ -527,9 +523,9 @@ class LineFractions:
|
|
|
527
523
|
i,
|
|
528
524
|
segment_indices[keep],
|
|
529
525
|
# clip to segments
|
|
530
|
-
clip(min_fractions[keep], 0, 1),
|
|
531
|
-
clip(closest_fractions[keep], 0, 1),
|
|
532
|
-
clip(max_fractions[keep], 0, 1),
|
|
526
|
+
np.clip(min_fractions[keep], 0, 1),
|
|
527
|
+
np.clip(closest_fractions[keep], 0, 1),
|
|
528
|
+
np.clip(max_fractions[keep], 0, 1),
|
|
533
529
|
)
|
|
534
530
|
ball_slices.append(slfs)
|
|
535
531
|
return ball_slices
|
|
@@ -726,14 +722,14 @@ def substrings(
|
|
|
726
722
|
frac_start_ceil = np.ceil(frac_start)
|
|
727
723
|
first = frac_start_ceil.astype(int)
|
|
728
724
|
first[frac_start == frac_start_ceil] += 1
|
|
729
|
-
clip(first, starts.from_vertices, starts.to_vertices + 1, out=first)
|
|
725
|
+
np.clip(first, starts.from_vertices, starts.to_vertices + 1, out=first)
|
|
730
726
|
|
|
731
727
|
# the last indices that are not convered by end interpolation/extrapolation
|
|
732
728
|
frac_end = ends.get_fractional_indices()
|
|
733
729
|
frac_end_floor = np.floor(frac_end)
|
|
734
730
|
last = frac_end_floor.astype(int)
|
|
735
731
|
last[frac_end == frac_end_floor] -= 1
|
|
736
|
-
clip(last, ends.from_vertices - 1, ends.to_vertices, out=last)
|
|
732
|
+
np.clip(last, ends.from_vertices - 1, ends.to_vertices, out=last)
|
|
737
733
|
|
|
738
734
|
data_length = len(data)
|
|
739
735
|
start_data = interpolate(data, starts)
|
|
@@ -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
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
414
|
-
_projected_points:
|
|
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:
|
|
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[
|
|
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()
|
|
@@ -593,7 +588,7 @@ class TrajectoryTrip(XYZDMixin):
|
|
|
593
588
|
)
|
|
594
589
|
# if the spacing was not ok, it can still be bad after untrusting all points
|
|
595
590
|
# since the total length can be too short for the points
|
|
596
|
-
untrusted_dists = np.full_like(self.dists, np.
|
|
591
|
+
untrusted_dists = np.full_like(self.dists, np.nan)
|
|
597
592
|
if not traj_spacing_ok(untrusted_dists):
|
|
598
593
|
raise SnappingError(
|
|
599
594
|
f"trajectory of length {self.trajectory.length:g} is too short "
|
|
@@ -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[
|
|
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[
|
|
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[
|
|
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:
|
|
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")
|