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 as e:
11 print(e)
12 Scene = None
14import numpy as num
16from pyrocko import geometry
17from pyrocko.guts import Bool, String, List
18from pyrocko.gui.qt_compat import qw
19from pyrocko.gui.vtk_util import TrimeshPipe, faces_to_cells
21from .. import common
23from .base import Element, ElementState, CPTHandler, CPTState
25logger = logging.getLogger('kite_scene')
26guts_prefix = 'sparrow'
28km = 1e3
29d2r = num.pi/180.
32class SceneTileAdapter(object):
34 def __init__(self, scene):
35 self._scene = scene
37 def x(self):
38 # TODO how to handle E given in m
39 frame = self._scene.frame
40 x = num.zeros(frame.cols + 1)
41 x[0] = frame.E[0] - 0.5 * frame.dE
42 x[1:] = frame.E + 0.5 * frame.dE
43 x += frame.llLon
44 return x
46 def y(self):
47 # TODO how to handle N given in m
48 frame = self._scene.frame
49 y = num.zeros(frame.rows + 1)
50 y[0] = frame.N[0] - 0.5 * frame.dN
51 y[1:] = frame.N + 0.5 * frame.dN
52 y += frame.llLat
53 return y
55 @property
56 def data(self):
57 disp = self._scene.displacement
58 disp[num.isnan(disp)] = None
59 return disp
62class KiteMeshPipe(TrimeshPipe):
63 def __init__(self, tile, cells_cache=None, **kwargs):
64 lat_edge = tile.y()
65 lon_edge = tile.x()
66 data_center = tile.data
68 nlat = lat_edge.size
69 nlon = lon_edge.size
70 nvertices = nlat * nlon
72 assert nlat > 1 and nlon > 1
73 assert data_center.shape == (nlat-1, nlon-1)
75 rtp = num.empty((nvertices, 3))
76 rtp[:, 0] = 1.0
77 rtp[:, 1] = (num.repeat(lat_edge, nlon) + 90.) * d2r
78 rtp[:, 2] = num.tile(lon_edge, nlat) * d2r
79 vertices = geometry.rtp2xyz(rtp)
81 faces = geometry.topo_to_faces_quad(nlat, nlon)
83 self._tile = tile
84 self._raw_vertices = vertices
86 if cells_cache is not None:
87 if id(faces) not in cells_cache:
88 cells_cache[id(faces)] = faces_to_cells(faces)
90 cells = cells_cache[id(faces)]
91 else:
92 cells = faces_to_cells(faces)
94 data_center = data_center.flatten()
96 TrimeshPipe.__init__(
97 self, self._raw_vertices,
98 cells=cells, values=data_center, **kwargs)
101class KiteSceneElement(ElementState):
102 visible = Bool.T(default=True)
103 filename = String.T()
104 scene = None
107class KiteState(ElementState):
108 visible = Bool.T(default=True)
109 scenes = List.T(KiteSceneElement.T(), default=[])
110 cpt = CPTState.T(default=CPTState.D(cpt_name='seismic_r'))
112 def create(self):
113 element = KiteElement()
114 return element
116 def add_scene(self, scene):
117 self.scenes.append(scene)
119 def remove_scene(self, scene):
120 if scene in self.scenes:
121 self.scenes.remove(scene)
124class KiteElement(Element):
126 def __init__(self):
127 Element.__init__(self)
128 self._controls = None
129 self._meshes = {}
130 self._cells = {}
131 self.cpt_handler = CPTHandler()
133 def bind_state(self, state):
134 Element.bind_state(self, state)
135 for var in ['visible', 'scenes']:
136 self.register_state_listener3(self.update, state, var)
138 self.cpt_handler.bind_state(state.cpt, self.update)
140 def unbind_state(self):
141 self.cpt_handler.unbind_state()
142 self._listeners = []
143 self._state = None
145 def get_name(self):
146 return 'Kite InSAR Scenes'
148 def set_parent(self, parent):
149 if Scene is None:
150 qw.QMessageBox.warning(
151 parent, 'Import Error',
152 'Software package Kite is needed to display InSAR scenes!')
153 return
155 self._parent = parent
156 self._parent.add_panel(
157 self.get_name(),
158 self._get_controls(),
159 visible=True,
160 title_controls=[
161 self.get_title_control_remove(),
162 self.get_title_control_visible()])
164 self.update()
166 def unset_parent(self):
167 self.unbind_state()
168 if self._parent:
169 for mesh in self._meshes:
170 self._parent.remove_actor(mesh.actor)
172 self._meshes.clear()
173 self._cells.clear()
175 if self._controls:
176 self._parent.remove_panel(self._controls)
177 self._controls = None
179 self._parent.update_view()
180 self._parent = None
182 def _load_scene_from_fn(self, fn):
183 try:
184 scene = Scene.load(fn)
185 except ImportError:
186 qw.QMessageBox.warning(
187 self._parent, 'Import Error',
188 'Could not load Kite scene from %s' % fn)
189 return
191 if scene.frame.spacing != 'degree':
192 logger.warning(
193 'Sparrow requires Scene spacing in degrees. '
194 'Skipped %s', fn)
196 return
198 return scene
200 def open_load_scene_dialog(self, *args):
201 caption = 'Select one or more Kite scenes to open'
203 fns, _ = qw.QFileDialog.getOpenFileNames(
204 self._parent, caption,
205 filter='YAML file (*.yml *.yaml)',
206 options=common.qfiledialog_options)
208 for fname in fns:
209 scene = self._load_scene_from_fn(fname)
211 if scene is None:
212 continue
214 logger.info('Adding Kite scene %s', fname)
216 scene_element = KiteSceneElement(filename=fname)
217 scene_element.scene = scene
218 self._state.add_scene(scene_element)
220 self.update()
222 def clear_scenes(self, *args):
223 logger.info('Clearing all loaded Kite scenes')
225 for mesh in self._meshes.values():
226 self._parent.remove_actor(mesh.actor)
228 self._meshes.clear()
229 self._state.scenes = []
231 self.update()
233 def update(self, *args):
234 state = self._state
236 for mesh in self._meshes.values():
237 self._parent.remove_actor(mesh.actor)
239 if self._state.visible:
240 for scene_element in state.scenes:
241 logger.info('Drawing Kite scene')
243 if scene_element.scene is None:
244 scene_element.scene = self._load_scene_from_fn(
245 scene_element.filename)
247 scene = scene_element.scene
249 scene_tile = SceneTileAdapter(scene)
251 k = (scene_tile, state.cpt.cpt_name)
253 if k not in self._meshes:
254 cpt = copy.deepcopy(
255 self.cpt_handler._cpts[state.cpt.cpt_name])
257 mesh = KiteMeshPipe(
258 scene_tile,
259 cells_cache=None,
260 cpt=cpt,
261 backface_culling=False)
263 values = scene_tile.data.flatten()
264 self.cpt_handler._values = values
265 self.cpt_handler.update_cpt()
267 mesh.set_shading('phong')
268 mesh.set_lookuptable(self.cpt_handler._lookuptable)
270 self._meshes[k] = mesh
271 else:
272 mesh = self._meshes[k]
273 self.cpt_handler.update_cpt()
275 if scene_element.visible:
276 self._parent.add_actor(mesh.actor)
278 self._parent.update_view()
280 def _get_controls(self):
281 if not self._controls:
282 frame = qw.QFrame()
283 layout = qw.QGridLayout()
284 frame.setLayout(layout)
286 pb_load = qw.QPushButton('Add Scene')
287 pb_load.clicked.connect(self.open_load_scene_dialog)
288 layout.addWidget(pb_load, 0, 1)
290 pb_clear = qw.QPushButton('Clear Scenes')
291 pb_clear.clicked.connect(self.clear_scenes)
292 layout.addWidget(pb_clear, 0, 2)
294 self.cpt_handler.cpt_controls(
295 self._parent, self._state.cpt, layout)
297 layout.addWidget(qw.QFrame(), 4, 0, 1, 3)
299 self._controls = frame
301 self._update_controls()
303 return self._controls
305 def _update_controls(self):
306 self.cpt_handler._update_cpt_combobox()
307 self.cpt_handler._update_cptscale_lineedit()
310__all__ = [
311 'KiteState',
312 'KiteElement'
313]