1# https://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6import copy
7import logging
8try:
9 from kite import Scene
10except ImportError:
11 Scene = None
13import numpy as num
15from pyrocko import geometry
16from pyrocko.guts import Bool, String, List
17from pyrocko.gui.qt_compat import qw
18from pyrocko.gui.vtk_util import TrimeshPipe, faces_to_cells
20from .. import common
22from .base import Element, ElementState, CPTHandler, CPTState
24logger = logging.getLogger('kite_scene')
25guts_prefix = 'sparrow'
27km = 1e3
28d2r = num.pi/180.
31class SceneTileAdapter(object):
33 def __init__(self, scene):
34 self._scene = scene
36 def x(self):
37 # TODO how to handle E given in m
38 frame = self._scene.frame
39 x = num.zeros(frame.cols + 1)
40 x[0] = frame.E[0] - 0.5 * frame.dE
41 x[1:] = frame.E + 0.5 * frame.dE
42 x += frame.llLon
43 return x
45 def y(self):
46 # TODO how to handle N given in m
47 frame = self._scene.frame
48 y = num.zeros(frame.rows + 1)
49 y[0] = frame.N[0] - 0.5 * frame.dN
50 y[1:] = frame.N + 0.5 * frame.dN
51 y += frame.llLat
52 return y
54 @property
55 def data(self):
56 disp = self._scene.displacement
57 disp[num.isnan(disp)] = None
58 return disp
61class KiteMeshPipe(TrimeshPipe):
62 def __init__(self, tile, cells_cache=None, **kwargs):
63 lat_edge = tile.y()
64 lon_edge = tile.x()
65 data_center = tile.data
67 nlat = lat_edge.size
68 nlon = lon_edge.size
69 nvertices = nlat * nlon
71 assert nlat > 1 and nlon > 1
72 assert data_center.shape == (nlat-1, nlon-1)
74 rtp = num.empty((nvertices, 3))
75 rtp[:, 0] = 1.0
76 rtp[:, 1] = (num.repeat(lat_edge, nlon) + 90.) * d2r
77 rtp[:, 2] = num.tile(lon_edge, nlat) * d2r
78 vertices = geometry.rtp2xyz(rtp)
80 faces = geometry.topo_to_faces_quad(nlat, nlon)
82 self._tile = tile
83 self._raw_vertices = vertices
85 if cells_cache is not None:
86 if id(faces) not in cells_cache:
87 cells_cache[id(faces)] = faces_to_cells(faces)
89 cells = cells_cache[id(faces)]
90 else:
91 cells = faces_to_cells(faces)
93 data_center = data_center.flatten()
95 TrimeshPipe.__init__(
96 self, self._raw_vertices,
97 cells=cells, values=data_center, **kwargs)
100class KiteSceneElement(ElementState):
101 visible = Bool.T(default=True)
102 filename = String.T()
103 scene = None
106class KiteState(ElementState):
107 visible = Bool.T(default=True)
108 scenes = List.T(KiteSceneElement.T(), default=[])
109 cpt = CPTState.T(default=CPTState.D(cpt_name='seismic_r'))
111 def create(self):
112 element = KiteElement()
113 return element
115 def add_scene(self, scene):
116 self.scenes.append(scene)
118 def remove_scene(self, scene):
119 if scene in self.scenes:
120 self.scenes.remove(scene)
123class KiteElement(Element):
125 def __init__(self):
126 Element.__init__(self)
127 self._controls = None
128 self._meshes = {}
129 self._cells = {}
130 self.cpt_handler = CPTHandler()
132 def bind_state(self, state):
133 Element.bind_state(self, state)
134 self.talkie_connect(state, ['visible', 'scenes'], self.update)
136 self.cpt_handler.bind_state(state.cpt, self.update)
138 def unbind_state(self):
139 self.cpt_handler.unbind_state()
140 Element.unbind_state(self)
142 def get_name(self):
143 return 'Kite InSAR Scenes'
145 def set_parent(self, parent):
146 if Scene is None:
147 qw.QMessageBox.warning(
148 parent, 'Import Error',
149 'Software package Kite is needed to display InSAR scenes!')
150 return
152 self._parent = parent
153 self._parent.add_panel(
154 self.get_title_label(),
155 self._get_controls(),
156 visible=True,
157 title_controls=[
158 self.get_title_control_remove(),
159 self.get_title_control_visible()])
161 self.update()
163 def unset_parent(self):
164 self.unbind_state()
165 if self._parent:
166 for mesh in self._meshes:
167 self._parent.remove_actor(mesh.actor)
169 self._meshes.clear()
170 self._cells.clear()
172 if self._controls:
173 self._parent.remove_panel(self._controls)
174 self._controls = None
176 self._parent.update_view()
177 self._parent = None
179 def _load_scene_from_fn(self, fn):
180 try:
181 scene = Scene.load(fn)
182 except ImportError:
183 qw.QMessageBox.warning(
184 self._parent, 'Import Error',
185 'Could not load Kite scene from %s' % fn)
186 return
188 if scene.frame.spacing != 'degree':
189 logger.warning(
190 'Sparrow requires Scene spacing in degrees. '
191 'Skipped %s', fn)
193 return
195 return scene
197 def open_load_scene_dialog(self, *args):
198 caption = 'Select one or more Kite scenes to open'
200 fns, _ = qw.QFileDialog.getOpenFileNames(
201 self._parent, caption,
202 filter='YAML file (*.yml *.yaml)',
203 options=common.qfiledialog_options)
205 for fname in fns:
206 scene = self._load_scene_from_fn(fname)
208 if scene is None:
209 continue
211 logger.info('Adding Kite scene %s', fname)
213 scene_element = KiteSceneElement(filename=fname)
214 scene_element.scene = scene
215 self._state.add_scene(scene_element)
217 self.update()
219 def clear_scenes(self, *args):
220 logger.info('Clearing all loaded Kite scenes')
222 for mesh in self._meshes.values():
223 self._parent.remove_actor(mesh.actor)
225 self._meshes.clear()
226 self._state.scenes = []
228 self.update()
230 def update(self, *args):
231 state = self._state
233 for mesh in self._meshes.values():
234 self._parent.remove_actor(mesh.actor)
236 if self._state.visible:
237 for scene_element in state.scenes:
238 logger.info('Drawing Kite scene')
240 if scene_element.scene is None:
241 scene_element.scene = self._load_scene_from_fn(
242 scene_element.filename)
244 scene = scene_element.scene
246 scene_tile = SceneTileAdapter(scene)
248 k = (scene_tile, state.cpt.cpt_name)
250 if k not in self._meshes:
251 cpt = copy.deepcopy(
252 self.cpt_handler._cpts[state.cpt.cpt_name])
254 mesh = KiteMeshPipe(
255 scene_tile,
256 cells_cache=None,
257 cpt=cpt,
258 backface_culling=False)
260 values = scene_tile.data.flatten()
261 self.cpt_handler._values = values
262 self.cpt_handler.update_cpt()
264 mesh.set_shading('phong')
265 mesh.set_lookuptable(self.cpt_handler._lookuptable)
267 self._meshes[k] = mesh
268 else:
269 mesh = self._meshes[k]
270 self.cpt_handler.update_cpt()
272 if scene_element.visible:
273 self._parent.add_actor(mesh.actor)
275 self._parent.update_view()
277 def _get_controls(self):
278 if not self._controls:
279 frame = qw.QFrame()
280 layout = qw.QGridLayout()
281 frame.setLayout(layout)
283 pb_load = qw.QPushButton('Add Scene')
284 pb_load.clicked.connect(self.open_load_scene_dialog)
285 layout.addWidget(pb_load, 0, 1)
287 pb_clear = qw.QPushButton('Clear Scenes')
288 pb_clear.clicked.connect(self.clear_scenes)
289 layout.addWidget(pb_clear, 0, 2)
291 self.cpt_handler.cpt_controls(
292 self._parent, self._state.cpt, layout)
294 layout.addWidget(qw.QFrame(), 4, 0, 1, 3)
296 self._controls = frame
298 self._update_controls()
300 return self._controls
302 def _update_controls(self):
303 self.cpt_handler._update_cpt_combobox()
304 self.cpt_handler._update_cptscale_lineedit()
307__all__ = [
308 'KiteState',
309 'KiteElement'
310]