test new version
This commit is contained in:
@@ -1,14 +1,18 @@
|
||||
"""3D SAR projection rendering helpers.
|
||||
|
||||
The sar3d processor publishes two normalized max-projections of the imaged
|
||||
volume as collection-level ImageF32 payloads:
|
||||
volume as collection-level ImageF32 payloads, matching the legacy render
|
||||
conventions (X across, Z range/"вдаль", Y up):
|
||||
|
||||
- ``sar3d_proj_xz`` — side view: X (m) horizontal, Z/range (m) vertical;
|
||||
- ``sar3d_proj_xy`` — top view: X (m) horizontal, Y (m) vertical;
|
||||
- ``sar3d_proj_xz`` — top view: X (m) horizontal, Z/range (m) vertical
|
||||
(max over the height Y), like the classic GPR heatmap;
|
||||
- ``sar3d_proj_bottom`` — switchable bottom view: height Y (m) vertical, the
|
||||
horizontal axis is the ground direction rotated by ``sar3d_bottom_view_deg``
|
||||
around Y (0° = front view X, 90° = side view Z, 30°/60° in between);
|
||||
|
||||
plus ``sar3d_peak`` (ScalarF32), the volume peak before normalization. Both
|
||||
projections are drawn on their own PlotWidgets stacked in one plot-stack page
|
||||
(side view on top), mirroring how the pass-through page stacks its two plots.
|
||||
(top view on top), mirroring how the pass-through page stacks its two plots.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -17,23 +21,39 @@ from PyQt6.QtCore import QRectF
|
||||
import numpy as np
|
||||
import pyqtgraph as pg
|
||||
|
||||
from python_app.gui.runtime.sar3d_views import sar3d_bottom_view_deg_from_label
|
||||
from python_app.models.dataset_model import ResultCollection
|
||||
|
||||
|
||||
class AppWindowSar3dPlotMixin:
|
||||
"""Renders 3D SAR volume projections published by the sar3d processor."""
|
||||
|
||||
def _sar3d_bottom_view_labels(self) -> tuple[str, str]:
|
||||
"""Return (plot title, horizontal axis label) for the active bottom view.
|
||||
|
||||
The plot surfaces are created before the processing section builds the
|
||||
bottom-view combo, so fall back to the front view until it exists.
|
||||
"""
|
||||
combo = getattr(self, "_sar3d_bottom_view", None)
|
||||
degrees = sar3d_bottom_view_deg_from_label(combo.currentText()) if combo is not None else 0.0
|
||||
if degrees <= 1e-9:
|
||||
return "3D SAR front view (X-Y)", "X"
|
||||
if degrees >= 90.0 - 1e-9:
|
||||
return "3D SAR side view (Z-Y)", "Range Z"
|
||||
return f"3D SAR view @ {degrees:g}\N{DEGREE SIGN}", f"u({degrees:g}\N{DEGREE SIGN})"
|
||||
|
||||
def _configure_sar3d_plot_axes(self) -> None:
|
||||
"""Apply persistent axis labels and view settings to both projection plots."""
|
||||
for plot, y_label in (
|
||||
(self._sar3d_xz_plot, "Range Z"),
|
||||
(self._sar3d_xy_plot, "Cross-range Y"),
|
||||
_bottom_title, bottom_x_label = self._sar3d_bottom_view_labels()
|
||||
for plot, x_label, y_label in (
|
||||
(self._sar3d_xz_plot, "X", "Range Z"),
|
||||
(self._sar3d_xy_plot, bottom_x_label, "Height Y"),
|
||||
):
|
||||
plot_item = plot.getPlotItem()
|
||||
plot_item.showAxis("left", show=True)
|
||||
plot_item.showAxis("bottom", show=True)
|
||||
plot_item.setClipToView(True)
|
||||
plot.setLabel("bottom", "X", units="m")
|
||||
plot.setLabel("bottom", x_label, units="m")
|
||||
plot.setLabel("left", y_label, units="m")
|
||||
view_box = plot.getViewBox()
|
||||
view_box.invertY(False)
|
||||
@@ -69,8 +89,9 @@ class AppWindowSar3dPlotMixin:
|
||||
self._sar3d_xz_image_item.hide()
|
||||
if self._sar3d_xy_image_item is not None:
|
||||
self._sar3d_xy_image_item.hide()
|
||||
self._sar3d_xz_plot.setTitle("3D SAR side view (X-Z)")
|
||||
self._sar3d_xy_plot.setTitle("3D SAR top view (X-Y)")
|
||||
bottom_title, _bottom_x_label = self._sar3d_bottom_view_labels()
|
||||
self._sar3d_xz_plot.setTitle("3D SAR top view (X-Z)")
|
||||
self._sar3d_xy_plot.setTitle(bottom_title)
|
||||
|
||||
def _draw_sar3d_projection(self, plot: pg.PlotWidget, image_item: pg.ImageItem, payload) -> bool:
|
||||
"""Draw one normalized projection payload onto its plot; return success."""
|
||||
@@ -98,10 +119,10 @@ class AppWindowSar3dPlotMixin:
|
||||
return True
|
||||
|
||||
def _draw_sar3d_projections(self, collection: ResultCollection) -> bool:
|
||||
"""Draw the latest collection-level 3D SAR side and top projections."""
|
||||
"""Draw the latest collection-level 3D SAR top and bottom projections."""
|
||||
xz_payload = self._collection_payload_by_name(collection, "sar3d_proj_xz", kind=3)
|
||||
xy_payload = self._collection_payload_by_name(collection, "sar3d_proj_xy", kind=3)
|
||||
if xz_payload is None and xy_payload is None:
|
||||
bottom_payload = self._collection_payload_by_name(collection, "sar3d_proj_bottom", kind=3)
|
||||
if xz_payload is None and bottom_payload is None:
|
||||
self._clear_sar3d_plot()
|
||||
return False
|
||||
|
||||
@@ -109,13 +130,15 @@ class AppWindowSar3dPlotMixin:
|
||||
plot.setUpdatesEnabled(False)
|
||||
try:
|
||||
self._ensure_sar3d_plot_items()
|
||||
# Axis labels track the bottom-view selector (payload axes change with it).
|
||||
self._configure_sar3d_plot_axes()
|
||||
xz_drawn = xz_payload is not None and self._draw_sar3d_projection(
|
||||
self._sar3d_xz_plot, self._sar3d_xz_image_item, xz_payload
|
||||
)
|
||||
xy_drawn = xy_payload is not None and self._draw_sar3d_projection(
|
||||
self._sar3d_xy_plot, self._sar3d_xy_image_item, xy_payload
|
||||
bottom_drawn = bottom_payload is not None and self._draw_sar3d_projection(
|
||||
self._sar3d_xy_plot, self._sar3d_xy_image_item, bottom_payload
|
||||
)
|
||||
if not xz_drawn and not xy_drawn:
|
||||
if not xz_drawn and not bottom_drawn:
|
||||
self._clear_sar3d_plot()
|
||||
return False
|
||||
|
||||
@@ -123,8 +146,9 @@ class AppWindowSar3dPlotMixin:
|
||||
peak_suffix = ""
|
||||
if peak_payload is not None:
|
||||
peak_suffix = f" | peak={float(peak_payload.scalar_value):.3g}"
|
||||
self._sar3d_xz_plot.setTitle(f"3D SAR side view (X-Z){peak_suffix}")
|
||||
self._sar3d_xy_plot.setTitle("3D SAR top view (X-Y)")
|
||||
bottom_title, _bottom_x_label = self._sar3d_bottom_view_labels()
|
||||
self._sar3d_xz_plot.setTitle(f"3D SAR top view (X-Z){peak_suffix}")
|
||||
self._sar3d_xy_plot.setTitle(bottom_title)
|
||||
finally:
|
||||
for plot in (self._sar3d_xz_plot, self._sar3d_xy_plot):
|
||||
plot.setUpdatesEnabled(True)
|
||||
|
||||
Reference in New Issue
Block a user