1# https://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

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

5 

6import copy 

7import logging 

8try: 

9 from kite import Scene 

10except ImportError as e: 

11 print(e) 

12 Scene = None 

13 

14import numpy as num 

15 

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 

20 

21from .. import common 

22 

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

24 

25logger = logging.getLogger('kite_scene') 

26guts_prefix = 'sparrow' 

27 

28km = 1e3 

29d2r = num.pi/180. 

30 

31 

32class SceneTileAdapter(object): 

33 

34 def __init__(self, scene): 

35 self._scene = scene 

36 

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 

45 

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 

54 

55 @property 

56 def data(self): 

57 disp = self._scene.displacement 

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

59 return disp 

60 

61 

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 

67 

68 nlat = lat_edge.size 

69 nlon = lon_edge.size 

70 nvertices = nlat * nlon 

71 

72 assert nlat > 1 and nlon > 1 

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

74 

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) 

80 

81 faces = geometry.topo_to_faces_quad(nlat, nlon) 

82 

83 self._tile = tile 

84 self._raw_vertices = vertices 

85 

86 if cells_cache is not None: 

87 if id(faces) not in cells_cache: 

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

89 

90 cells = cells_cache[id(faces)] 

91 else: 

92 cells = faces_to_cells(faces) 

93 

94 data_center = data_center.flatten() 

95 

96 TrimeshPipe.__init__( 

97 self, self._raw_vertices, 

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

99 

100 

101class KiteSceneElement(ElementState): 

102 visible = Bool.T(default=True) 

103 filename = String.T() 

104 scene = None 

105 

106 

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

111 

112 def create(self): 

113 element = KiteElement() 

114 return element 

115 

116 def add_scene(self, scene): 

117 self.scenes.append(scene) 

118 

119 def remove_scene(self, scene): 

120 if scene in self.scenes: 

121 self.scenes.remove(scene) 

122 

123 

124class KiteElement(Element): 

125 

126 def __init__(self): 

127 Element.__init__(self) 

128 self._controls = None 

129 self._meshes = {} 

130 self._cells = {} 

131 self.cpt_handler = CPTHandler() 

132 

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) 

137 

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

139 

140 def unbind_state(self): 

141 self.cpt_handler.unbind_state() 

142 self._listeners = [] 

143 self._state = None 

144 

145 def get_name(self): 

146 return 'Kite InSAR Scenes' 

147 

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 

154 

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

163 

164 self.update() 

165 

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) 

171 

172 self._meshes.clear() 

173 self._cells.clear() 

174 

175 if self._controls: 

176 self._parent.remove_panel(self._controls) 

177 self._controls = None 

178 

179 self._parent.update_view() 

180 self._parent = None 

181 

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 

190 

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

192 logger.warning( 

193 'Sparrow requires Scene spacing in degrees. ' 

194 'Skipped %s', fn) 

195 

196 return 

197 

198 return scene 

199 

200 def open_load_scene_dialog(self, *args): 

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

202 

203 fns, _ = qw.QFileDialog.getOpenFileNames( 

204 self._parent, caption, 

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

206 options=common.qfiledialog_options) 

207 

208 for fname in fns: 

209 scene = self._load_scene_from_fn(fname) 

210 

211 if scene is None: 

212 continue 

213 

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

215 

216 scene_element = KiteSceneElement(filename=fname) 

217 scene_element.scene = scene 

218 self._state.add_scene(scene_element) 

219 

220 self.update() 

221 

222 def clear_scenes(self, *args): 

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

224 

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

226 self._parent.remove_actor(mesh.actor) 

227 

228 self._meshes.clear() 

229 self._state.scenes = [] 

230 

231 self.update() 

232 

233 def update(self, *args): 

234 state = self._state 

235 

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

237 self._parent.remove_actor(mesh.actor) 

238 

239 if self._state.visible: 

240 for scene_element in state.scenes: 

241 logger.info('Drawing Kite scene') 

242 

243 if scene_element.scene is None: 

244 scene_element.scene = self._load_scene_from_fn( 

245 scene_element.filename) 

246 

247 scene = scene_element.scene 

248 

249 scene_tile = SceneTileAdapter(scene) 

250 

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

252 

253 if k not in self._meshes: 

254 cpt = copy.deepcopy( 

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

256 

257 mesh = KiteMeshPipe( 

258 scene_tile, 

259 cells_cache=None, 

260 cpt=cpt, 

261 backface_culling=False) 

262 

263 values = scene_tile.data.flatten() 

264 self.cpt_handler._values = values 

265 self.cpt_handler.update_cpt() 

266 

267 mesh.set_shading('phong') 

268 mesh.set_lookuptable(self.cpt_handler._lookuptable) 

269 

270 self._meshes[k] = mesh 

271 else: 

272 mesh = self._meshes[k] 

273 self.cpt_handler.update_cpt() 

274 

275 if scene_element.visible: 

276 self._parent.add_actor(mesh.actor) 

277 

278 self._parent.update_view() 

279 

280 def _get_controls(self): 

281 if not self._controls: 

282 frame = qw.QFrame() 

283 layout = qw.QGridLayout() 

284 frame.setLayout(layout) 

285 

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

287 pb_load.clicked.connect(self.open_load_scene_dialog) 

288 layout.addWidget(pb_load, 0, 1) 

289 

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

291 pb_clear.clicked.connect(self.clear_scenes) 

292 layout.addWidget(pb_clear, 0, 2) 

293 

294 self.cpt_handler.cpt_controls( 

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

296 

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

298 

299 self._controls = frame 

300 

301 self._update_controls() 

302 

303 return self._controls 

304 

305 def _update_controls(self): 

306 self.cpt_handler._update_cpt_combobox() 

307 self.cpt_handler._update_cptscale_lineedit() 

308 

309 

310__all__ = [ 

311 'KiteState', 

312 'KiteElement' 

313]