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.
- {pysnapping-0.2.3 → pysnapping-0.2.4}/.gitignore +0 -4
- 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.3 → pysnapping-0.2.4}/PKG-INFO +25 -15
- {pysnapping-0.2.3 → pysnapping-0.2.4}/README.md +23 -13
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pyproject.toml +28 -4
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/linear_referencing.py +4 -5
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/snap.py +31 -36
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/test/test_snap.py +9 -9
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/test/test_util.py +1 -1
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/util.py +8 -10
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping.egg-info/PKG-INFO +25 -15
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping.egg-info/SOURCES.txt +2 -3
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping.egg-info/scm_file_list.json +12 -13
- pysnapping-0.2.4/pysnapping.egg-info/scm_version.json +8 -0
- pysnapping-0.2.4/uv.lock +721 -0
- pysnapping-0.2.3/.gitlab-ci.yml +0 -66
- pysnapping-0.2.3/.pre-commit-config.yaml +0 -38
- pysnapping-0.2.3/dev-requirements.in +0 -7
- pysnapping-0.2.3/dev-requirements.txt +0 -76
- pysnapping-0.2.3/pysnapping.egg-info/scm_version.json +0 -8
- pysnapping-0.2.3/requirements.txt +0 -12
- {pysnapping-0.2.3 → pysnapping-0.2.4}/.editorconfig +0 -0
- {pysnapping-0.2.3 → pysnapping-0.2.4}/LICENSE +0 -0
- {pysnapping-0.2.3 → pysnapping-0.2.4}/MANIFEST.in +0 -0
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/__init__.py +1 -1
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/py.typed +0 -0
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/stats.py +3 -3
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/test/__init__.py +0 -0
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/test/complex_trip.geojson +0 -0
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/test/conftest.py +1 -1
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping/test/test_linear_referencing.py +5 -5
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping.egg-info/dependency_links.txt +0 -0
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping.egg-info/requires.txt +0 -0
- {pysnapping-0.2.3 → pysnapping-0.2.4}/pysnapping.egg-info/top_level.txt +0 -0
- {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
|
+
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.
|
|
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
|
-
|
|
69
|
+
Add the latest stable release from PyPI to your project with uv:
|
|
70
70
|
|
|
71
71
|
```bash
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -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.
|
|
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
|
-
|
|
76
|
-
|
|
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:
|
|
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:
|
|
376
|
-
high:
|
|
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:
|
|
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
|
-
|
|
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()
|
|
@@ -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")
|
|
@@ -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
|
-
|
|
7
|
-
from
|
|
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,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:
|
|
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
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|