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: 

11 Scene = None 

12 

13import numpy as num 

14 

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 

19 

20from .. import common 

21 

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

23 

24logger = logging.getLogger('kite_scene') 

25guts_prefix = 'sparrow' 

26 

27km = 1e3 

28d2r = num.pi/180. 

29 

30 

31class SceneTileAdapter(object): 

32 

33 def __init__(self, scene): 

34 self._scene = scene 

35 

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 

44 

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 

53 

54 @property 

55 def data(self): 

56 disp = self._scene.displacement 

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

58 return disp 

59 

60 

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 

66 

67 nlat = lat_edge.size 

68 nlon = lon_edge.size 

69 nvertices = nlat * nlon 

70 

71 assert nlat > 1 and nlon > 1 

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

73 

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) 

79 

80 faces = geometry.topo_to_faces_quad(nlat, nlon) 

81 

82 self._tile = tile 

83 self._raw_vertices = vertices 

84 

85 if cells_cache is not None: 

86 if id(faces) not in cells_cache: 

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

88 

89 cells = cells_cache[id(faces)] 

90 else: 

91 cells = faces_to_cells(faces) 

92 

93 data_center = data_center.flatten() 

94 

95 TrimeshPipe.__init__( 

96 self, self._raw_vertices, 

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

98 

99 

100class KiteSceneElement(ElementState): 

101 visible = Bool.T(default=True) 

102 filename = String.T() 

103 scene = None 

104 

105 

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

110 

111 def create(self): 

112 element = KiteElement() 

113 return element 

114 

115 def add_scene(self, scene): 

116 self.scenes.append(scene) 

117 

118 def remove_scene(self, scene): 

119 if scene in self.scenes: 

120 self.scenes.remove(scene) 

121 

122 

123class KiteElement(Element): 

124 

125 def __init__(self): 

126 Element.__init__(self) 

127 self._controls = None 

128 self._meshes = {} 

129 self._cells = {} 

130 self.cpt_handler = CPTHandler() 

131 

132 def bind_state(self, state): 

133 Element.bind_state(self, state) 

134 self.talkie_connect(state, ['visible', 'scenes'], self.update) 

135 

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

137 

138 def unbind_state(self): 

139 self.cpt_handler.unbind_state() 

140 Element.unbind_state(self) 

141 

142 def get_name(self): 

143 return 'Kite InSAR Scenes' 

144 

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 

151 

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

160 

161 self.update() 

162 

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) 

168 

169 self._meshes.clear() 

170 self._cells.clear() 

171 

172 if self._controls: 

173 self._parent.remove_panel(self._controls) 

174 self._controls = None 

175 

176 self._parent.update_view() 

177 self._parent = None 

178 

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 

187 

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

189 logger.warning( 

190 'Sparrow requires Scene spacing in degrees. ' 

191 'Skipped %s', fn) 

192 

193 return 

194 

195 return scene 

196 

197 def open_load_scene_dialog(self, *args): 

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

199 

200 fns, _ = qw.QFileDialog.getOpenFileNames( 

201 self._parent, caption, 

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

203 options=common.qfiledialog_options) 

204 

205 for fname in fns: 

206 scene = self._load_scene_from_fn(fname) 

207 

208 if scene is None: 

209 continue 

210 

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

212 

213 scene_element = KiteSceneElement(filename=fname) 

214 scene_element.scene = scene 

215 self._state.add_scene(scene_element) 

216 

217 self.update() 

218 

219 def clear_scenes(self, *args): 

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

221 

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

223 self._parent.remove_actor(mesh.actor) 

224 

225 self._meshes.clear() 

226 self._state.scenes = [] 

227 

228 self.update() 

229 

230 def update(self, *args): 

231 state = self._state 

232 

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

234 self._parent.remove_actor(mesh.actor) 

235 

236 if self._state.visible: 

237 for scene_element in state.scenes: 

238 logger.info('Drawing Kite scene') 

239 

240 if scene_element.scene is None: 

241 scene_element.scene = self._load_scene_from_fn( 

242 scene_element.filename) 

243 

244 scene = scene_element.scene 

245 

246 scene_tile = SceneTileAdapter(scene) 

247 

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

249 

250 if k not in self._meshes: 

251 cpt = copy.deepcopy( 

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

253 

254 mesh = KiteMeshPipe( 

255 scene_tile, 

256 cells_cache=None, 

257 cpt=cpt, 

258 backface_culling=False) 

259 

260 values = scene_tile.data.flatten() 

261 self.cpt_handler._values = values 

262 self.cpt_handler.update_cpt() 

263 

264 mesh.set_shading('phong') 

265 mesh.set_lookuptable(self.cpt_handler._lookuptable) 

266 

267 self._meshes[k] = mesh 

268 else: 

269 mesh = self._meshes[k] 

270 self.cpt_handler.update_cpt() 

271 

272 if scene_element.visible: 

273 self._parent.add_actor(mesh.actor) 

274 

275 self._parent.update_view() 

276 

277 def _get_controls(self): 

278 if not self._controls: 

279 frame = qw.QFrame() 

280 layout = qw.QGridLayout() 

281 frame.setLayout(layout) 

282 

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

284 pb_load.clicked.connect(self.open_load_scene_dialog) 

285 layout.addWidget(pb_load, 0, 1) 

286 

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

288 pb_clear.clicked.connect(self.clear_scenes) 

289 layout.addWidget(pb_clear, 0, 2) 

290 

291 self.cpt_handler.cpt_controls( 

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

293 

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

295 

296 self._controls = frame 

297 

298 self._update_controls() 

299 

300 return self._controls 

301 

302 def _update_controls(self): 

303 self.cpt_handler._update_cpt_combobox() 

304 self.cpt_handler._update_cptscale_lineedit() 

305 

306 

307__all__ = [ 

308 'KiteState', 

309 'KiteElement' 

310]