plexus-python-common 1.0.62__tar.gz → 1.0.64__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.
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/PKG-INFO +1 -1
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/carto/OSMFile.py +1 -1
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/carto/OSMNode.py +1 -1
- plexus_python_common-1.0.62/src/plexus/common/proj.py → plexus_python_common-1.0.64/src/plexus/common/utils/gisutils.py +102 -1
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/tagutils.py +53 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus_python_common.egg-info/PKG-INFO +1 -1
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus_python_common.egg-info/SOURCES.txt +2 -4
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/common/carto/osm_file_test.py +2 -2
- plexus_python_common-1.0.62/test/plexus_tests/common/proj_test.py → plexus_python_common-1.0.64/test/plexus_tests/common/utils/gisutils_test.py +64 -2
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/common/utils/tagutils_test.py +175 -0
- plexus_python_common-1.0.62/src/plexus/common/pose.py +0 -107
- plexus_python_common-1.0.62/test/plexus_tests/common/pose_test.py +0 -66
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/.editorconfig +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/.github/workflows/pr.yml +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/.github/workflows/push.yml +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/.gitignore +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/MANIFEST.in +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/README.md +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/VERSION +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/pyproject.toml +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/jsonutils/dummy.0.jsonl +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/jsonutils/dummy.1.jsonl +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/jsonutils/dummy.2.jsonl +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/0-dummy +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/1-dummy +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/2-dummy +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/dummy.0.0.jsonl +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/dummy.0.0.vol-0.jsonl +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/dummy.0.jsonl +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/dummy.1.1.jsonl +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/dummy.1.1.vol-1.jsonl +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/dummy.1.jsonl +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/dummy.2.2.jsonl +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/dummy.2.2.vol-2.jsonl +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/dummy.2.jsonl +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/dummy.csv.part0 +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/dummy.csv.part1 +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/dummy.csv.part2 +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/dummy.txt +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/s3utils/dir.baz/file.bar.baz +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/s3utils/dir.baz/file.foo.bar +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/s3utils/dir.baz/file.foo.baz +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/s3utils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.bar.baz +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.bar +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.baz +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/s3utils/dir.foo/file.bar +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/s3utils/dir.foo/file.baz +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/s3utils/dir.foo/file.foo +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/s3utils_archive/archive.compressed.zip +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/s3utils_archive/archive.uncompressed.zip +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/setup.cfg +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/setup.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/__init__.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/carto/OSMTags.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/carto/OSMWay.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/carto/__init__.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/resources/__init__.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/resources/tags/__init__.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/resources/tags/unittest-1.0.0.tagset.yaml +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/resources/tags/universal-1.0.0.tagset.yaml +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/__init__.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/apiutils.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/bagutils.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/config.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/datautils.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/dockerutils.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/jsonutils.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/ormutils.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/pathutils.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/s3utils.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/sqlutils.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/strutils.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/testutils.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus_python_common.egg-info/dependency_links.txt +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus_python_common.egg-info/not-zip-safe +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus_python_common.egg-info/requires.txt +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus_python_common.egg-info/top_level.txt +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/__init__.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/common/__init__.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/common/carto/__init__.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/common/carto/osm_tags_test.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/common/utils/__init__.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/common/utils/bagutils_test.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/common/utils/datautils_test.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/common/utils/dockerutils_test.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/common/utils/jsonutils_test.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/common/utils/ormutils_test.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/common/utils/pathutils_test.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/common/utils/s3utils_test.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/common/utils/strutils_test.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/common/utils/testutils_test.py +0 -0
- {plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/testenv.py +0 -0
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/carto/OSMFile.py
RENAMED
|
@@ -5,7 +5,7 @@ import lxml.etree
|
|
|
5
5
|
from plexus.common.carto.OSMNode import OSMNode
|
|
6
6
|
from plexus.common.carto.OSMTags import OSMTags
|
|
7
7
|
from plexus.common.carto.OSMWay import OSMWay
|
|
8
|
-
from plexus.common.
|
|
8
|
+
from plexus.common.utils.gisutils import Coord, Proj
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class OSMFile(object):
|
|
@@ -5,10 +5,111 @@ from typing import Annotated, Self
|
|
|
5
5
|
import numpy as np
|
|
6
6
|
import pydantic as pdt
|
|
7
7
|
import pyproj
|
|
8
|
+
import pyquaternion as pyquat
|
|
8
9
|
from iker.common.utils.funcutils import singleton
|
|
9
10
|
from iker.common.utils.strutils import parse_params_string, repr_data, str_conv
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
|
|
13
|
+
class Pose(object):
|
|
14
|
+
@classmethod
|
|
15
|
+
def from_numbers(
|
|
16
|
+
cls,
|
|
17
|
+
px: float,
|
|
18
|
+
py: float,
|
|
19
|
+
pz: float,
|
|
20
|
+
qx: float,
|
|
21
|
+
qy: float,
|
|
22
|
+
qz: float,
|
|
23
|
+
qw: float,
|
|
24
|
+
ts: float = 0,
|
|
25
|
+
) -> Self:
|
|
26
|
+
"""
|
|
27
|
+
Constructs a pose from numbers representing position and orientation
|
|
28
|
+
"""
|
|
29
|
+
return Pose(ts, np.array([px, py, pz], dtype=np.float64), np.array([qw, qx, qy, qz], dtype=np.float64))
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def add(cls, x: Self, d: Self) -> Self:
|
|
33
|
+
"""
|
|
34
|
+
Performs pose SE3 addition, as x + d = y
|
|
35
|
+
"""
|
|
36
|
+
xq = pyquat.Quaternion(x.q)
|
|
37
|
+
dq = pyquat.Quaternion(d.q)
|
|
38
|
+
|
|
39
|
+
yp = x.p + xq.rotate(d.p)
|
|
40
|
+
yq = xq * dq
|
|
41
|
+
|
|
42
|
+
return Pose(0, yp, yq.normalised.elements)
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def sub(cls, y: Self, x: Self) -> Self:
|
|
46
|
+
"""
|
|
47
|
+
Performs pose SE3 subtraction, as x + d = y => d = y - x
|
|
48
|
+
"""
|
|
49
|
+
xq = pyquat.Quaternion(x.q)
|
|
50
|
+
yq = pyquat.Quaternion(y.q)
|
|
51
|
+
|
|
52
|
+
dp = xq.inverse.rotate(y.p - x.p)
|
|
53
|
+
dq = xq.inverse * yq
|
|
54
|
+
|
|
55
|
+
return Pose(0, dp, dq.normalised.elements)
|
|
56
|
+
|
|
57
|
+
@classmethod
|
|
58
|
+
def interpolate(cls, a: Self, b: Self, t: float) -> Self:
|
|
59
|
+
"""
|
|
60
|
+
Interpolates between two given poses, as a * t + b * (1 - t)
|
|
61
|
+
|
|
62
|
+
:return: interpolated pose
|
|
63
|
+
"""
|
|
64
|
+
ts = a.ts + (b.ts - a.ts) * t
|
|
65
|
+
p = a.p + (b.p - a.p) * t
|
|
66
|
+
q = pyquat.Quaternion.slerp(pyquat.Quaternion(a.q), pyquat.Quaternion(b.q), t)
|
|
67
|
+
return Pose(ts, p, q.normalised.elements)
|
|
68
|
+
|
|
69
|
+
def __init__(self, ts: float, p: np.ndarray, q: np.ndarray):
|
|
70
|
+
"""
|
|
71
|
+
Represents a pose
|
|
72
|
+
|
|
73
|
+
:param ts: timestamp
|
|
74
|
+
:param p: position vector
|
|
75
|
+
:param q: orientation quaternion
|
|
76
|
+
"""
|
|
77
|
+
self.ts = ts
|
|
78
|
+
self.p = p
|
|
79
|
+
self.q = q
|
|
80
|
+
|
|
81
|
+
def matrix(self) -> np.ndarray:
|
|
82
|
+
"""
|
|
83
|
+
Returns the transformation matrix of this pose
|
|
84
|
+
|
|
85
|
+
:return: transformation matrix
|
|
86
|
+
"""
|
|
87
|
+
r = pyquat.Quaternion(self.q).rotation_matrix
|
|
88
|
+
t = self.p[:, None]
|
|
89
|
+
return np.block([[r, t], [np.zeros((1, 3), dtype=np.float64), np.ones((1, 1), dtype=np.float64)]])
|
|
90
|
+
|
|
91
|
+
def translate(self, v: np.ndarray) -> np.ndarray:
|
|
92
|
+
"""
|
|
93
|
+
Translate the given vector with the pose position
|
|
94
|
+
|
|
95
|
+
:param v: the vector to be translated
|
|
96
|
+
|
|
97
|
+
:return: translated vector
|
|
98
|
+
"""
|
|
99
|
+
return self.p + v
|
|
100
|
+
|
|
101
|
+
def rotate(self, v: np.ndarray) -> np.ndarray:
|
|
102
|
+
"""
|
|
103
|
+
Rotates the given vector with the pose orientation
|
|
104
|
+
|
|
105
|
+
:param v: the vector to be rotated
|
|
106
|
+
|
|
107
|
+
:return: rotated vector
|
|
108
|
+
"""
|
|
109
|
+
return pyquat.Quaternion(self.q).rotate(v)
|
|
110
|
+
|
|
111
|
+
def __str__(self):
|
|
112
|
+
return repr_data(self)
|
|
12
113
|
|
|
13
114
|
|
|
14
115
|
class Proj(ABC):
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/tagutils.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
import dataclasses
|
|
3
3
|
import datetime
|
|
4
|
+
import math
|
|
4
5
|
import os
|
|
5
6
|
import pathlib
|
|
6
7
|
import textwrap
|
|
@@ -16,6 +17,7 @@ import sqlalchemy as sa
|
|
|
16
17
|
import sqlalchemy.dialects.sqlite as sa_sqlite
|
|
17
18
|
import sqlalchemy.orm as sa_orm
|
|
18
19
|
from iker.common.utils.dbutils import ConnectionMaker
|
|
20
|
+
from iker.common.utils.dtutils import dt_from_ts_us, dt_to_ts_us
|
|
19
21
|
from iker.common.utils.funcutils import memorized, singleton
|
|
20
22
|
from iker.common.utils.iterutils import batched, head_or_none
|
|
21
23
|
from iker.common.utils.iterutils import dicttree
|
|
@@ -48,6 +50,8 @@ __all__ = [
|
|
|
48
50
|
"tag_cache_file_path",
|
|
49
51
|
"TagCache",
|
|
50
52
|
"tag_cache",
|
|
53
|
+
"standard_clip_duration_us",
|
|
54
|
+
"populate_clip_ranges",
|
|
51
55
|
]
|
|
52
56
|
|
|
53
57
|
|
|
@@ -1093,3 +1097,52 @@ def tag_cache(*, identifier: str | None = None, file_path: str | None = None) ->
|
|
|
1093
1097
|
if file_path is not None:
|
|
1094
1098
|
return TagCache(file_path=file_path)
|
|
1095
1099
|
return TagCache(file_path=tag_cache_file_path())
|
|
1100
|
+
|
|
1101
|
+
|
|
1102
|
+
|
|
1103
|
+
@singleton
|
|
1104
|
+
def standard_clip_duration_us() -> int:
|
|
1105
|
+
"""Duration of clips to split samples into, in microseconds, which is fixed to 20 seconds for now."""
|
|
1106
|
+
return 20 * 1_000_000 # 20 seconds
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
|
|
1110
|
+
def populate_clip_ranges(
|
|
1111
|
+
data_begin_dt: datetime.datetime,
|
|
1112
|
+
data_end_dt: datetime.datetime,
|
|
1113
|
+
*,
|
|
1114
|
+
clip_duration_us: int = None
|
|
1115
|
+
) -> Generator[tuple[datetime.datetime, datetime.datetime, datetime.datetime, datetime.datetime]]:
|
|
1116
|
+
"""
|
|
1117
|
+
Generate clip begin datetime, clip data begin datetime, and clip data end datetime for each clip slot that overlaps
|
|
1118
|
+
with the given data begin and end datetimes.
|
|
1119
|
+
The clip begin datetime is the beginning of the clip slot, the clip data begin datetime is the maximum of the clip
|
|
1120
|
+
begin datetime and the data begin datetime, and the clip data end datetime is the minimum of the clip end datetime
|
|
1121
|
+
and the data end datetime.
|
|
1122
|
+
|
|
1123
|
+
:param data_begin_dt: The beginning datetime of the data.
|
|
1124
|
+
:param data_end_dt: The end datetime of the data.
|
|
1125
|
+
:param clip_duration_us: The duration of each clip in microseconds. If not provided,
|
|
1126
|
+
the standard clip duration will be used.
|
|
1127
|
+
:return: A generator of tuples containing the clip begin datetime, clip data begin datetime,
|
|
1128
|
+
and clip data end datetime
|
|
1129
|
+
"""
|
|
1130
|
+
clip_duration_us = clip_duration_us or standard_clip_duration_us()
|
|
1131
|
+
|
|
1132
|
+
if data_begin_dt == data_end_dt:
|
|
1133
|
+
clip_slot = math.floor(dt_to_ts_us(data_begin_dt) / clip_duration_us)
|
|
1134
|
+
clip_begin_dt = dt_from_ts_us(clip_slot * clip_duration_us)
|
|
1135
|
+
clip_end_dt = dt_from_ts_us((clip_slot + 1) * clip_duration_us)
|
|
1136
|
+
yield clip_begin_dt, clip_end_dt, data_begin_dt, data_end_dt
|
|
1137
|
+
return
|
|
1138
|
+
|
|
1139
|
+
begin_clip_slot = math.floor(dt_to_ts_us(data_begin_dt) / clip_duration_us)
|
|
1140
|
+
end_clip_slot = math.ceil(dt_to_ts_us(data_end_dt) / clip_duration_us)
|
|
1141
|
+
|
|
1142
|
+
for clip_slot in range(begin_clip_slot, end_clip_slot):
|
|
1143
|
+
clip_begin_dt = dt_from_ts_us(clip_slot * clip_duration_us)
|
|
1144
|
+
clip_end_dt = dt_from_ts_us((clip_slot + 1) * clip_duration_us)
|
|
1145
|
+
clip_data_begin_dt = max(clip_begin_dt, data_begin_dt)
|
|
1146
|
+
clip_data_end_dt = min(clip_end_dt, data_end_dt)
|
|
1147
|
+
|
|
1148
|
+
yield clip_begin_dt, clip_end_dt, clip_data_begin_dt, clip_data_end_dt
|
|
@@ -39,8 +39,6 @@ resources/unittest/s3utils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz
|
|
|
39
39
|
resources/unittest/s3utils_archive/archive.compressed.zip
|
|
40
40
|
resources/unittest/s3utils_archive/archive.uncompressed.zip
|
|
41
41
|
src/plexus/common/__init__.py
|
|
42
|
-
src/plexus/common/pose.py
|
|
43
|
-
src/plexus/common/proj.py
|
|
44
42
|
src/plexus/common/carto/OSMFile.py
|
|
45
43
|
src/plexus/common/carto/OSMNode.py
|
|
46
44
|
src/plexus/common/carto/OSMTags.py
|
|
@@ -56,6 +54,7 @@ src/plexus/common/utils/bagutils.py
|
|
|
56
54
|
src/plexus/common/utils/config.py
|
|
57
55
|
src/plexus/common/utils/datautils.py
|
|
58
56
|
src/plexus/common/utils/dockerutils.py
|
|
57
|
+
src/plexus/common/utils/gisutils.py
|
|
59
58
|
src/plexus/common/utils/jsonutils.py
|
|
60
59
|
src/plexus/common/utils/ormutils.py
|
|
61
60
|
src/plexus/common/utils/pathutils.py
|
|
@@ -73,8 +72,6 @@ src/plexus_python_common.egg-info/top_level.txt
|
|
|
73
72
|
test/testenv.py
|
|
74
73
|
test/plexus_tests/__init__.py
|
|
75
74
|
test/plexus_tests/common/__init__.py
|
|
76
|
-
test/plexus_tests/common/pose_test.py
|
|
77
|
-
test/plexus_tests/common/proj_test.py
|
|
78
75
|
test/plexus_tests/common/carto/__init__.py
|
|
79
76
|
test/plexus_tests/common/carto/osm_file_test.py
|
|
80
77
|
test/plexus_tests/common/carto/osm_tags_test.py
|
|
@@ -82,6 +79,7 @@ test/plexus_tests/common/utils/__init__.py
|
|
|
82
79
|
test/plexus_tests/common/utils/bagutils_test.py
|
|
83
80
|
test/plexus_tests/common/utils/datautils_test.py
|
|
84
81
|
test/plexus_tests/common/utils/dockerutils_test.py
|
|
82
|
+
test/plexus_tests/common/utils/gisutils_test.py
|
|
85
83
|
test/plexus_tests/common/utils/jsonutils_test.py
|
|
86
84
|
test/plexus_tests/common/utils/ormutils_test.py
|
|
87
85
|
test/plexus_tests/common/utils/pathutils_test.py
|
|
@@ -4,8 +4,8 @@ import unittest.mock
|
|
|
4
4
|
import lxml.etree
|
|
5
5
|
|
|
6
6
|
from plexus.common.carto import OSMFile
|
|
7
|
-
from plexus.common.
|
|
8
|
-
from plexus.common.
|
|
7
|
+
from plexus.common.utils.gisutils import Coord
|
|
8
|
+
from plexus.common.utils.gisutils import make_proj
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class MockedTree(object):
|
|
@@ -1,11 +1,73 @@
|
|
|
1
|
+
import math
|
|
1
2
|
import random
|
|
2
3
|
import unittest
|
|
3
4
|
|
|
4
5
|
import ddt
|
|
6
|
+
import numpy as np
|
|
5
7
|
import pydantic as pdt
|
|
8
|
+
import pyquaternion as pyquat
|
|
9
|
+
from iker.common.utils.randutils import randomizer
|
|
6
10
|
|
|
7
|
-
from plexus.common.
|
|
8
|
-
from plexus.common.
|
|
11
|
+
from plexus.common.utils.gisutils import Coord, EQDCProj, UTMProj, WebMercProj
|
|
12
|
+
from plexus.common.utils.gisutils import Pose
|
|
13
|
+
from plexus.common.utils.gisutils import make_proj
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@ddt.ddt
|
|
17
|
+
class PoseTest(unittest.TestCase):
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def random_pose() -> Pose:
|
|
21
|
+
q = pyquat.Quaternion(axis=randomizer().random_unit_vector(3),
|
|
22
|
+
angle=math.pi * randomizer().next_float(0.0, 0.5))
|
|
23
|
+
return Pose(0, np.array(randomizer().random_unit_vector(3)), q.normalised.elements)
|
|
24
|
+
|
|
25
|
+
def assert_array_almost_equal(self, xs: np.ndarray, ys: np.ndarray, delta: float):
|
|
26
|
+
self.assertEqual(len(xs), len(ys))
|
|
27
|
+
for x, y in zip(xs, ys):
|
|
28
|
+
self.assertAlmostEqual(x, y, delta=delta)
|
|
29
|
+
|
|
30
|
+
def test_builtin_init(self):
|
|
31
|
+
for _ in range(0, 10000):
|
|
32
|
+
expect = PoseTest.random_pose()
|
|
33
|
+
actual = Pose(0, expect.p, expect.q)
|
|
34
|
+
|
|
35
|
+
self.assert_array_almost_equal(expect.p, actual.p, delta=1e-9)
|
|
36
|
+
self.assert_array_almost_equal(expect.q, actual.q, delta=1e-9)
|
|
37
|
+
|
|
38
|
+
def test_add_sub(self):
|
|
39
|
+
for _ in range(0, 10000):
|
|
40
|
+
pose = PoseTest.random_pose()
|
|
41
|
+
delta = PoseTest.random_pose()
|
|
42
|
+
|
|
43
|
+
result = Pose.sub(Pose.add(pose, delta), pose)
|
|
44
|
+
|
|
45
|
+
self.assert_array_almost_equal(delta.p, result.p, delta=1e-9)
|
|
46
|
+
self.assert_array_almost_equal(delta.q, result.q, delta=1e-9)
|
|
47
|
+
|
|
48
|
+
def test_interpolate(self):
|
|
49
|
+
for _ in range(0, 10000):
|
|
50
|
+
t = randomizer().next_float()
|
|
51
|
+
|
|
52
|
+
pose1 = PoseTest.random_pose()
|
|
53
|
+
pose2 = PoseTest.random_pose()
|
|
54
|
+
|
|
55
|
+
inter1 = Pose.interpolate(pose1, pose2, t)
|
|
56
|
+
inter2 = Pose.interpolate(pose2, pose1, 1.0 - t)
|
|
57
|
+
|
|
58
|
+
self.assert_array_almost_equal(inter1.p, inter2.p, delta=1e-9)
|
|
59
|
+
self.assert_array_almost_equal(inter1.q, inter2.q, delta=1e-9)
|
|
60
|
+
|
|
61
|
+
def test_matrix(self):
|
|
62
|
+
for _ in range(0, 10000):
|
|
63
|
+
point = np.array(randomizer().random_unit_vector(3))
|
|
64
|
+
|
|
65
|
+
pose = PoseTest.random_pose()
|
|
66
|
+
|
|
67
|
+
expect = pose.translate(pose.rotate(point))
|
|
68
|
+
result = np.matmul(pose.matrix(), np.array([*point, 1.0])[:, None]).transpose()[0][:3]
|
|
69
|
+
|
|
70
|
+
self.assert_array_almost_equal(expect, result, delta=1e-9)
|
|
9
71
|
|
|
10
72
|
|
|
11
73
|
@ddt.ddt
|
|
@@ -10,6 +10,7 @@ from iker.common.utils.iterutils import last
|
|
|
10
10
|
from iker.common.utils.jsonutils import JsonObject
|
|
11
11
|
|
|
12
12
|
from plexus.common.utils.tagutils import MutableTagset, Tag, TagCache, Tagset
|
|
13
|
+
from plexus.common.utils.tagutils import populate_clip_ranges
|
|
13
14
|
from plexus.common.utils.tagutils import predefined_tagsets, render_tagset_markdown_readme
|
|
14
15
|
from plexus.common.utils.tagutils import tag_cache_file_path
|
|
15
16
|
|
|
@@ -497,3 +498,177 @@ class TagUtilsTest(unittest.TestCase):
|
|
|
497
498
|
tag_records_count * 2 * (tags_count - tagset_tags_count) // tags_count)
|
|
498
499
|
self.assertEqual(len(list(dst_target_cache.iter_tags(tagsets=[tagset], tagset_inverted=False))),
|
|
499
500
|
tag_records_count * 2 * tagset_tags_count // tags_count)
|
|
501
|
+
|
|
502
|
+
data_populate_clip_ranges = [
|
|
503
|
+
(
|
|
504
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
505
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
506
|
+
[
|
|
507
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
508
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
509
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
510
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00")),
|
|
511
|
+
],
|
|
512
|
+
),
|
|
513
|
+
(
|
|
514
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
515
|
+
dt_parse_iso("2023-01-01T00:00:50.000000+00:00"),
|
|
516
|
+
[
|
|
517
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
518
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
519
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
520
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00")),
|
|
521
|
+
(dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
522
|
+
dt_parse_iso("2023-01-01T00:00:40.000000+00:00"),
|
|
523
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
524
|
+
dt_parse_iso("2023-01-01T00:00:40.000000+00:00")),
|
|
525
|
+
(dt_parse_iso("2023-01-01T00:00:40.000000+00:00"),
|
|
526
|
+
dt_parse_iso("2023-01-01T00:01:00.000000+00:00"),
|
|
527
|
+
dt_parse_iso("2023-01-01T00:00:40.000000+00:00"),
|
|
528
|
+
dt_parse_iso("2023-01-01T00:00:50.000000+00:00")),
|
|
529
|
+
],
|
|
530
|
+
),
|
|
531
|
+
(
|
|
532
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
533
|
+
dt_parse_iso("2023-01-01T00:00:15.000000+00:00"),
|
|
534
|
+
[
|
|
535
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
536
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
537
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
538
|
+
dt_parse_iso("2023-01-01T00:00:15.000000+00:00")),
|
|
539
|
+
],
|
|
540
|
+
),
|
|
541
|
+
(
|
|
542
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
543
|
+
dt_parse_iso("2023-01-01T00:00:30.000000+00:00"),
|
|
544
|
+
[
|
|
545
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
546
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
547
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
548
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00")),
|
|
549
|
+
(dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
550
|
+
dt_parse_iso("2023-01-01T00:00:40.000000+00:00"),
|
|
551
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
552
|
+
dt_parse_iso("2023-01-01T00:00:30.000000+00:00")),
|
|
553
|
+
],
|
|
554
|
+
),
|
|
555
|
+
(
|
|
556
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
557
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
558
|
+
[
|
|
559
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
560
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
561
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
562
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00")),
|
|
563
|
+
],
|
|
564
|
+
),
|
|
565
|
+
(
|
|
566
|
+
dt_parse_iso("2023-01-01T00:00:05.000000+00:00"),
|
|
567
|
+
dt_parse_iso("2023-01-01T00:00:05.000000+00:00"),
|
|
568
|
+
[
|
|
569
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
570
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
571
|
+
dt_parse_iso("2023-01-01T00:00:05.000000+00:00"),
|
|
572
|
+
dt_parse_iso("2023-01-01T00:00:05.000000+00:00")),
|
|
573
|
+
],
|
|
574
|
+
)
|
|
575
|
+
]
|
|
576
|
+
|
|
577
|
+
@ddt.idata(data_populate_clip_ranges)
|
|
578
|
+
@ddt.unpack
|
|
579
|
+
def test_populate_clip_ranges(self, data_begin_dt, data_end_dt, expected):
|
|
580
|
+
result = list(populate_clip_ranges(data_begin_dt, data_end_dt))
|
|
581
|
+
self.assertEqual(result, expected)
|
|
582
|
+
|
|
583
|
+
data_populate_clip_ranges__customized_duration = [
|
|
584
|
+
(
|
|
585
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
586
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
587
|
+
[
|
|
588
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
589
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
590
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
591
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00")),
|
|
592
|
+
(dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
593
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
594
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
595
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00")),
|
|
596
|
+
],
|
|
597
|
+
),
|
|
598
|
+
(
|
|
599
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
600
|
+
dt_parse_iso("2023-01-01T00:00:50.000000+00:00"),
|
|
601
|
+
[
|
|
602
|
+
(dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
603
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
604
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
605
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00")),
|
|
606
|
+
(dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
607
|
+
dt_parse_iso("2023-01-01T00:00:30.000000+00:00"),
|
|
608
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
609
|
+
dt_parse_iso("2023-01-01T00:00:30.000000+00:00")),
|
|
610
|
+
(dt_parse_iso("2023-01-01T00:00:30.000000+00:00"),
|
|
611
|
+
dt_parse_iso("2023-01-01T00:00:40.000000+00:00"),
|
|
612
|
+
dt_parse_iso("2023-01-01T00:00:30.000000+00:00"),
|
|
613
|
+
dt_parse_iso("2023-01-01T00:00:40.000000+00:00")),
|
|
614
|
+
(dt_parse_iso("2023-01-01T00:00:40.000000+00:00"),
|
|
615
|
+
dt_parse_iso("2023-01-01T00:00:50.000000+00:00"),
|
|
616
|
+
dt_parse_iso("2023-01-01T00:00:40.000000+00:00"),
|
|
617
|
+
dt_parse_iso("2023-01-01T00:00:50.000000+00:00")),
|
|
618
|
+
],
|
|
619
|
+
),
|
|
620
|
+
(
|
|
621
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
622
|
+
dt_parse_iso("2023-01-01T00:00:15.000000+00:00"),
|
|
623
|
+
[
|
|
624
|
+
(dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
625
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
626
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
627
|
+
dt_parse_iso("2023-01-01T00:00:15.000000+00:00")),
|
|
628
|
+
],
|
|
629
|
+
),
|
|
630
|
+
(
|
|
631
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
632
|
+
dt_parse_iso("2023-01-01T00:00:30.000000+00:00"),
|
|
633
|
+
[
|
|
634
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
635
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
636
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
637
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00")),
|
|
638
|
+
(dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
639
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
640
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
641
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00")),
|
|
642
|
+
(dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
643
|
+
dt_parse_iso("2023-01-01T00:00:30.000000+00:00"),
|
|
644
|
+
dt_parse_iso("2023-01-01T00:00:20.000000+00:00"),
|
|
645
|
+
dt_parse_iso("2023-01-01T00:00:30.000000+00:00")),
|
|
646
|
+
],
|
|
647
|
+
),
|
|
648
|
+
(
|
|
649
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
650
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
651
|
+
[
|
|
652
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
653
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
654
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
655
|
+
dt_parse_iso("2023-01-01T00:00:00.000000+00:00")),
|
|
656
|
+
],
|
|
657
|
+
),
|
|
658
|
+
(
|
|
659
|
+
dt_parse_iso("2023-01-01T00:00:05.000000+00:00"),
|
|
660
|
+
dt_parse_iso("2023-01-01T00:00:05.000000+00:00"),
|
|
661
|
+
[
|
|
662
|
+
(dt_parse_iso("2023-01-01T00:00:00.000000+00:00"),
|
|
663
|
+
dt_parse_iso("2023-01-01T00:00:10.000000+00:00"),
|
|
664
|
+
dt_parse_iso("2023-01-01T00:00:05.000000+00:00"),
|
|
665
|
+
dt_parse_iso("2023-01-01T00:00:05.000000+00:00")),
|
|
666
|
+
],
|
|
667
|
+
)
|
|
668
|
+
]
|
|
669
|
+
|
|
670
|
+
@ddt.idata(data_populate_clip_ranges__customized_duration)
|
|
671
|
+
@ddt.unpack
|
|
672
|
+
def test_populate_clip_ranges__customized_duration(self, data_begin_dt, data_end_dt, expected):
|
|
673
|
+
result = list(populate_clip_ranges(data_begin_dt, data_end_dt, clip_duration_us=10_000_000))
|
|
674
|
+
self.assertEqual(result, expected)
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
from typing import Self
|
|
2
|
-
|
|
3
|
-
import numpy as np
|
|
4
|
-
import pyquaternion as pyquat
|
|
5
|
-
from iker.common.utils.strutils import repr_data
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class Pose(object):
|
|
9
|
-
@classmethod
|
|
10
|
-
def from_numbers(
|
|
11
|
-
cls,
|
|
12
|
-
px: float,
|
|
13
|
-
py: float,
|
|
14
|
-
pz: float,
|
|
15
|
-
qx: float,
|
|
16
|
-
qy: float,
|
|
17
|
-
qz: float,
|
|
18
|
-
qw: float,
|
|
19
|
-
ts: float = 0,
|
|
20
|
-
) -> Self:
|
|
21
|
-
"""
|
|
22
|
-
Constructs a pose from numbers representing position and orientation
|
|
23
|
-
"""
|
|
24
|
-
return Pose(ts, np.array([px, py, pz], dtype=np.float64), np.array([qw, qx, qy, qz], dtype=np.float64))
|
|
25
|
-
|
|
26
|
-
@classmethod
|
|
27
|
-
def add(cls, x: Self, d: Self) -> Self:
|
|
28
|
-
"""
|
|
29
|
-
Performs pose SE3 addition, as x + d = y
|
|
30
|
-
"""
|
|
31
|
-
xq = pyquat.Quaternion(x.q)
|
|
32
|
-
dq = pyquat.Quaternion(d.q)
|
|
33
|
-
|
|
34
|
-
yp = x.p + xq.rotate(d.p)
|
|
35
|
-
yq = xq * dq
|
|
36
|
-
|
|
37
|
-
return Pose(0, yp, yq.normalised.elements)
|
|
38
|
-
|
|
39
|
-
@classmethod
|
|
40
|
-
def sub(cls, y: Self, x: Self) -> Self:
|
|
41
|
-
"""
|
|
42
|
-
Performs pose SE3 subtraction, as x + d = y => d = y - x
|
|
43
|
-
"""
|
|
44
|
-
xq = pyquat.Quaternion(x.q)
|
|
45
|
-
yq = pyquat.Quaternion(y.q)
|
|
46
|
-
|
|
47
|
-
dp = xq.inverse.rotate(y.p - x.p)
|
|
48
|
-
dq = xq.inverse * yq
|
|
49
|
-
|
|
50
|
-
return Pose(0, dp, dq.normalised.elements)
|
|
51
|
-
|
|
52
|
-
@classmethod
|
|
53
|
-
def interpolate(cls, a: Self, b: Self, t: float) -> Self:
|
|
54
|
-
"""
|
|
55
|
-
Interpolates between two given poses, as a * t + b * (1 - t)
|
|
56
|
-
|
|
57
|
-
:return: interpolated pose
|
|
58
|
-
"""
|
|
59
|
-
ts = a.ts + (b.ts - a.ts) * t
|
|
60
|
-
p = a.p + (b.p - a.p) * t
|
|
61
|
-
q = pyquat.Quaternion.slerp(pyquat.Quaternion(a.q), pyquat.Quaternion(b.q), t)
|
|
62
|
-
return Pose(ts, p, q.normalised.elements)
|
|
63
|
-
|
|
64
|
-
def __init__(self, ts: float, p: np.ndarray, q: np.ndarray):
|
|
65
|
-
"""
|
|
66
|
-
Represents a pose
|
|
67
|
-
|
|
68
|
-
:param ts: timestamp
|
|
69
|
-
:param p: position vector
|
|
70
|
-
:param q: orientation quaternion
|
|
71
|
-
"""
|
|
72
|
-
self.ts = ts
|
|
73
|
-
self.p = p
|
|
74
|
-
self.q = q
|
|
75
|
-
|
|
76
|
-
def matrix(self) -> np.ndarray:
|
|
77
|
-
"""
|
|
78
|
-
Returns the transformation matrix of this pose
|
|
79
|
-
|
|
80
|
-
:return: transformation matrix
|
|
81
|
-
"""
|
|
82
|
-
r = pyquat.Quaternion(self.q).rotation_matrix
|
|
83
|
-
t = self.p[:, None]
|
|
84
|
-
return np.block([[r, t], [np.zeros((1, 3), dtype=np.float64), np.ones((1, 1), dtype=np.float64)]])
|
|
85
|
-
|
|
86
|
-
def translate(self, v: np.ndarray) -> np.ndarray:
|
|
87
|
-
"""
|
|
88
|
-
Translate the given vector with the pose position
|
|
89
|
-
|
|
90
|
-
:param v: the vector to be translated
|
|
91
|
-
|
|
92
|
-
:return: translated vector
|
|
93
|
-
"""
|
|
94
|
-
return self.p + v
|
|
95
|
-
|
|
96
|
-
def rotate(self, v: np.ndarray) -> np.ndarray:
|
|
97
|
-
"""
|
|
98
|
-
Rotates the given vector with the pose orientation
|
|
99
|
-
|
|
100
|
-
:param v: the vector to be rotated
|
|
101
|
-
|
|
102
|
-
:return: rotated vector
|
|
103
|
-
"""
|
|
104
|
-
return pyquat.Quaternion(self.q).rotate(v)
|
|
105
|
-
|
|
106
|
-
def __str__(self):
|
|
107
|
-
return repr_data(self)
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import math
|
|
2
|
-
import unittest
|
|
3
|
-
|
|
4
|
-
import ddt
|
|
5
|
-
import numpy as np
|
|
6
|
-
import pyquaternion as pyquat
|
|
7
|
-
from iker.common.utils.randutils import randomizer
|
|
8
|
-
|
|
9
|
-
from plexus.common.pose import Pose
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@ddt.ddt
|
|
13
|
-
class PoseTest(unittest.TestCase):
|
|
14
|
-
|
|
15
|
-
@staticmethod
|
|
16
|
-
def random_pose() -> Pose:
|
|
17
|
-
q = pyquat.Quaternion(axis=randomizer().random_unit_vector(3),
|
|
18
|
-
angle=math.pi * randomizer().next_float(0.0, 0.5))
|
|
19
|
-
return Pose(0, np.array(randomizer().random_unit_vector(3)), q.normalised.elements)
|
|
20
|
-
|
|
21
|
-
def assert_array_almost_equal(self, xs: np.ndarray, ys: np.ndarray, delta: float):
|
|
22
|
-
self.assertEqual(len(xs), len(ys))
|
|
23
|
-
for x, y in zip(xs, ys):
|
|
24
|
-
self.assertAlmostEqual(x, y, delta=delta)
|
|
25
|
-
|
|
26
|
-
def test_builtin_init(self):
|
|
27
|
-
for _ in range(0, 10000):
|
|
28
|
-
expect = PoseTest.random_pose()
|
|
29
|
-
actual = Pose(0, expect.p, expect.q)
|
|
30
|
-
|
|
31
|
-
self.assert_array_almost_equal(expect.p, actual.p, delta=1e-9)
|
|
32
|
-
self.assert_array_almost_equal(expect.q, actual.q, delta=1e-9)
|
|
33
|
-
|
|
34
|
-
def test_add_sub(self):
|
|
35
|
-
for _ in range(0, 10000):
|
|
36
|
-
pose = PoseTest.random_pose()
|
|
37
|
-
delta = PoseTest.random_pose()
|
|
38
|
-
|
|
39
|
-
result = Pose.sub(Pose.add(pose, delta), pose)
|
|
40
|
-
|
|
41
|
-
self.assert_array_almost_equal(delta.p, result.p, delta=1e-9)
|
|
42
|
-
self.assert_array_almost_equal(delta.q, result.q, delta=1e-9)
|
|
43
|
-
|
|
44
|
-
def test_interpolate(self):
|
|
45
|
-
for _ in range(0, 10000):
|
|
46
|
-
t = randomizer().next_float()
|
|
47
|
-
|
|
48
|
-
pose1 = PoseTest.random_pose()
|
|
49
|
-
pose2 = PoseTest.random_pose()
|
|
50
|
-
|
|
51
|
-
inter1 = Pose.interpolate(pose1, pose2, t)
|
|
52
|
-
inter2 = Pose.interpolate(pose2, pose1, 1.0 - t)
|
|
53
|
-
|
|
54
|
-
self.assert_array_almost_equal(inter1.p, inter2.p, delta=1e-9)
|
|
55
|
-
self.assert_array_almost_equal(inter1.q, inter2.q, delta=1e-9)
|
|
56
|
-
|
|
57
|
-
def test_matrix(self):
|
|
58
|
-
for _ in range(0, 10000):
|
|
59
|
-
point = np.array(randomizer().random_unit_vector(3))
|
|
60
|
-
|
|
61
|
-
pose = PoseTest.random_pose()
|
|
62
|
-
|
|
63
|
-
expect = pose.translate(pose.rotate(point))
|
|
64
|
-
result = np.matmul(pose.matrix(), np.array([*point, 1.0])[:, None]).transpose()[0][:3]
|
|
65
|
-
|
|
66
|
-
self.assert_array_almost_equal(expect, result, delta=1e-9)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/0-dummy
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/1-dummy
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/2-dummy
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/resources/unittest/pathutils/dummy.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/carto/OSMTags.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/carto/OSMWay.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/carto/__init__.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/resources/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/__init__.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/apiutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/bagutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/config.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/datautils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/dockerutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/jsonutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/ormutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/pathutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/s3utils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/sqlutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/strutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/src/plexus/common/utils/testutils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.62 → plexus_python_common-1.0.64}/test/plexus_tests/common/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|