1# https://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

4# ---|P------/S----------~Lg---------- 

5 

6from __future__ import absolute_import, print_function, division 

7 

8import copy 

9import logging 

10try: 

11 from kite import Scene 

12except ImportError as e: 

13 print(e) 

14 Scene = None 

15 

16import numpy as num 

17 

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 

22 

23from .. import common 

24 

25from .base import Element, ElementState, CPTHandler, CPTState 

26 

27logger = logging.getLogger('kite_scene') 

28guts_prefix = 'sparrow' 

29 

30km = 1e3 

31d2r = num.pi/180. 

32 

33 

34class SceneTileAdapter(object): 

35 

36 def __init__(self, scene): 

37 self._scene = scene 

38 

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 

47 

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 

56 

57 @property 

58 def data(self): 

59 disp = self._scene.displacement 

60 disp[num.isnan(disp)] = None 

61 return disp 

62 

63 

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 

69 

70 nlat = lat_edge.size 

71 nlon = lon_edge.size 

72 nvertices = nlat * nlon 

73 

74 assert nlat > 1 and nlon > 1 

75 assert data_center.shape == (nlat-1, nlon-1) 

76 

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) 

82 

83 faces = geometry.topo_to_faces_quad(nlat, nlon) 

84 

85 self._tile = tile 

86 self._raw_vertices = vertices 

87 

88 if cells_cache is not None: 

89 if id(faces) not in cells_cache: 

90 cells_cache[id(faces)] = faces_to_cells(faces) 

91 

92 cells = cells_cache[id(faces)] 

93 else: 

94 cells = faces_to_cells(faces) 

95 

96 data_center = data_center.flatten() 

97 

98 TrimeshPipe.__init__( 

99 self, self._raw_vertices, 

100 cells=cells, values=data_center, **kwargs) 

101 

102 

103class KiteSceneElement(ElementState): 

104 visible = Bool.T(default=True) 

105 filename = String.T() 

106 scene = None 

107 

108 

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')) 

113 

114 def create(self): 

115 element = KiteElement() 

116 return element 

117 

118 def add_scene(self, scene): 

119 self.scenes.append(scene) 

120 

121 def remove_scene(self, scene): 

122 if scene in self.scenes: 

123 self.scenes.remove(scene) 

124 

125 

126class KiteElement(Element): 

127 

128 def __init__(self): 

129 Element.__init__(self) 

130 self._controls = None 

131 self._meshes = {} 

132 self._cells = {} 

133 self.cpt_handler = CPTHandler() 

134 

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) 

139 

140 self.cpt_handler.bind_state(state.cpt, self.update) 

141 

142 def unbind_state(self): 

143 self.cpt_handler.unbind_state() 

144 self._listeners = [] 

145 self._state = None 

146 

147 def get_name(self): 

148 return 'Kite InSAR Scenes' 

149 

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 

156 

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()]) 

165 

166 self.update() 

167 

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) 

173 

174 self._meshes.clear() 

175 self._cells.clear() 

176 

177 if self._controls: 

178 self._parent.remove_panel(self._controls) 

179 self._controls = None 

180 

181 self._parent.update_view() 

182 self._parent = None 

183 

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 

192 

193 if scene.frame.spacing != 'degree': 

194 logger.warning( 

195 'Sparrow requires Scene spacing in degrees. ' 

196 'Skipped %s', fn) 

197 

198 return 

199 

200 return scene 

201 

202 def open_load_scene_dialog(self, *args): 

203 caption = 'Select one or more Kite scenes to open' 

204 

205 fns, _ = qw.QFileDialog.getOpenFileNames( 

206 self._parent, caption, 

207 filter='YAML file (*.yml *.yaml)', 

208 options=common.qfiledialog_options) 

209 

210 for fname in fns: 

211 scene = self._load_scene_from_fn(fname) 

212 

213 if scene is None: 

214 continue 

215 

216 logger.info('Adding Kite scene %s', fname) 

217 

218 scene_element = KiteSceneElement(filename=fname) 

219 scene_element.scene = scene 

220 self._state.add_scene(scene_element) 

221 

222 self.update() 

223 

224 def clear_scenes(self, *args): 

225 logger.info('Clearing all loaded Kite scenes') 

226 

227 for mesh in self._meshes.values(): 

228 self._parent.remove_actor(mesh.actor) 

229 

230 self._meshes.clear() 

231 self._state.scenes = [] 

232 

233 self.update() 

234 

235 def update(self, *args): 

236 state = self._state 

237 

238 for mesh in self._meshes.values(): 

239 self._parent.remove_actor(mesh.actor) 

240 

241 if self._state.visible: 

242 for scene_element in state.scenes: 

243 logger.info('Drawing Kite scene') 

244 

245 if scene_element.scene is None: 

246 scene_element.scene = self._load_scene_from_fn( 

247 scene_element.filename) 

248 

249 scene = scene_element.scene 

250 

251 scene_tile = SceneTileAdapter(scene) 

252 

253 k = (scene_tile, state.cpt.cpt_name) 

254 

255 if k not in self._meshes: 

256 cpt = copy.deepcopy( 

257 self.cpt_handler._cpts[state.cpt.cpt_name]) 

258 

259 mesh = KiteMeshPipe( 

260 scene_tile, 

261 cells_cache=None, 

262 cpt=cpt, 

263 backface_culling=False) 

264 

265 values = scene_tile.data.flatten() 

266 self.cpt_handler._values = values 

267 self.cpt_handler.update_cpt() 

268 

269 mesh.set_shading('phong') 

270 mesh.set_lookuptable(self.cpt_handler._lookuptable) 

271 

272 self._meshes[k] = mesh 

273 else: 

274 mesh = self._meshes[k] 

275 self.cpt_handler.update_cpt() 

276 

277 if scene_element.visible: 

278 self._parent.add_actor(mesh.actor) 

279 

280 self._parent.update_view() 

281 

282 def _get_controls(self): 

283 if not self._controls: 

284 frame = qw.QFrame() 

285 layout = qw.QGridLayout() 

286 frame.setLayout(layout) 

287 

288 pb_load = qw.QPushButton('Add Scene') 

289 pb_load.clicked.connect(self.open_load_scene_dialog) 

290 layout.addWidget(pb_load, 0, 1) 

291 

292 pb_clear = qw.QPushButton('Clear Scenes') 

293 pb_clear.clicked.connect(self.clear_scenes) 

294 layout.addWidget(pb_clear, 0, 2) 

295 

296 self.cpt_handler.cpt_controls( 

297 self._parent, self._state.cpt, layout) 

298 

299 layout.addWidget(qw.QFrame(), 4, 0, 1, 3) 

300 

301 self._controls = frame 

302 

303 self._update_controls() 

304 

305 return self._controls 

306 

307 def _update_controls(self): 

308 self.cpt_handler._update_cpt_combobox() 

309 self.cpt_handler._update_cptscale_lineedit() 

310 

311 

312__all__ = [ 

313 'KiteState', 

314 'KiteElement' 

315]