gri-plot 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.
- {gri_plot-0.2.3 → gri_plot-0.2.4}/PKG-INFO +6 -17
- {gri_plot-0.2.3 → gri_plot-0.2.4}/README.md +5 -15
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/__init__.py +5 -26
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/figure3d.py +3 -83
- gri_plot-0.2.4/gri_plot/observables/__init__.py +15 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/surfaces/__init__.py +3 -12
- gri_plot-0.2.4/gri_plot/surfaces/mesh.py +133 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/pyproject.toml +9 -2
- gri_plot-0.2.4/report.xml +1 -0
- gri_plot-0.2.4/test/test_figure3d.py +79 -0
- gri_plot-0.2.4/test/test_frames.py +136 -0
- gri_plot-0.2.4/test/test_surfaces/test_mesh.py +51 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/uv.lock +0 -175
- gri_plot-0.2.3/.coveragerc +0 -8
- gri_plot-0.2.3/gri_plot/observables/__init__.py +0 -21
- gri_plot-0.2.3/gri_plot/observables/fdoa.py +0 -256
- gri_plot-0.2.3/gri_plot/observables/tdoa.py +0 -203
- gri_plot-0.2.3/gri_plot/observables/terrain.py +0 -295
- gri_plot-0.2.3/gri_plot/surfaces/gradients/__init__.py +0 -39
- gri_plot-0.2.3/gri_plot/surfaces/gradients/axis.py +0 -76
- gri_plot-0.2.3/gri_plot/surfaces/gradients/line.py +0 -88
- gri_plot-0.2.3/gri_plot/surfaces/gradients/plane.py +0 -55
- gri_plot-0.2.3/gri_plot/surfaces/gradients/point.py +0 -42
- gri_plot-0.2.3/gri_plot/surfaces/intersection.py +0 -219
- gri_plot-0.2.3/gri_plot/surfaces/mesh.py +0 -605
- gri_plot-0.2.3/report.xml +0 -1
- gri_plot-0.2.3/test/test_figure3d.py +0 -287
- gri_plot-0.2.3/test/test_frames.py +0 -44
- gri_plot-0.2.3/test/test_observables/test_fdoa.py +0 -59
- gri_plot-0.2.3/test/test_observables/test_tdoa.py +0 -72
- gri_plot-0.2.3/test/test_observables/test_terrain.py +0 -45
- gri_plot-0.2.3/test/test_surfaces/test_intersection.py +0 -203
- gri_plot-0.2.3/test/test_surfaces/test_mesh.py +0 -239
- {gri_plot-0.2.3 → gri_plot-0.2.4}/.docs_other_projects.md +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/.gitignore +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/.gitlab-ci-deps.yml +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/.gitlab-ci.yml +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/.init_venv.sh +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/.pre-commit-config.yaml +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/.pre-commit-unskipped.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/.python-version +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/.ruff.toml +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/.vscode/settings.json +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/CONTRIBUTING.md +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/LICENSE +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/figure_map.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/frames.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/observables/aoa.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/observables/los.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/observables/range_sphere.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/plot_ellipse.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/py.typed +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/scatter.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/scatter_map.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/shapes/__init__.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/shapes/cone.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/shapes/cylinder.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/shapes/ellipse.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/shapes/ellipsoid.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/shapes/meshgen.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/gri_plot/shapes/sphere.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/notes.txt +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/test/__init__.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/test/test_figure_map.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/test/test_observables/__init__.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/test/test_observables/test_aoa.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/test/test_observables/test_los.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/test/test_observables/test_range_sphere.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/test/test_plot_ellipse.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/test/test_plot_scattermap.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/test/test_scatter.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/test/test_shapes/__init__.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/test/test_shapes/test_cone.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/test/test_shapes/test_cylinder.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/test/test_shapes/test_ellipse.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/test/test_shapes/test_ellipsoid.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/test/test_shapes/test_sphere.py +0 -0
- {gri_plot-0.2.3 → gri_plot-0.2.4}/test/test_surfaces/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gri-plot
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: 3D geolocation visualization and 2D plotting utilities built on Plotly
|
|
5
5
|
Project-URL: Homepage, https://geosolresearch.com
|
|
6
6
|
Project-URL: Repository, https://gitlab.com/geosol-foss/python/gri-plot
|
|
@@ -20,7 +20,6 @@ Requires-Python: >=3.12
|
|
|
20
20
|
Requires-Dist: gri-utils>=0.3.3
|
|
21
21
|
Requires-Dist: numpy>=2.3.3
|
|
22
22
|
Requires-Dist: plotly>=6.3.0
|
|
23
|
-
Requires-Dist: scikit-image>=0.25.0
|
|
24
23
|
Description-Content-Type: text/markdown
|
|
25
24
|
|
|
26
25
|
[](https://geosolresearch.com)
|
|
@@ -31,7 +30,7 @@ Description-Content-Type: text/markdown
|
|
|
31
30
|
|
|
32
31
|
## Overview
|
|
33
32
|
|
|
34
|
-
This library renders
|
|
33
|
+
This library renders parametric geometric shapes (ellipsoids, spheres, cones, cylinders) and observable surfaces that inherit from them (AOA cones, line-of-sight rays, range spheres) as interactive 3D meshes. It also provides shorthand functions for 2D scatter plots and geographic maps.
|
|
35
34
|
|
|
36
35
|
## Installation
|
|
37
36
|
|
|
@@ -86,14 +85,12 @@ Scene builder for composing 3D visualizations from multiple surfaces and points:
|
|
|
86
85
|
- **`add_surface()`**: Add any `ImplicitShape` (shapes or observables)
|
|
87
86
|
- **`add_points()`**: Add labeled scatter points
|
|
88
87
|
- **`add_line()`**: Add line segments connecting points
|
|
89
|
-
- **`add_intersection()`**: Highlight where multiple surfaces intersect
|
|
90
88
|
- **`plot_surfaces()`**: Convenience function for quick multi-surface plots
|
|
91
89
|
- Method chaining, automatic color cycling from D3 palette, `plotly_dark` template
|
|
92
90
|
|
|
93
91
|
```python
|
|
94
92
|
from gri_plot import Figure3D
|
|
95
93
|
from gri_plot.shapes import Sphere
|
|
96
|
-
from gri_plot.observables import TdoaSurface
|
|
97
94
|
import numpy as np
|
|
98
95
|
|
|
99
96
|
fig = Figure3D()
|
|
@@ -113,7 +110,7 @@ Coordinate frame handling for 3D display:
|
|
|
113
110
|
|
|
114
111
|
## Shapes
|
|
115
112
|
|
|
116
|
-
Geometric primitives implementing the `ImplicitShape` ABC. Each supports parametric mesh generation (fast, exact geometry) and implicit residual evaluation
|
|
113
|
+
Geometric primitives implementing the `ImplicitShape` ABC. Each supports parametric mesh generation (fast, exact geometry) and implicit residual evaluation.
|
|
117
114
|
|
|
118
115
|
- **Ellipsoid**: From center + covariance matrix, or center + semi-axes + rotation matrix
|
|
119
116
|
- **Sphere**: From center + radius
|
|
@@ -123,31 +120,23 @@ Geometric primitives implementing the `ImplicitShape` ABC. Each supports paramet
|
|
|
123
120
|
|
|
124
121
|
## Observables
|
|
125
122
|
|
|
126
|
-
Geolocation-specific surfaces
|
|
123
|
+
Geolocation-specific surfaces that inherit from parametric shapes:
|
|
127
124
|
|
|
128
|
-
- **TdoaSurface**: TDOA hyperboloid where range_1 - range_2 = c x tdoa, from two collector positions + time difference
|
|
129
|
-
- **FdoaSurface**: FDOA iso-Doppler surface from two collector positions/velocities + carrier frequency + frequency difference
|
|
130
125
|
- **AoaSurface**: AOA cone (extends Cone) from collector position + measured direction + angular error
|
|
131
126
|
- **LosSurface**: Line of sight ray (extends Cylinder) from start point + direction + length; also provides `to_line_trace()` for simple line rendering
|
|
132
127
|
- **RangeSphere**: Range/TOA sphere (extends Sphere) from collector position + range measurement
|
|
133
|
-
- **TerrainSurface**: Terrain from XYZ vertex grid with interpolated residual evaluation; factory methods `from_xyz_grids()` and `from_elevation_grid()`
|
|
134
128
|
|
|
135
129
|
## Surfaces
|
|
136
130
|
|
|
137
|
-
Low-level infrastructure for
|
|
131
|
+
Low-level infrastructure for shape rendering:
|
|
138
132
|
|
|
139
133
|
- **ImplicitShape ABC**: Interface requiring `residual_fn()`, `get_bounds_xyz()`, `to_mesh()`, `to_trace()`, `is_volume`, `label`; also provides `contains()` for point-in-shape testing
|
|
140
|
-
- **Mesh utilities**: `
|
|
141
|
-
- **IntersectionField**: Euclidean distance in residual space for highlighting multi-surface intersections; distinguishes volumes (interior contribution) from surfaces (boundary contribution)
|
|
142
|
-
- **Gradient functions**: Intensity calculators for mesh coloring -- `distance_from_point`, `distance_from_line`, `distance_from_line_segment`, `distance_from_plane`, `distance_from_axis`, `axis_value`
|
|
134
|
+
- **Mesh utilities**: `grid_to_mesh()` (parametric grid triangulation), `vertices_to_mesh3d()` (vertices/faces to Plotly Mesh3d trace)
|
|
143
135
|
|
|
144
136
|
## Dependencies
|
|
145
137
|
|
|
146
138
|
- **plotly**: Interactive 2D and 3D visualization
|
|
147
|
-
- **matplotlib**: Static plot support
|
|
148
139
|
- **numpy**: Array operations
|
|
149
|
-
- **scipy**: Marching cubes mesh extraction
|
|
150
|
-
- **scikit-image**: Additional mesh processing
|
|
151
140
|
- **gri-utils**: Coordinate conversions and constants
|
|
152
141
|
|
|
153
142
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## Overview
|
|
8
8
|
|
|
9
|
-
This library renders
|
|
9
|
+
This library renders parametric geometric shapes (ellipsoids, spheres, cones, cylinders) and observable surfaces that inherit from them (AOA cones, line-of-sight rays, range spheres) as interactive 3D meshes. It also provides shorthand functions for 2D scatter plots and geographic maps.
|
|
10
10
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
@@ -61,14 +61,12 @@ Scene builder for composing 3D visualizations from multiple surfaces and points:
|
|
|
61
61
|
- **`add_surface()`**: Add any `ImplicitShape` (shapes or observables)
|
|
62
62
|
- **`add_points()`**: Add labeled scatter points
|
|
63
63
|
- **`add_line()`**: Add line segments connecting points
|
|
64
|
-
- **`add_intersection()`**: Highlight where multiple surfaces intersect
|
|
65
64
|
- **`plot_surfaces()`**: Convenience function for quick multi-surface plots
|
|
66
65
|
- Method chaining, automatic color cycling from D3 palette, `plotly_dark` template
|
|
67
66
|
|
|
68
67
|
```python
|
|
69
68
|
from gri_plot import Figure3D
|
|
70
69
|
from gri_plot.shapes import Sphere
|
|
71
|
-
from gri_plot.observables import TdoaSurface
|
|
72
70
|
import numpy as np
|
|
73
71
|
|
|
74
72
|
fig = Figure3D()
|
|
@@ -88,7 +86,7 @@ Coordinate frame handling for 3D display:
|
|
|
88
86
|
|
|
89
87
|
## Shapes
|
|
90
88
|
|
|
91
|
-
Geometric primitives implementing the `ImplicitShape` ABC. Each supports parametric mesh generation (fast, exact geometry) and implicit residual evaluation
|
|
89
|
+
Geometric primitives implementing the `ImplicitShape` ABC. Each supports parametric mesh generation (fast, exact geometry) and implicit residual evaluation.
|
|
92
90
|
|
|
93
91
|
- **Ellipsoid**: From center + covariance matrix, or center + semi-axes + rotation matrix
|
|
94
92
|
- **Sphere**: From center + radius
|
|
@@ -98,31 +96,23 @@ Geometric primitives implementing the `ImplicitShape` ABC. Each supports paramet
|
|
|
98
96
|
|
|
99
97
|
## Observables
|
|
100
98
|
|
|
101
|
-
Geolocation-specific surfaces
|
|
99
|
+
Geolocation-specific surfaces that inherit from parametric shapes:
|
|
102
100
|
|
|
103
|
-
- **TdoaSurface**: TDOA hyperboloid where range_1 - range_2 = c x tdoa, from two collector positions + time difference
|
|
104
|
-
- **FdoaSurface**: FDOA iso-Doppler surface from two collector positions/velocities + carrier frequency + frequency difference
|
|
105
101
|
- **AoaSurface**: AOA cone (extends Cone) from collector position + measured direction + angular error
|
|
106
102
|
- **LosSurface**: Line of sight ray (extends Cylinder) from start point + direction + length; also provides `to_line_trace()` for simple line rendering
|
|
107
103
|
- **RangeSphere**: Range/TOA sphere (extends Sphere) from collector position + range measurement
|
|
108
|
-
- **TerrainSurface**: Terrain from XYZ vertex grid with interpolated residual evaluation; factory methods `from_xyz_grids()` and `from_elevation_grid()`
|
|
109
104
|
|
|
110
105
|
## Surfaces
|
|
111
106
|
|
|
112
|
-
Low-level infrastructure for
|
|
107
|
+
Low-level infrastructure for shape rendering:
|
|
113
108
|
|
|
114
109
|
- **ImplicitShape ABC**: Interface requiring `residual_fn()`, `get_bounds_xyz()`, `to_mesh()`, `to_trace()`, `is_volume`, `label`; also provides `contains()` for point-in-shape testing
|
|
115
|
-
- **Mesh utilities**: `
|
|
116
|
-
- **IntersectionField**: Euclidean distance in residual space for highlighting multi-surface intersections; distinguishes volumes (interior contribution) from surfaces (boundary contribution)
|
|
117
|
-
- **Gradient functions**: Intensity calculators for mesh coloring -- `distance_from_point`, `distance_from_line`, `distance_from_line_segment`, `distance_from_plane`, `distance_from_axis`, `axis_value`
|
|
110
|
+
- **Mesh utilities**: `grid_to_mesh()` (parametric grid triangulation), `vertices_to_mesh3d()` (vertices/faces to Plotly Mesh3d trace)
|
|
118
111
|
|
|
119
112
|
## Dependencies
|
|
120
113
|
|
|
121
114
|
- **plotly**: Interactive 2D and 3D visualization
|
|
122
|
-
- **matplotlib**: Static plot support
|
|
123
115
|
- **numpy**: Array operations
|
|
124
|
-
- **scipy**: Marching cubes mesh extraction
|
|
125
|
-
- **scikit-image**: Additional mesh processing
|
|
126
116
|
- **gri-utils**: Coordinate conversions and constants
|
|
127
117
|
|
|
128
118
|
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
"""Plotting utilities for GeoSol Research.
|
|
2
2
|
|
|
3
3
|
This package provides utilities for 2D and 3D visualization using Plotly,
|
|
4
|
-
including scatter plots, geographic maps, and 3D
|
|
5
|
-
observables.
|
|
4
|
+
including scatter plots, geographic maps, and parametric 3D shapes for
|
|
5
|
+
geolocation observables.
|
|
6
6
|
|
|
7
7
|
Main components:
|
|
8
8
|
- scatter: 2D scatter plots with Plotly
|
|
9
9
|
- scatter_map: Geographic scatter maps
|
|
10
10
|
- Figure3D: 3D figure for combining surfaces
|
|
11
11
|
- shapes: Geometric shapes (Ellipsoid, Sphere, Cone, Cylinder)
|
|
12
|
-
- observables:
|
|
13
|
-
- surfaces:
|
|
12
|
+
- observables: Parametric geolocation surfaces (AOA, LOS, Range)
|
|
13
|
+
- surfaces: ImplicitShape ABC and mesh trace utilities
|
|
14
14
|
- frames: Coordinate frame handling
|
|
15
15
|
"""
|
|
16
16
|
|
|
@@ -23,11 +23,8 @@ from .frames import Bounds, Frame, FrameTransformer
|
|
|
23
23
|
# Observables
|
|
24
24
|
from .observables import (
|
|
25
25
|
AoaSurface,
|
|
26
|
-
FdoaSurface,
|
|
27
26
|
LosSurface,
|
|
28
27
|
RangeSphere,
|
|
29
|
-
TdoaSurface,
|
|
30
|
-
TerrainSurface,
|
|
31
28
|
)
|
|
32
29
|
from .plot_ellipse import plot_ellipse
|
|
33
30
|
from .scatter import scatter
|
|
@@ -45,15 +42,7 @@ from .surfaces import (
|
|
|
45
42
|
expand_bounds,
|
|
46
43
|
merge_bounds,
|
|
47
44
|
)
|
|
48
|
-
from .surfaces.
|
|
49
|
-
axis_value,
|
|
50
|
-
distance_from_axis,
|
|
51
|
-
distance_from_line,
|
|
52
|
-
distance_from_line_segment,
|
|
53
|
-
distance_from_plane,
|
|
54
|
-
distance_from_point,
|
|
55
|
-
)
|
|
56
|
-
from .surfaces.mesh import field_to_mesh, grid_to_mesh, vertices_to_mesh3d
|
|
45
|
+
from .surfaces.mesh import grid_to_mesh, vertices_to_mesh3d
|
|
57
46
|
|
|
58
47
|
__all__ = [
|
|
59
48
|
"DEFAULT_COLORS",
|
|
@@ -63,7 +52,6 @@ __all__ = [
|
|
|
63
52
|
"Cylinder",
|
|
64
53
|
"Ellipse",
|
|
65
54
|
"Ellipsoid",
|
|
66
|
-
"FdoaSurface",
|
|
67
55
|
"Figure3D",
|
|
68
56
|
"FigureMap",
|
|
69
57
|
"Frame",
|
|
@@ -73,19 +61,10 @@ __all__ = [
|
|
|
73
61
|
"MapStyle",
|
|
74
62
|
"RangeSphere",
|
|
75
63
|
"Sphere",
|
|
76
|
-
"TdoaSurface",
|
|
77
|
-
"TerrainSurface",
|
|
78
|
-
"axis_value",
|
|
79
64
|
"cone_mesh",
|
|
80
65
|
"cylinder_mesh",
|
|
81
|
-
"distance_from_axis",
|
|
82
|
-
"distance_from_line",
|
|
83
|
-
"distance_from_line_segment",
|
|
84
|
-
"distance_from_plane",
|
|
85
|
-
"distance_from_point",
|
|
86
66
|
"ellipsoid_mesh",
|
|
87
67
|
"expand_bounds",
|
|
88
|
-
"field_to_mesh",
|
|
89
68
|
"grid_to_mesh",
|
|
90
69
|
"merge_bounds",
|
|
91
70
|
"plot_ellipse",
|
|
@@ -12,9 +12,7 @@ import numpy as np
|
|
|
12
12
|
import plotly.graph_objects as go
|
|
13
13
|
|
|
14
14
|
from .frames import Frame, FrameTransformer
|
|
15
|
-
from .surfaces import ImplicitShape,
|
|
16
|
-
from .surfaces.intersection import IntersectionField
|
|
17
|
-
from .surfaces.mesh import field_to_mesh, vertices_to_mesh3d
|
|
15
|
+
from .surfaces import ImplicitShape, merge_bounds
|
|
18
16
|
|
|
19
17
|
if TYPE_CHECKING:
|
|
20
18
|
from collections.abc import Sequence
|
|
@@ -140,7 +138,7 @@ class Figure3D:
|
|
|
140
138
|
|
|
141
139
|
Args:
|
|
142
140
|
surface: ImplicitShape object to visualize.
|
|
143
|
-
resolution: Mesh resolution. If None, uses
|
|
141
|
+
resolution: Mesh resolution. If None, uses the shape's default.
|
|
144
142
|
color: Base color for single-color gradient. If None, uses full
|
|
145
143
|
Viridis colorscale. For multiple surfaces, pick colors from
|
|
146
144
|
DEFAULT_COLORS to distinguish them (e.g., color=DEFAULT_COLORS[0]).
|
|
@@ -304,84 +302,6 @@ class Figure3D:
|
|
|
304
302
|
trace_type = type(trace)
|
|
305
303
|
return trace_type(**trace_dict)
|
|
306
304
|
|
|
307
|
-
def add_intersection( # noqa: PLR0913 - intersection has many visual options
|
|
308
|
-
self,
|
|
309
|
-
*surfaces: ImplicitShape,
|
|
310
|
-
threshold: float = 1.5,
|
|
311
|
-
steepness: float | None = None,
|
|
312
|
-
resolution: int | None = None,
|
|
313
|
-
color: str | None = None,
|
|
314
|
-
opacity: float = 0.8,
|
|
315
|
-
label: str | None = None,
|
|
316
|
-
**kwargs,
|
|
317
|
-
) -> Figure3D:
|
|
318
|
-
"""Add an intersection visualization for multiple implicit surfaces.
|
|
319
|
-
|
|
320
|
-
Highlights regions where multiple surfaces intersect by computing a
|
|
321
|
-
combined "closeness" field. Single-surface regions are hidden (below
|
|
322
|
-
threshold), while multi-surface intersections are progressively
|
|
323
|
-
highlighted.
|
|
324
|
-
|
|
325
|
-
Args:
|
|
326
|
-
*surfaces: ImplicitSurface objects to intersect. Must be at least 2.
|
|
327
|
-
threshold: Value subtracted from combined closeness. Default 1.5
|
|
328
|
-
hides single-surface regions while showing 2+ intersections.
|
|
329
|
-
steepness: Exponential falloff rate. If None, auto-computed based
|
|
330
|
-
on number of surfaces to ensure good visual gradient.
|
|
331
|
-
resolution: Mesh resolution. If None, uses field_to_mesh default.
|
|
332
|
-
color: Base color for gradient. If None, uses next palette color.
|
|
333
|
-
opacity: Surface opacity (0-1).
|
|
334
|
-
label: Optional label for legends.
|
|
335
|
-
**kwargs: Additional arguments passed to the Mesh3d trace.
|
|
336
|
-
|
|
337
|
-
Returns:
|
|
338
|
-
Self for method chaining.
|
|
339
|
-
|
|
340
|
-
Raises:
|
|
341
|
-
ValueError: If fewer than 2 surfaces provided.
|
|
342
|
-
"""
|
|
343
|
-
intersection = IntersectionField(
|
|
344
|
-
surfaces,
|
|
345
|
-
threshold=threshold,
|
|
346
|
-
steepness=steepness,
|
|
347
|
-
)
|
|
348
|
-
|
|
349
|
-
# Get bounds and add to tracking
|
|
350
|
-
bounds_xyz = intersection.get_bounds_xyz()
|
|
351
|
-
self._surface_bounds.append(bounds_xyz)
|
|
352
|
-
|
|
353
|
-
# Determine color
|
|
354
|
-
if color is None:
|
|
355
|
-
color = self._next_color()
|
|
356
|
-
colorscale = _color_to_gradient(color)
|
|
357
|
-
|
|
358
|
-
# Generate mesh using marching cubes via field_to_mesh
|
|
359
|
-
bounds = expand_bounds(bounds_xyz, 1.05)
|
|
360
|
-
if resolution is None:
|
|
361
|
-
vertices, faces = field_to_mesh(intersection.residual_fn, bounds)
|
|
362
|
-
else:
|
|
363
|
-
vertices, faces = field_to_mesh(
|
|
364
|
-
intersection.residual_fn,
|
|
365
|
-
bounds,
|
|
366
|
-
resolution=resolution,
|
|
367
|
-
)
|
|
368
|
-
|
|
369
|
-
# Create Mesh3d trace
|
|
370
|
-
trace = vertices_to_mesh3d(
|
|
371
|
-
vertices,
|
|
372
|
-
faces,
|
|
373
|
-
colorscale=colorscale,
|
|
374
|
-
opacity=opacity,
|
|
375
|
-
name=label,
|
|
376
|
-
**kwargs,
|
|
377
|
-
)
|
|
378
|
-
|
|
379
|
-
if self.display_frame != Frame.XYZ:
|
|
380
|
-
trace = self._transform_trace(trace)
|
|
381
|
-
|
|
382
|
-
self._traces.append(trace)
|
|
383
|
-
return self
|
|
384
|
-
|
|
385
305
|
def get_bounds_xyz(
|
|
386
306
|
self,
|
|
387
307
|
) -> tuple[NDArray[np.floating], NDArray[np.floating]] | None:
|
|
@@ -457,7 +377,7 @@ def plot_surfaces(
|
|
|
457
377
|
display_frame: Coordinate frame for visualization.
|
|
458
378
|
origin_xyz: Origin for ENU frame.
|
|
459
379
|
title: Figure title.
|
|
460
|
-
resolution: Mesh resolution. If None, uses
|
|
380
|
+
resolution: Mesh resolution. If None, uses the shape's default.
|
|
461
381
|
**kwargs: Additional arguments passed to add_surface.
|
|
462
382
|
|
|
463
383
|
Returns:
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Observable surface classes for geolocation visualization.
|
|
2
|
+
|
|
3
|
+
This module provides parametric surfaces for visualizing AOA cones,
|
|
4
|
+
line-of-sight rays, and range spheres.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .aoa import AoaSurface
|
|
8
|
+
from .los import LosSurface
|
|
9
|
+
from .range_sphere import RangeSphere
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"AoaSurface",
|
|
13
|
+
"LosSurface",
|
|
14
|
+
"RangeSphere",
|
|
15
|
+
]
|
|
@@ -21,26 +21,20 @@ class ImplicitShape(ABC):
|
|
|
21
21
|
This ABC provides a unified interface for shapes that can be:
|
|
22
22
|
- Evaluated via an implicit function (residual_fn)
|
|
23
23
|
- Rendered as a Plotly Mesh3d trace
|
|
24
|
-
- Used in intersection calculations
|
|
25
24
|
|
|
26
25
|
Residual convention:
|
|
27
26
|
- residual < 0: inside the shape
|
|
28
27
|
- residual = 0: on the boundary
|
|
29
28
|
- residual > 0: outside the shape
|
|
30
29
|
|
|
31
|
-
The is_volume property
|
|
32
|
-
|
|
33
|
-
- False: boundary (residual = 0) is the solution locus
|
|
30
|
+
The is_volume property describes whether the shape represents a solid
|
|
31
|
+
volume or a thin boundary surface.
|
|
34
32
|
"""
|
|
35
33
|
|
|
36
34
|
@property
|
|
37
35
|
@abstractmethod
|
|
38
36
|
def is_volume(self) -> bool:
|
|
39
|
-
"""Whether this shape represents a volume (True) or surface (False).
|
|
40
|
-
|
|
41
|
-
Volumes contribute to intersection everywhere inside (residual < 0).
|
|
42
|
-
Surfaces contribute based on distance from boundary (|residual|).
|
|
43
|
-
"""
|
|
37
|
+
"""Whether this shape represents a volume (True) or surface (False)."""
|
|
44
38
|
...
|
|
45
39
|
|
|
46
40
|
@property
|
|
@@ -163,11 +157,8 @@ def expand_bounds(
|
|
|
163
157
|
return center - half_extent * factor, center + half_extent * factor
|
|
164
158
|
|
|
165
159
|
|
|
166
|
-
from .intersection import IntersectionField # noqa: E402
|
|
167
|
-
|
|
168
160
|
__all__ = [
|
|
169
161
|
"ImplicitShape",
|
|
170
|
-
"IntersectionField",
|
|
171
162
|
"expand_bounds",
|
|
172
163
|
"merge_bounds",
|
|
173
164
|
]
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Core mesh utilities for 3D surface visualization.
|
|
2
|
+
|
|
3
|
+
Provides functions for creating Plotly Mesh3d traces from vertex/face arrays
|
|
4
|
+
and for building triangular meshes from parametric 2D grids.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import plotly.graph_objects as go
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from collections.abc import Callable
|
|
16
|
+
|
|
17
|
+
from numpy.typing import NDArray
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def vertices_to_mesh3d( # noqa: PLR0913 - mesh rendering needs many options
|
|
21
|
+
vertices: NDArray[np.floating],
|
|
22
|
+
faces: NDArray[np.integer],
|
|
23
|
+
*,
|
|
24
|
+
intensity_fn: Callable[[NDArray[np.floating]], NDArray[np.floating]] | None = None,
|
|
25
|
+
color: str | None = None,
|
|
26
|
+
opacity: float = 0.7,
|
|
27
|
+
name: str | None = None,
|
|
28
|
+
colorscale: str | list = "Viridis",
|
|
29
|
+
colorbar_title: str | None = None,
|
|
30
|
+
showscale: bool = False,
|
|
31
|
+
**kwargs,
|
|
32
|
+
) -> go.Mesh3d:
|
|
33
|
+
"""Create a Plotly Mesh3d trace from vertices and faces.
|
|
34
|
+
|
|
35
|
+
This is the core rendering function. All shape and surface classes should
|
|
36
|
+
use this to generate their Plotly traces.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
vertices: Vertex positions, shape (N, 3).
|
|
40
|
+
faces: Triangle indices, shape (M, 3).
|
|
41
|
+
intensity_fn: Optional function to compute vertex intensities for coloring.
|
|
42
|
+
Takes vertices (N, 3) and returns intensities (N,). If None and color
|
|
43
|
+
is None, defaults to distance from mesh centroid (works well in any
|
|
44
|
+
coordinate frame).
|
|
45
|
+
color: Uniform surface color. Mutually exclusive with intensity_fn.
|
|
46
|
+
opacity: Surface opacity (0-1).
|
|
47
|
+
name: Trace name for legend.
|
|
48
|
+
colorscale: Plotly colorscale name or custom colorscale list
|
|
49
|
+
(used with intensity_fn).
|
|
50
|
+
colorbar_title: Title for colorbar (used with intensity_fn).
|
|
51
|
+
showscale: Whether to show the colorbar.
|
|
52
|
+
**kwargs: Additional arguments for go.Mesh3d.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Plotly Mesh3d trace.
|
|
56
|
+
"""
|
|
57
|
+
vertices = np.asarray(vertices)
|
|
58
|
+
faces = np.asarray(faces)
|
|
59
|
+
|
|
60
|
+
trace_kwargs: dict = {
|
|
61
|
+
"x": vertices[:, 0],
|
|
62
|
+
"y": vertices[:, 1],
|
|
63
|
+
"z": vertices[:, 2],
|
|
64
|
+
"i": faces[:, 0],
|
|
65
|
+
"j": faces[:, 1],
|
|
66
|
+
"k": faces[:, 2],
|
|
67
|
+
"opacity": opacity,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if color is not None:
|
|
71
|
+
trace_kwargs["color"] = color
|
|
72
|
+
else:
|
|
73
|
+
if intensity_fn is not None:
|
|
74
|
+
intensity = intensity_fn(vertices)
|
|
75
|
+
else:
|
|
76
|
+
centroid = vertices.mean(axis=0)
|
|
77
|
+
intensity = np.linalg.norm(vertices - centroid, axis=-1)
|
|
78
|
+
|
|
79
|
+
trace_kwargs["intensity"] = intensity
|
|
80
|
+
trace_kwargs["colorscale"] = colorscale
|
|
81
|
+
trace_kwargs["showscale"] = showscale
|
|
82
|
+
if colorbar_title is not None:
|
|
83
|
+
trace_kwargs["colorbar"] = {"title": colorbar_title}
|
|
84
|
+
|
|
85
|
+
if name is not None:
|
|
86
|
+
trace_kwargs["name"] = name
|
|
87
|
+
trace_kwargs["showlegend"] = True
|
|
88
|
+
|
|
89
|
+
trace_kwargs.update(kwargs)
|
|
90
|
+
return go.Mesh3d(**trace_kwargs)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def grid_to_mesh(
|
|
94
|
+
x: NDArray[np.floating],
|
|
95
|
+
y: NDArray[np.floating],
|
|
96
|
+
z: NDArray[np.floating],
|
|
97
|
+
) -> tuple[NDArray[np.floating], NDArray[np.integer]]:
|
|
98
|
+
"""Convert a 2D grid of 3D points to vertices and triangular faces.
|
|
99
|
+
|
|
100
|
+
Used for parametric surfaces where x, y, z are 2D arrays defining
|
|
101
|
+
a surface patch.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
x: X coordinates, 2D array of shape (M, N).
|
|
105
|
+
y: Y coordinates, 2D array of shape (M, N).
|
|
106
|
+
z: Z coordinates, 2D array of shape (M, N).
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Tuple of (vertices, faces) where:
|
|
110
|
+
- vertices: shape (M*N, 3)
|
|
111
|
+
- faces: shape ((M-1)*(N-1)*2, 3)
|
|
112
|
+
"""
|
|
113
|
+
m, n = x.shape
|
|
114
|
+
vertices = np.column_stack([x.ravel(), y.ravel(), z.ravel()])
|
|
115
|
+
|
|
116
|
+
faces = []
|
|
117
|
+
for i in range(m - 1):
|
|
118
|
+
for j in range(n - 1):
|
|
119
|
+
idx00 = i * n + j
|
|
120
|
+
idx01 = i * n + (j + 1)
|
|
121
|
+
idx10 = (i + 1) * n + j
|
|
122
|
+
idx11 = (i + 1) * n + (j + 1)
|
|
123
|
+
|
|
124
|
+
faces.append([idx00, idx10, idx11])
|
|
125
|
+
faces.append([idx00, idx11, idx01])
|
|
126
|
+
|
|
127
|
+
return vertices, np.array(faces, dtype=np.int64)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
__all__ = [
|
|
131
|
+
"grid_to_mesh",
|
|
132
|
+
"vertices_to_mesh3d",
|
|
133
|
+
]
|
|
@@ -21,7 +21,6 @@ dependencies = [
|
|
|
21
21
|
"gri-utils>=0.3.3",
|
|
22
22
|
"numpy>=2.3.3",
|
|
23
23
|
"plotly>=6.3.0",
|
|
24
|
-
"scikit-image>=0.25.0",
|
|
25
24
|
]
|
|
26
25
|
|
|
27
26
|
[tool.uv.sources]
|
|
@@ -63,5 +62,13 @@ dev = [
|
|
|
63
62
|
"ruff>=0.13.1",
|
|
64
63
|
]
|
|
65
64
|
|
|
65
|
+
[tool.coverage.run]
|
|
66
|
+
source = ["gri_plot"]
|
|
67
|
+
branch = true
|
|
68
|
+
|
|
66
69
|
[tool.coverage.report]
|
|
67
|
-
fail_under =
|
|
70
|
+
fail_under = 50
|
|
71
|
+
skip_empty = true
|
|
72
|
+
|
|
73
|
+
[tool.coverage.html]
|
|
74
|
+
directory = ".cov_html"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?><testsuites name="pytest tests"><testsuite name="pytest" errors="0" failures="0" skipped="24" tests="109" time="6.356" timestamp="2026-04-18T04:32:03.689023+00:00" hostname="runner-xs6vzpvoq-project-74598118-concurrent-0"><testcase classname="test.test_figure3d" name="test_enu_requires_origin" time="0.002" /><testcase classname="test.test_figure3d" name="test_build_returns_plotly_figure" time="0.988" /><testcase classname="test.test_figure3d" name="test_plot_surfaces_convenience" time="0.050" /><testcase classname="test.test_figure3d" name="test_visual_ellipsoid_enu" time="0.001"><skipped type="pytest.skip" message="Visual test - unskip to generate plot">/builds/geosol-foss/python/gri-plot/test/test_figure3d.py:39: Visual test - unskip to generate plot</skipped></testcase><testcase classname="test.test_figure3d" name="test_visual_multiple_spheres" time="0.000"><skipped type="pytest.skip" message="Visual test - unskip to generate plot">/builds/geosol-foss/python/gri-plot/test/test_figure3d.py:52: Visual test - unskip to generate plot</skipped></testcase><testcase classname="test.test_figure3d" name="test_visual_combined_surfaces" time="0.000"><skipped type="pytest.skip" message="Visual test - unskip to generate plot">/builds/geosol-foss/python/gri-plot/test/test_figure3d.py:64: Visual test - unskip to generate plot</skipped></testcase><testcase classname="test.test_figure_map" name="test_default_init" time="0.001" /><testcase classname="test.test_figure_map" name="test_blank_enu_requires_origin" time="0.001" /><testcase classname="test.test_figure_map" name="test_blank_enu_with_origin" time="0.001" /><testcase classname="test.test_figure_map" name="test_build_satellite_returns_figure" time="0.043" /><testcase classname="test.test_figure_map" name="test_build_blank_returns_figure" time="0.042" /><testcase classname="test.test_figure_map" name="test_build_with_title" time="0.036" /><testcase classname="test.test_figure_map" name="test_add_contour_returns_self" time="0.003" /><testcase classname="test.test_figure_map" name="test_add_contour_satellite_creates_scattermap" time="0.038" /><testcase classname="test.test_figure_map" name="test_add_contour_blank_creates_scatter" time="0.038" /><testcase classname="test.test_figure_map" name="test_add_contours_returns_self" time="0.002" /><testcase classname="test.test_figure_map" name="test_add_contours_creates_separate_traces" time="0.038" /><testcase classname="test.test_figure_map" name="test_add_contours_empty_list" time="0.037" /><testcase classname="test.test_figure_map" name="test_add_band_returns_self" time="0.009" /><testcase classname="test.test_figure_map" name="test_add_band_creates_filled_trace" time="0.044" /><testcase classname="test.test_figure_map" name="test_add_band_blank_mode" time="0.042" /><testcase classname="test.test_figure_map" name="test_add_band_empty_list" time="0.036" /><testcase classname="test.test_figure_map" name="test_add_filled_contour_returns_self" time="0.002" /><testcase classname="test.test_figure_map" name="test_add_filled_contour_creates_fill" time="0.037" /><testcase classname="test.test_figure_map" name="test_add_markers_returns_self" time="0.002" /><testcase classname="test.test_figure_map" name="test_add_markers_satellite" time="0.040" /><testcase classname="test.test_figure_map" name="test_add_markers_blank" time="0.038" /><testcase classname="test.test_figure_map" name="test_add_markers_single_point" time="0.039" /><testcase classname="test.test_figure_map" name="test_full_chaining" time="0.050" /><testcase classname="test.test_figure_map" name="test_auto_color_cycling" time="0.040" /><testcase classname="test.test_figure_map" name="test_visual_satellite_contours" time="0.001"><skipped type="pytest.skip" message="Visual test - unskip to inspect satellite map">/builds/geosol-foss/python/gri-plot/test/test_figure_map.py:308: Visual test - unskip to inspect satellite map</skipped></testcase><testcase classname="test.test_figure_map" name="test_visual_blank_enu" time="0.001"><skipped type="pytest.skip" message="Visual test - unskip to inspect blank ENU plot">/builds/geosol-foss/python/gri-plot/test/test_figure_map.py:321: Visual test - unskip to inspect blank ENU plot</skipped></testcase><testcase classname="test.test_frames" name="test_xyz_lla_roundtrip" time="0.001" /><testcase classname="test.test_frames" name="test_enu_requires_origin" time="0.001" /><testcase classname="test.test_frames" name="test_enu_roundtrip" time="0.001" /><testcase classname="test.test_frames" name="test_axis_labels" time="0.001" /><testcase classname="test.test_frames" name="test_axis_labels_enu" time="0.001" /><testcase classname="test.test_frames" name="test_xyz_to_display_is_identity" time="0.001" /><testcase classname="test.test_frames" name="test_lla_roundtrip_via_transformer" time="0.001" /><testcase classname="test.test_frames" name="test_transform_bounds_xyz_passthrough" time="0.001" /><testcase classname="test.test_frames" name="test_transform_bounds_enu_orders_corners" time="0.002" /><testcase classname="test.test_frames" name="test_transform_bounds_lla_samples_all_corners" time="0.001" /><testcase classname="test.test_observables.test_aoa" name="test_residual_on_axis_inside" time="0.002" /><testcase classname="test.test_observables.test_aoa" name="test_residual_perpendicular_outside" time="0.001" /><testcase classname="test.test_observables.test_aoa" name="test_invalid_max_range" time="0.001" /><testcase classname="test.test_observables.test_aoa" name="test_visual_aoa_cone" time="0.001"><skipped type="pytest.skip" message="Visual test - unskip to generate plot">/builds/geosol-foss/python/gri-plot/test/test_observables/test_aoa.py:40: Visual test - unskip to generate plot</skipped></testcase><testcase classname="test.test_observables.test_los" name="test_residual_inside_outside" time="0.001" /><testcase classname="test.test_observables.test_los" name="test_invalid_length" time="0.001" /><testcase classname="test.test_observables.test_los" name="test_visual_los_ray" time="0.001"><skipped type="pytest.skip" message="Visual test - unskip to generate plot">/builds/geosol-foss/python/gri-plot/test/test_observables/test_los.py:32: Visual test - unskip to generate plot</skipped></testcase><testcase classname="test.test_observables.test_range_sphere" name="test_residual_on_surface" time="0.001" /><testcase classname="test.test_observables.test_range_sphere" name="test_invalid_range" time="0.001" /><testcase classname="test.test_observables.test_range_sphere" name="test_visual_range_sphere" time="0.001"><skipped type="pytest.skip" message="Visual test - unskip to generate plot">/builds/geosol-foss/python/gri-plot/test/test_observables/test_range_sphere.py:25: Visual test - unskip to generate plot</skipped></testcase><testcase classname="test.test_plot_ellipse" name="test_returns_figure" time="0.047" /><testcase classname="test.test_plot_ellipse" name="test_requires_at_least_one_ellipse" time="0.002" /><testcase classname="test.test_plot_ellipse" name="test_multiple_ellipses_create_traces" time="0.044" /><testcase classname="test.test_plot_ellipse" name="test_show_axes_false_reduces_traces" time="0.075" /><testcase classname="test.test_plot_ellipse" name="test_visual_single_ellipse" time="0.001"><skipped type="pytest.skip" message="Visual test - unskip to view">/builds/geosol-foss/python/gri-plot/test/test_plot_ellipse.py:40: Visual test - unskip to view</skipped></testcase><testcase classname="test.test_plot_ellipse" name="test_visual_multiple_ellipses" time="0.001"><skipped type="pytest.skip" message="Visual test - unskip to view">/builds/geosol-foss/python/gri-plot/test/test_plot_ellipse.py:48: Visual test - unskip to view</skipped></testcase><testcase classname="test.test_plot_scattermap" name="test_map_plot_single" time="0.001"><skipped type="pytest.skip" message="manual only for demonstration and test">/builds/geosol-foss/python/gri-plot/test/test_plot_scattermap.py:6: manual only for demonstration and test</skipped></testcase><testcase classname="test.test_plot_scattermap" name="test_map_plot_mult" time="0.001"><skipped type="pytest.skip" message="manual only for demonstration and test">/builds/geosol-foss/python/gri-plot/test/test_plot_scattermap.py:23: manual only for demonstration and test</skipped></testcase><testcase classname="test.test_scatter" name="test_single" time="0.001"><skipped type="pytest.skip" message="manual only for demonstration and test">/builds/geosol-foss/python/gri-plot/test/test_scatter.py:7: manual only for demonstration and test</skipped></testcase><testcase classname="test.test_scatter" name="test_double" time="0.001"><skipped type="pytest.skip" message="manual only for demonstration and test">/builds/geosol-foss/python/gri-plot/test/test_scatter.py:14: manual only for demonstration and test</skipped></testcase><testcase classname="test.test_scatter" name="test_x_space" time="0.000"><skipped type="pytest.skip" message="manual only for demonstration and test">/builds/geosol-foss/python/gri-plot/test/test_scatter.py:22: manual only for demonstration and test</skipped></testcase><testcase classname="test.test_scatter" name="test_sequence" time="0.001"><skipped type="pytest.skip" message="manual only for demonstration and test">/builds/geosol-foss/python/gri-plot/test/test_scatter.py:30: manual only for demonstration and test</skipped></testcase><testcase classname="test.test_scatter" name="test_kwargs" time="0.001"><skipped type="pytest.skip" message="manual only for demonstration and test">/builds/geosol-foss/python/gri-plot/test/test_scatter.py:38: manual only for demonstration and test</skipped></testcase><testcase classname="test.test_scatter" name="test_trace_kwargs" time="0.001"><skipped type="pytest.skip" message="manual only for demonstration and test">/builds/geosol-foss/python/gri-plot/test/test_scatter.py:46: manual only for demonstration and test</skipped></testcase><testcase classname="test.test_scatter" name="test_trace_kwargs2" time="0.001"><skipped type="pytest.skip" message="manual only for demonstration and test">/builds/geosol-foss/python/gri-plot/test/test_scatter.py:54: manual only for demonstration and test</skipped></testcase><testcase classname="test.test_shapes.test_cone" name="test_residual_inside_outside" time="0.002" /><testcase classname="test.test_shapes.test_cone" name="test_invalid_parameters[0.0-100.0]" time="0.001" /><testcase classname="test.test_shapes.test_cone" name="test_invalid_parameters[1.5707963267948966-100.0]" time="0.001" /><testcase classname="test.test_shapes.test_cone" name="test_invalid_parameters[0.5-0.0]" time="0.001" /><testcase classname="test.test_shapes.test_cone" name="test_invalid_parameters[0.5--10.0]" time="0.001" /><testcase classname="test.test_shapes.test_cone" name="test_visual_cone" time="0.001"><skipped type="pytest.skip" message="Visual test - unskip to generate plot">/builds/geosol-foss/python/gri-plot/test/test_shapes/test_cone.py:41: Visual test - unskip to generate plot</skipped></testcase><testcase classname="test.test_shapes.test_cylinder" name="test_residual_inside_outside" time="0.001" /><testcase classname="test.test_shapes.test_cylinder" name="test_invalid_parameters[0.0-100.0]" time="0.001" /><testcase classname="test.test_shapes.test_cylinder" name="test_invalid_parameters[-10.0-100.0]" time="0.001" /><testcase classname="test.test_shapes.test_cylinder" name="test_invalid_parameters[25.0-0.0]" time="0.001" /><testcase classname="test.test_shapes.test_cylinder" name="test_invalid_parameters[25.0--10.0]" time="0.001" /><testcase classname="test.test_shapes.test_cylinder" name="test_visual_cylinder" time="0.001"><skipped type="pytest.skip" message="Visual test - unskip to generate plot">/builds/geosol-foss/python/gri-plot/test/test_shapes/test_cylinder.py:44: Visual test - unskip to generate plot</skipped></testcase><testcase classname="test.test_shapes.test_ellipse" name="test_properties_stored" time="0.001" /><testcase classname="test.test_shapes.test_ellipse" name="test_center_defaults_to_origin" time="0.001" /><testcase classname="test.test_shapes.test_ellipse" name="test_ori_deg_normalized[370.0-10.0]" time="0.001" /><testcase classname="test.test_shapes.test_ellipse" name="test_ori_deg_normalized[-10.0-350.0]" time="0.001" /><testcase classname="test.test_shapes.test_ellipse" name="test_ori_deg_normalized[0.0-0.0]" time="0.001" /><testcase classname="test.test_shapes.test_ellipse" name="test_ori_deg_normalized[360.0-0.0]" time="0.001" /><testcase classname="test.test_shapes.test_ellipse" name="test_ori_deg_normalized[720.0-0.0]" time="0.001" /><testcase classname="test.test_shapes.test_ellipse" name="test_rejects_non_positive_axes[0-50]" time="0.001" /><testcase classname="test.test_shapes.test_ellipse" name="test_rejects_non_positive_axes[-10-50]" time="0.001" /><testcase classname="test.test_shapes.test_ellipse" name="test_rejects_non_positive_axes[100-0]" time="0.001" /><testcase classname="test.test_shapes.test_ellipse" name="test_rejects_non_positive_axes[100--5]" time="0.001" /><testcase classname="test.test_shapes.test_ellipse" name="test_rejects_sma_less_than_smi" time="0.001" /><testcase classname="test.test_shapes.test_ellipse" name="test_boundary_shape" time="0.001" /><testcase classname="test.test_shapes.test_ellipse" name="test_boundary_extent_at_zero_orientation" time="0.001" /><testcase classname="test.test_shapes.test_ellipse" name="test_boundary_respects_center" time="0.001" /><testcase classname="test.test_shapes.test_ellipse" name="test_repr" time="0.001" /><testcase classname="test.test_shapes.test_ellipsoid" name="test_residual_on_surface" time="0.001" /><testcase classname="test.test_shapes.test_ellipsoid" name="test_requires_covariance_or_semi_axes" time="0.001" /><testcase classname="test.test_shapes.test_ellipsoid" name="test_rejects_both_covariance_and_semi_axes" time="0.001" /><testcase classname="test.test_shapes.test_ellipsoid" name="test_visual_ellipsoid_from_covariance" time="0.001"><skipped type="pytest.skip" message="Visual test - unskip to generate plot">/builds/geosol-foss/python/gri-plot/test/test_shapes/test_ellipsoid.py:31: Visual test - unskip to generate plot</skipped></testcase><testcase classname="test.test_shapes.test_ellipsoid" name="test_visual_ellipsoid_from_semi_axes" time="0.001"><skipped type="pytest.skip" message="Visual test - unskip to generate plot">/builds/geosol-foss/python/gri-plot/test/test_shapes/test_ellipsoid.py:42: Visual test - unskip to generate plot</skipped></testcase><testcase classname="test.test_shapes.test_sphere" name="test_residual_fn[point_offset0-0]" time="0.001" /><testcase classname="test.test_shapes.test_sphere" name="test_residual_fn[point_offset1--1]" time="0.001" /><testcase classname="test.test_shapes.test_sphere" name="test_residual_fn[point_offset2-1]" time="0.002" /><testcase classname="test.test_shapes.test_sphere" name="test_invalid_radius" time="0.001" /><testcase classname="test.test_shapes.test_sphere" name="test_visual_sphere" time="0.001"><skipped type="pytest.skip" message="Visual test - unskip to generate plot">/builds/geosol-foss/python/gri-plot/test/test_shapes/test_sphere.py:37: Visual test - unskip to generate plot</skipped></testcase><testcase classname="test.test_surfaces.test_mesh" name="test_vertices_to_mesh3d_returns_mesh3d" time="0.002" /><testcase classname="test.test_surfaces.test_mesh" name="test_vertices_to_mesh3d_uniform_color" time="0.003" /><testcase classname="test.test_surfaces.test_mesh" name="test_vertices_to_mesh3d_intensity_fn_applied" time="0.002" /><testcase classname="test.test_surfaces.test_mesh" name="test_grid_to_mesh_produces_expected_shape" time="0.001" /></testsuite></testsuites>
|