gri-plot 0.2.2__tar.gz → 0.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gri_plot-0.2.2 → gri_plot-0.2.4}/.docs_other_projects.md +10 -6
- {gri_plot-0.2.2 → gri_plot-0.2.4}/.init_venv.sh +8 -3
- {gri_plot-0.2.2 → gri_plot-0.2.4}/PKG-INFO +7 -18
- {gri_plot-0.2.2 → gri_plot-0.2.4}/README.md +5 -15
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/__init__.py +5 -26
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/figure3d.py +3 -83
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/figure_map.py +38 -14
- gri_plot-0.2.4/gri_plot/observables/__init__.py +15 -0
- {gri_plot-0.2.2 → 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.2 → gri_plot-0.2.4}/pyproject.toml +10 -3
- 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.2 → gri_plot-0.2.4}/uv.lock +0 -175
- gri_plot-0.2.2/.coveragerc +0 -8
- gri_plot-0.2.2/gri_plot/observables/__init__.py +0 -21
- gri_plot-0.2.2/gri_plot/observables/fdoa.py +0 -256
- gri_plot-0.2.2/gri_plot/observables/tdoa.py +0 -203
- gri_plot-0.2.2/gri_plot/observables/terrain.py +0 -295
- gri_plot-0.2.2/gri_plot/surfaces/gradients/__init__.py +0 -39
- gri_plot-0.2.2/gri_plot/surfaces/gradients/axis.py +0 -76
- gri_plot-0.2.2/gri_plot/surfaces/gradients/line.py +0 -88
- gri_plot-0.2.2/gri_plot/surfaces/gradients/plane.py +0 -55
- gri_plot-0.2.2/gri_plot/surfaces/gradients/point.py +0 -42
- gri_plot-0.2.2/gri_plot/surfaces/intersection.py +0 -219
- gri_plot-0.2.2/gri_plot/surfaces/mesh.py +0 -605
- gri_plot-0.2.2/report.xml +0 -1
- gri_plot-0.2.2/test/test_figure3d.py +0 -287
- gri_plot-0.2.2/test/test_frames.py +0 -44
- gri_plot-0.2.2/test/test_observables/test_fdoa.py +0 -59
- gri_plot-0.2.2/test/test_observables/test_tdoa.py +0 -72
- gri_plot-0.2.2/test/test_observables/test_terrain.py +0 -45
- gri_plot-0.2.2/test/test_surfaces/test_intersection.py +0 -203
- gri_plot-0.2.2/test/test_surfaces/test_mesh.py +0 -239
- {gri_plot-0.2.2 → gri_plot-0.2.4}/.gitignore +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/.gitlab-ci-deps.yml +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/.gitlab-ci.yml +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/.pre-commit-config.yaml +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/.pre-commit-unskipped.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/.python-version +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/.ruff.toml +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/.vscode/settings.json +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/CONTRIBUTING.md +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/LICENSE +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/frames.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/observables/aoa.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/observables/los.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/observables/range_sphere.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/plot_ellipse.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/py.typed +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/scatter.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/scatter_map.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/shapes/__init__.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/shapes/cone.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/shapes/cylinder.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/shapes/ellipse.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/shapes/ellipsoid.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/shapes/meshgen.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/gri_plot/shapes/sphere.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/notes.txt +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/test/__init__.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/test/test_figure_map.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/test/test_observables/__init__.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/test/test_observables/test_aoa.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/test/test_observables/test_los.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/test/test_observables/test_range_sphere.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/test/test_plot_ellipse.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/test/test_plot_scattermap.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/test/test_scatter.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/test/test_shapes/__init__.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/test/test_shapes/test_cone.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/test/test_shapes/test_cylinder.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/test/test_shapes/test_ellipse.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/test/test_shapes/test_ellipsoid.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/test/test_shapes/test_sphere.py +0 -0
- {gri_plot-0.2.2 → gri_plot-0.2.4}/test/test_surfaces/__init__.py +0 -0
|
@@ -16,10 +16,6 @@ Equation fitting wrapper around scipy.optimize.curve\_fit. Simplifies curve fitt
|
|
|
16
16
|
|
|
17
17
|
Class-based memoization with full type hints and IDE integration. Provides caching decorators for performance optimization.
|
|
18
18
|
|
|
19
|
-
### [GRI Plot](https://gitlab.com/geosol-foss/python/gri-plot)
|
|
20
|
-
|
|
21
|
-
Plotting utilities for Plotly and Matplotlib. Provides consistent styling and common plot types for geospatial visualization.
|
|
22
|
-
|
|
23
19
|
### [GRI Signal](https://gitlab.com/geosol-foss/python/gri-signal)
|
|
24
20
|
|
|
25
21
|
Functional signal processing library with pure functions operating on numpy arrays. Includes signal generation (tones, PRN, pulsed), channel simulation (delay, AWGN, Doppler, multipath), modulation (AM/FM/PM, BPSK/QPSK/QAM), sampling (ADC), filtering (lowpass, highpass, bandpass, notch, analytic), spectral analysis (PSD, SNR, THD, time-bandwidth product), cross-correlation, and beamforming.
|
|
@@ -46,6 +42,14 @@ Solar flux and NeQuickG coefficient data pipeline for ionospheric modeling.
|
|
|
46
42
|
|
|
47
43
|
A lot of the work we do is down to nanosecond or tens of nanosecond accuracy. We needed an object that could both handle that level of precision as well as do a lot of the string <=> time conversions for the many semi-standards formats we run into in this field.
|
|
48
44
|
|
|
45
|
+
### [GRI Obs](https://gitlab.com/geosol-foss/python/gri-obs)
|
|
46
|
+
|
|
47
|
+
Typed observation objects (TDOA, FDOA, AOA, Range, PDOA) for geolocation measurements. Each observation carries measurement data, noise characterization, sensor geometry, and measurement math (predicted values, Jacobians, noise covariance) for EKF integration with Kalman filters.
|
|
48
|
+
|
|
49
|
+
### [GRI Plot](https://gitlab.com/geosol-foss/python/gri-plot)
|
|
50
|
+
|
|
51
|
+
Plotting utilities for Plotly and Matplotlib. Provides consistent styling and common plot types for geospatial visualization.
|
|
52
|
+
|
|
49
53
|
### [GRI Pos](https://gitlab.com/geosol-foss/python/gri-pos)
|
|
50
54
|
|
|
51
55
|
A position object that greatly simplifies handling coordinates, distances, and memory, mostly on the WGS-84 ellipsoid.
|
|
@@ -66,7 +70,7 @@ ERA5 data pipeline for downloading and processing tropospheric refractivity data
|
|
|
66
70
|
|
|
67
71
|
### [GRI Convolve](https://gitlab.com/geosol-foss/python/gri-convolve)
|
|
68
72
|
|
|
69
|
-
Ellipsoid convolution with outlier detection and clustering. Combines multiple statistical position estimates into refined solutions.
|
|
73
|
+
Ellipsoid convolution with outlier detection and clustering, plus Kalman tracking via Interacting Multiple Model (IMM) filter. Combines multiple statistical position estimates into refined solutions. The Kalman tracker supports both ellipsoid-based and raw observable (EKF) updates, bidirectional outlier detection, and online track segmentation.
|
|
70
74
|
|
|
71
75
|
### [GRI GeoSim](https://gitlab.com/geosol-foss/python/gri-geosim)
|
|
72
76
|
|
|
@@ -78,4 +82,4 @@ Some neat (small) examples and projects we've put together over time using the a
|
|
|
78
82
|
|
|
79
83
|
### [GRI SigSim](https://gitlab.com/geosol-foss/python/gri-sigsim)
|
|
80
84
|
|
|
81
|
-
Object-based RF signal simulation library. Wraps gri-signal functions with chainable object-oriented classes for waveforms (
|
|
85
|
+
Object-based RF signal simulation library. Wraps gri-signal functions with chainable object-oriented classes for waveforms (pulsed, PRN, tone), channel modeling (delay, Doppler, multipath), receiver simulation, and TOA estimation.
|
|
@@ -65,10 +65,15 @@ if [ $? -ne 0 ]; then
|
|
|
65
65
|
fi
|
|
66
66
|
uv sync
|
|
67
67
|
|
|
68
|
-
# activate and print version
|
|
68
|
+
# activate and print version (Scripts/ on Windows, bin/ elsewhere)
|
|
69
69
|
echo "${bold}Activating environment${normal}"
|
|
70
|
-
|
|
71
|
-
$VENV/
|
|
70
|
+
if [[ -d "$VENV/Scripts" ]]; then
|
|
71
|
+
. $VENV/Scripts/activate
|
|
72
|
+
$VENV/Scripts/python --version
|
|
73
|
+
else
|
|
74
|
+
. $VENV/bin/activate
|
|
75
|
+
$VENV/bin/python --version
|
|
76
|
+
fi
|
|
72
77
|
|
|
73
78
|
# Install pre-commit scripts
|
|
74
79
|
echo "${bold}Installing git hooks${normal}"
|
|
@@ -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
|
|
@@ -17,10 +17,9 @@ Classifier: Programming Language :: Python :: 3
|
|
|
17
17
|
Classifier: Topic :: Scientific/Engineering
|
|
18
18
|
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
19
19
|
Requires-Python: >=3.12
|
|
20
|
-
Requires-Dist: gri-utils>=0.
|
|
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:
|
|
@@ -241,11 +241,9 @@ class FigureMap:
|
|
|
241
241
|
showlegend=show_legend and label is not None,
|
|
242
242
|
)
|
|
243
243
|
else:
|
|
244
|
-
x_vals = display_coords[:, 0]
|
|
245
|
-
y_vals = display_coords[:, 1]
|
|
246
244
|
trace = go.Scatter(
|
|
247
|
-
x=
|
|
248
|
-
y=
|
|
245
|
+
x=display_coords[:, 1],
|
|
246
|
+
y=display_coords[:, 0],
|
|
249
247
|
mode="lines",
|
|
250
248
|
line={"color": color, "width": width},
|
|
251
249
|
name=label,
|
|
@@ -278,8 +276,8 @@ class FigureMap:
|
|
|
278
276
|
)
|
|
279
277
|
else:
|
|
280
278
|
trace = go.Scatter(
|
|
281
|
-
x=display_coords[:,
|
|
282
|
-
y=display_coords[:,
|
|
279
|
+
x=display_coords[:, 1],
|
|
280
|
+
y=display_coords[:, 0],
|
|
283
281
|
mode="lines",
|
|
284
282
|
fill="toself",
|
|
285
283
|
fillcolor=fill_color,
|
|
@@ -478,21 +476,40 @@ class FigureMap:
|
|
|
478
476
|
self._add_line_trace(display, color, width, label, show_legend=True)
|
|
479
477
|
return self
|
|
480
478
|
|
|
481
|
-
def add_heatmap(
|
|
479
|
+
def add_heatmap( # noqa: PLR0913
|
|
482
480
|
self,
|
|
483
481
|
sheet_xyz: NDArray[np.floating],
|
|
482
|
+
*,
|
|
483
|
+
values: NDArray[np.floating] | None = None,
|
|
484
|
+
lat_deg: NDArray[np.floating] | None = None,
|
|
485
|
+
lon_deg: NDArray[np.floating] | None = None,
|
|
484
486
|
colorscale: str = "earth",
|
|
485
487
|
opacity: float = 0.6,
|
|
486
488
|
label: str | None = None,
|
|
487
489
|
) -> FigureMap:
|
|
488
|
-
"""Add a
|
|
490
|
+
"""Add a heatmap over a spatial grid.
|
|
491
|
+
|
|
492
|
+
Renders a 2D grid as a colored heatmap. When ``values`` is None,
|
|
493
|
+
color represents the altitude of each grid point. When ``values``
|
|
494
|
+
is provided, it is used as the color field instead (e.g. TDOA,
|
|
495
|
+
TDOA-dot, or any scalar quantity defined on the grid).
|
|
496
|
+
|
|
497
|
+
When ``lat_deg`` and ``lon_deg`` are provided they are used
|
|
498
|
+
directly as the axis coordinates, bypassing extraction from
|
|
499
|
+
``sheet_xyz``. This is useful when the sheet contains NaN cells
|
|
500
|
+
(e.g. a visibility-masked grid) that would corrupt the
|
|
501
|
+
coordinate recovery.
|
|
489
502
|
|
|
490
|
-
|
|
491
|
-
the altitude (Z-height) of each grid point. Only supported in
|
|
492
|
-
BLANK mode.
|
|
503
|
+
Only supported in BLANK mode.
|
|
493
504
|
|
|
494
505
|
Args:
|
|
495
506
|
sheet_xyz: Terrain sheet in ECEF meters, shape (M, N, 3).
|
|
507
|
+
values: Optional scalar field to color by, shape (M, N).
|
|
508
|
+
When None, altitude is used.
|
|
509
|
+
lat_deg: Optional latitude axis, shape (M,). When None,
|
|
510
|
+
extracted from sheet_xyz.
|
|
511
|
+
lon_deg: Optional longitude axis, shape (N,). When None,
|
|
512
|
+
extracted from sheet_xyz.
|
|
496
513
|
colorscale: Plotly colorscale name. Default "earth".
|
|
497
514
|
opacity: Heatmap opacity (0-1). Default 0.6.
|
|
498
515
|
label: Colorbar title.
|
|
@@ -510,7 +527,11 @@ class FigureMap:
|
|
|
510
527
|
sheet_xyz = np.asarray(sheet_xyz, dtype=np.float64)
|
|
511
528
|
m, n, _ = sheet_xyz.shape
|
|
512
529
|
|
|
513
|
-
if
|
|
530
|
+
if lat_deg is not None and lon_deg is not None:
|
|
531
|
+
x_vals = np.asarray(lon_deg, dtype=np.float64)
|
|
532
|
+
y_vals = np.asarray(lat_deg, dtype=np.float64)
|
|
533
|
+
z_vals = values
|
|
534
|
+
elif self._frame == "enu":
|
|
514
535
|
assert self._origin_xyz is not None # noqa: S101
|
|
515
536
|
flat = sheet_xyz.reshape(-1, 3)
|
|
516
537
|
enu = get_enu(self._origin_xyz, flat)
|
|
@@ -526,6 +547,9 @@ class FigureMap:
|
|
|
526
547
|
y_vals = lats
|
|
527
548
|
z_vals = alts
|
|
528
549
|
|
|
550
|
+
if values is not None:
|
|
551
|
+
z_vals = np.asarray(values, dtype=np.float64)
|
|
552
|
+
|
|
529
553
|
trace = go.Heatmap(
|
|
530
554
|
x=x_vals,
|
|
531
555
|
y=y_vals,
|
|
@@ -580,8 +604,8 @@ class FigureMap:
|
|
|
580
604
|
self._traces.append(go.Scattermap(**trace_kwargs))
|
|
581
605
|
else:
|
|
582
606
|
trace_kwargs = {
|
|
583
|
-
"x": display[:,
|
|
584
|
-
"y": display[:,
|
|
607
|
+
"x": display[:, 1],
|
|
608
|
+
"y": display[:, 0],
|
|
585
609
|
"mode": mode,
|
|
586
610
|
"marker": {"size": size, "color": color},
|
|
587
611
|
"showlegend": False,
|
|
@@ -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
|
]
|