1# https://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6from __future__ import absolute_import, print_function, division
8import copy
9import logging
10try:
11 from kite import Scene
12except ImportError as e:
13 print(e)
14 Scene = None
16import numpy as num
18from pyrocko import geometry
19from pyrocko.guts import Bool, String, List
20from pyrocko.gui.qt_compat import qw
21from pyrocko.gui.vtk_util import TrimeshPipe, faces_to_cells
23from .. import common
25from .base import Element, ElementState, CPTHandler, CPTState
27logger = logging.getLogger('kite_scene')
28guts_prefix = 'sparrow'
30km = 1e3
31d2r = num.pi/180.
34class SceneTileAdapter(object):
36 def __init__(self, scene):
37 self._scene = scene
39 def x(self):
40 # TODO how to handle E given in m
41 frame = self._scene.frame
42 x = num.zeros(frame.cols + 1)
43 x[0] = frame.E[0] - 0.5 * frame.dE
44 x[1:] = frame.E + 0.5 * frame.dE
45 x += frame.llLon
46 return x
48 def y(self):
49 # TODO how to handle N given in m
50 frame = self._scene.frame
51 y = num.zeros(frame.rows + 1)
52 y[0] = frame.N[0] - 0.5 * frame.dN
53 y[1:] = frame.N + 0.5 * frame.dN
54 y += frame.llLat
55 return y
57 @property
58 def data(self):
59 disp = self._scene.displacement
60 disp[num.isnan(disp)] = None
61 return disp
64class KiteMeshPipe(TrimeshPipe):
65 def __init__(self, tile, cells_cache=None, **kwargs):
66 lat_edge = tile.y()
67 lon_edge = tile.x()
68 data_center = tile.data
70 nlat = lat_edge.size
71 nlon = lon_edge.size
72 nvertices = nlat * nlon
74 assert nlat > 1 and nlon > 1
75 assert data_center.shape == (nlat-1, nlon-1)
77 rtp = num.empty((nvertices, 3))
78 rtp[:, 0] = 1.0
79 rtp[:, 1] = (num.repeat(lat_edge, nlon) + 90.) * d2r
80 rtp[:, 2] = num.tile(lon_edge, nlat) * d2r
81 vertices = geometry.rtp2xyz(rtp)
83 faces = geometry.topo_to_faces_quad(nlat, nlon)
85 self._tile = tile
86 self._raw_vertices = vertices
88 if cells_cache is not None:
89 if id(faces) not in cells_cache:
90 cells_cache[id(faces)] = faces_to_cells(faces)
92 cells = cells_cache[id(faces)]
93 else:
94 cells = faces_to_cells(faces)
96 data_center = data_center.flatten()
98 TrimeshPipe.__init__(
99 self, self._raw_vertices,
100 cells=cells, values=data_center, **kwargs)
103class KiteSceneElement(ElementState):
104 visible = Bool.T(default=True)
105 filename = String.T()
106 scene = None
109class KiteState(ElementState):
110 visible = Bool.T(default=True)
111 scenes = List.T(KiteSceneElement.T(), default=[])
112 cpt = CPTState.T(default=CPTState.D(cpt_name='seismic_r'))
114 def create(self):
115 element = KiteElement()
116 return element
118 def add_scene(self, scene):
119 self.scenes.append(scene)
121 def remove_scene(self, scene):
122 if scene in self.scenes:
123 self.scenes.remove(scene)
126class KiteElement(Element):
128 def __init__(self):
129 Element.__init__(self)
130 self._controls = None
131 self._meshes = {}
132 self._cells = {}
133 self.cpt_handler = CPTHandler()
135 def bind_state(self, state):
136 Element.bind_state(self, state)
137 for var in ['visible', 'scenes']:
138 self.register_state_listener3(self.update, state, var)
140 self.cpt_handler.bind_state(state.cpt, self.update)
142 def unbind_state(self):
143 self.cpt_handler.unbind_state()
144 self._listeners = []
145 self._state = None
147 def get_name(self):
148 return 'Kite InSAR Scenes'
150 def set_parent(self, parent):
151 if Scene is None:
152 qw.QMessageBox.warning(
153 parent, 'Import Error',
154 'Software package Kite is needed to display InSAR scenes!')
155 return
157 self._parent = parent
158 self._parent.add_panel(
159 self.get_name(),
160 self._get_controls(),
161 visible=True,
162 title_controls=[
163 self.get_title_control_remove(),
164 self.get_title_control_visible()])
166 self.update()
168 def unset_parent(self):
169 self.unbind_state()
170 if self._parent:
171 for mesh in self._meshes:
172 self._parent.remove_actor(mesh.actor)
174 self._meshes.clear()
175 self._cells.clear()
177 if self._controls:
178 self._parent.remove_panel(self._controls)
179 self._controls = None
181 self._parent.update_view()
182 self._parent = None
184 def _load_scene_from_fn(self, fn):
185 try:
186 scene = Scene.load(fn)
187 except ImportError:
188 qw.QMessageBox.warning(
189 self._parent, 'Import Error',
190 'Could not load Kite scene from %s' % fn)
191 return
193 if scene.frame.spacing != 'degree':
194 logger.warning(
195 'Sparrow requires Scene spacing in degrees. '
196 'Skipped %s', fn)
198 return
200 return scene
202 def open_load_scene_dialog(self, *args):
203 caption = 'Select one or more Kite scenes to open'
205 fns, _ = qw.QFileDialog.getOpenFileNames(
206 self._parent, caption,
207 filter='YAML file (*.yml *.yaml)',
208 options=common.qfiledialog_options)
210 for fname in fns:
211 scene = self._load_scene_from_fn(fname)
213 if scene is None:
214 continue
216 logger.info('Adding Kite scene %s', fname)
218 scene_element = KiteSceneElement(filename=fname)
219 scene_element.scene = scene
220 self._state.add_scene(scene_element)
222 self.update()
224 def clear_scenes(self, *args):
225 logger.info('Clearing all loaded Kite scenes')
227 for mesh in self._meshes.values():
228 self._parent.remove_actor(mesh.actor)
230 self._meshes.clear()
231 self._state.scenes = []
233 self.update()
235 def update(self, *args):
236 state = self._state
238 for mesh in self._meshes.values():
239 self._parent.remove_actor(mesh.actor)
241 if self._state.visible:
242 for scene_element in state.scenes:
243 logger.info('Drawing Kite scene')
245 if scene_element.scene is None:
246 scene_element.scene = self._load_scene_from_fn(
247 scene_element.filename)
249 scene = scene_element.scene
251 scene_tile = SceneTileAdapter(scene)
253 k = (scene_tile, state.cpt.cpt_name)
255 if k not in self._meshes:
256 cpt = copy.deepcopy(
257 self.cpt_handler._cpts[state.cpt.cpt_name])
259 mesh = KiteMeshPipe(
260 scene_tile,
261 cells_cache=None,
262 cpt=cpt,
263 backface_culling=False)
265 values = scene_tile.data.flatten()
266 self.cpt_handler._values = values
267 self.cpt_handler.update_cpt()
269 mesh.set_shading('phong')
270 mesh.set_lookuptable(self.cpt_handler._lookuptable)
272 self._meshes[k] = mesh
273 else:
274 mesh = self._meshes[k]
275 self.cpt_handler.update_cpt()
277 if scene_element.visible:
278 self._parent.add_actor(mesh.actor)
280 self._parent.update_view()
282 def _get_controls(self):
283 if not self._controls:
284 frame = qw.QFrame()
285 layout = qw.QGridLayout()
286 frame.setLayout(layout)
288 pb_load = qw.QPushButton('Add Scene')
289 pb_load.clicked.connect(self.open_load_scene_dialog)
290 layout.addWidget(pb_load, 0, 1)
292 pb_clear = qw.QPushButton('Clear Scenes')
293 pb_clear.clicked.connect(self.clear_scenes)
294 layout.addWidget(pb_clear, 0, 2)
296 self.cpt_handler.cpt_controls(
297 self._parent, self._state.cpt, layout)
299 layout.addWidget(qw.QFrame(), 4, 0, 1, 3)
301 self._controls = frame
303 self._update_controls()
305 return self._controls
307 def _update_controls(self):
308 self.cpt_handler._update_cpt_combobox()
309 self.cpt_handler._update_cptscale_lineedit()
312__all__ = [
313 'KiteState',
314 'KiteElement'
315]