Source code for insarviz.view.InterferogramWidget
import pyqtgraph
import numpy as np
import datetime
from .__prelude__ import Qt, SELF, EACH
from .WidgetTree import Container, Leaf
def _to_date(time_stamp):
return datetime.datetime.strftime(datetime.datetime.fromtimestamp(time_stamp), "%Y-%m-%d")
[docs]
class InteractiveGraphItem(pyqtgraph.GraphItem):
# Define signals for edge interactions
sigEdgeClicked = Qt.Signal(object) # self, edge_index
sigEdgeHovered = Qt.Signal(object) # self, edge_index
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.hovered_edge = None
[docs]
def mouseClickEvent(self, ev):
"""Handle mouse clicks on edges"""
if ev.button() == Qt.Qt.MouseButton.LeftButton:
edge_idx = self._getEdgeAt(ev.pos())
if edge_idx is not None:
ev.accept()
self.sigEdgeClicked.emit(edge_idx)
else:
ev.ignore()
else:
ev.ignore()
[docs]
def hoverEvent(self, ev):
"""Handle hover events on edges"""
if ev.isExit():
self.hovered_edge = None
self.update()
return
edge_idx = self._getEdgeAt(ev.pos())
if edge_idx != self.hovered_edge:
self.hovered_edge = edge_idx
self.update()
if edge_idx is not None:
self.sigEdgeHovered.emit(edge_idx)
[docs]
def _getEdgeAt(self, pos):
"""Find which edge is at the given position"""
if self.pos is None or self.adjacency is None:
return None
scene_pos = self.mapToScene(pos)
click_point = pyqtgraph.Point(scene_pos)
# Check each edge
for i, edge in enumerate(self.adjacency):
p1 = pyqtgraph.Point(self.mapToScene(pyqtgraph.Point(*self.pos[edge[0]])))
p2 = pyqtgraph.Point(self.mapToScene(pyqtgraph.Point(*self.pos[edge[1]])))
# Calculate distance from point to line segment
dist = self._pointToLineDistance(click_point, p1, p2)
# Use a tolerance of 5 pixels
if dist < 5:
return i
return None
[docs]
def _pointToLineDistance(self, point, line_p1, line_p2):
"""Calculate distance from point to line segment"""
line_vec = line_p2 - line_p1
point_vec = point - line_p1
line_len2 = line_vec.dot(line_vec)
if line_len2 == 0:
return point_vec.length()
t = max(0, min(1, point_vec.dot(line_vec) / line_len2))
projection = line_p1 + line_vec * t
return (point - projection).length()
[docs]
class InterferogramWidget(Qt.QWidget):
[docs]
class Widgets:
unwrapped_preview_label: Qt.QLabel
wrapped_filtered_preview_label: Qt.QLabel
aps_preview_label: Qt.QLabel
def __init__(self, interferogram):
super().__init__()
self.interferogram = interferogram
wtree = Container(Qt.QHBoxLayout, (), [
Leaf(Qt.QLabel, (), id="unwrapped_preview_label"),
Leaf(Qt.QLabel, (), id="wrapped_filtered_preview_label"),
Leaf(Qt.QLabel, (), id="aps_preview_label"),
Leaf(Qt.QWidget, (), id="interf_info"),
(Leaf(Qt.QWidget, ()), {"stretch": 1}),
])
self.widgets = self.Widgets()
wtree.create_in(self, self.widgets)
info_layout = Qt.QFormLayout()
info_layout.setAlignment(Qt.Qt.AlignVCenter)
info_layout.addRow("Start date", Qt.QLabel(_to_date(interferogram.start_date)))
info_layout.addRow("End date", Qt.QLabel(_to_date(interferogram.end_date)))
self.widgets.interf_selection = Qt.QLabel("")
info_layout.addRow("Selected", self.widgets.interf_selection)
self.__set_selected()
self.interferogram.dynamic_attribute("selected").value_changed.connect(self.__set_selected)
self.widgets.interf_info.setLayout(info_layout)
self.__update_preview()
self.interferogram.wrapped_filtered_preview.ready.connect(self.__update_preview)
self.interferogram.aps_preview.ready.connect(self.__update_preview)
self.interferogram.unwrapped_preview.ready.connect(self.__update_preview)
@Qt.Slot()
def __set_selected(self):
selected = self.interferogram.selected
self.widgets.interf_selection.setText("Yes" if selected else "No")
@Qt.Slot()
def __update_preview(self):
unwrapped_preview_image = self.interferogram.unwrapped_preview.latest()
if unwrapped_preview_image is not None:
h, w, _ = unwrapped_preview_image.shape
data = Qt.QImage(bytes(unwrapped_preview_image.data), w, h, Qt.QImage.Format.Format_RGBA8888)
self.widgets.unwrapped_preview_label.setPixmap(Qt.QPixmap.fromImage(data))
wrapped_filtered_preview_image = self.interferogram.wrapped_filtered_preview.latest()
if wrapped_filtered_preview_image is not None:
h, w, _ = wrapped_filtered_preview_image.shape
data = Qt.QImage(bytes(wrapped_filtered_preview_image.data), w, h, Qt.QImage.Format.Format_RGBA8888)
self.widgets.wrapped_filtered_preview_label.setPixmap(Qt.QPixmap.fromImage(data))
aps_preview_image = self.interferogram.aps_preview.latest()
if aps_preview_image is not None:
h, w, _ = aps_preview_image.shape
data = Qt.QImage(bytes(aps_preview_image.data), w, h, Qt.QImage.Format.Format_RGBA8888)
self.widgets.aps_preview_label.setPixmap(Qt.QPixmap.fromImage(data))
[docs]
class InterferogramNetworkWidget(Qt.QWidget):
def __init__(self, network):
super().__init__()
self.network = network
self.widgets = self.Widgets()
wtree = Container(Qt.QHBoxLayout, (), [
Leaf(pyqtgraph.PlotWidget, (), id="interfero_plot"),
Leaf(Qt.QScrollArea, (), id="interfero_scroll_area"),
])
wtree.create_in(self, self.widgets)
scroll_wtree = Container(Qt.QVBoxLayout, (), [])
self.widgets.interfero_scroll_area.setWidget(scroll_wtree.create(self.widgets))
self.widgets.interfero_scroll_area.setWidgetResizable(True)
self.setWindowTitle(f"Interferogram from {network.base_url}")
date_list = list(network.date_baselines.items())
date_indices = { date: i for i, (date, _)
in enumerate(date_list) }
pos = np.array(date_list)
adj = np.array([ (date_indices[interf.start_date], date_indices[interf.end_date])
for interf in network.interferograms ])
self.graph_item = InteractiveGraphItem(pos = pos, adj = adj)
self.graph_item.sigEdgeClicked.connect(self.__on_edge_clicked)
time_line = pyqtgraph.InfiniteLine(pos = network.observed_timestamp, angle=90, movable=True)
def on_time_moved():
network.observed_timestamp = time_line.getPos()[0]
time_line.sigDragged.connect(on_time_moved)
self._interferogram_selection = (SELF.interferograms / EACH(SELF.selected))[network]
self._interferogram_selection.value_changed.connect(self.__update_interfero_list)
self.__update_interfero_list()
SELF.observed_timestamp[network].value_changed.connect(self.__update_interfero_list)
self.widgets.interfero_plot.setAxisItems({"bottom": pyqtgraph.DateAxisItem(text='Date', units='yyyy-mm-dd')})
self.widgets.interfero_plot.addItem(self.graph_item)
self.widgets.interfero_plot.addItem(time_line)
def __on_edge_clicked(self, edge_idx):
interf = self.network.interferograms[edge_idx]
interf.selected = not interf.selected
def __interf_color(self, interf, timestamp):
if interf.selected:
return (0,255,0,255,5)
if interf.strides_date(timestamp):
return (0,128,0,255,3)
else:
return (0,0,0,255,1)
def __reset_colors(self):
interf_colors = np.array([
self.__interf_color(interf, self.network.observed_timestamp)
for interf in self.network.interferograms
], dtype=[('red', 'u1'), ('green', 'u1'), ('blue', 'u1'), ('alpha', 'u1'), ('width', 'f4')])
self.graph_item.setPen(interf_colors)
@Qt.Slot()
def __update_interfero_list(self):
timestamp = self.network.observed_timestamp
self.__reset_colors()
layout = Qt.QVBoxLayout()
for i in range(layout.count()):
layout.takeAt(0).widget().deleteLater()
# Show selected interferograms, always and before others
selected_interferograms = []
striding_interferograms = []
for interf in self.network.interferograms:
if interf.selected:
selected_interferograms.append(interf)
elif interf.strides_date(timestamp):
striding_interferograms.append(interf)
def mkGroupBox(title):
return lambda: Qt.QGroupBox(title)
selected_widget = []
if len(selected_interferograms) > 0:
selected_widget = [
Container(Qt.QVBoxLayout, (), [
Leaf(InterferogramWidget, (interf, )) for interf in selected_interferograms
], widget_class = mkGroupBox("Selected")), ]
list_wtree = Container(Qt.QVBoxLayout, (), selected_widget + [
Container(Qt.QVBoxLayout, (), [
Leaf(InterferogramWidget, (interf, )) for interf in striding_interferograms
], widget_class = mkGroupBox("Overlapping")),
(Leaf(Qt.QWidget,()), {"stretch": 1}),
])
self.widgets.interfero_scroll_area.setWidget(list_wtree.create(self.widgets))