1# https://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

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

5 

6import numpy as num 

7 

8from pyrocko.guts import Bool, Float 

9from pyrocko.gui.qt_compat import qw, qc 

10 

11from pyrocko.dataset.active_faults import ActiveFaults 

12from pyrocko.gui import vtk_util 

13from pyrocko import plot, orthodrome as od, model, cake 

14import vtk 

15 

16from .base import Element, ElementState 

17 

18guts_prefix = 'sparrow' 

19 

20km = 1e3 

21 

22 

23def color(x): 

24 return num.array(plot.to01(plot.color(x)), dtype=num.float64) 

25 

26 

27def to_latlondepth(event, station, rays): 

28 

29 lines = [] 

30 azimuth, _ = event.azibazi_to(station) 

31 for ray in rays: 

32 fanz, fanx, _ = ray.zxt_path_subdivided() 

33 

34 for zs, xs in zip(fanz, fanx): 

35 lats, lons = od.azidist_to_latlon( 

36 event.lat, event.lon, azimuth, xs) 

37 

38 line = num.zeros((xs.size, 3)) 

39 line[:, 0] = lats 

40 line[:, 1] = lons 

41 line[:, 2] = zs 

42 

43 lines.append(line) 

44 

45 return lines 

46 

47 

48class RaysState(ElementState): 

49 visible = Bool.T(default=True) 

50 line_width = Float.T(default=1.0) 

51 opacity = Float.T(default=1.0) 

52 lat = Float.T(default=0.0) 

53 lon = Float.T(default=0.0) 

54 depth = Float.T(default=10000.0) 

55 

56 def create(self): 

57 element = RaysElement() 

58 element.bind_state(self) 

59 return element 

60 

61 

62class RaysPipe(object): 

63 def __init__(self, ray_data): 

64 

65 self._opacity = 1.0 

66 self._line_width = 1.0 

67 self._actors = {} 

68 

69 mapper = vtk.vtkDataSetMapper() 

70 lines = [] 

71 for event, station, rays in ray_data: 

72 lines.extend(to_latlondepth(event, station, rays)) 

73 

74 grid = vtk_util.make_multi_polyline( 

75 lines_latlondepth=lines) 

76 

77 vtk_util.vtk_set_input(mapper, grid) 

78 

79 actor = vtk.vtkActor() 

80 actor.SetMapper(mapper) 

81 

82 self._actors['ray'] = actor 

83 

84 def set_opacity(self, opacity): 

85 opacity = float(opacity) 

86 if self._opacity != opacity: 

87 for actor in self._actors.values(): 

88 actor.GetProperty().SetOpacity(opacity) 

89 

90 self._opacity = opacity 

91 

92 def set_line_width(self, width): 

93 width = float(width) 

94 if self._line_width != width: 

95 for actor in self._actors.values(): 

96 actor.GetProperty().SetLineWidth(width) 

97 

98 self._line_width = width 

99 

100 def get_actors(self): 

101 return [self._actors[k] for k in sorted(self._actors.keys())] 

102 

103 

104class RaysElement(Element): 

105 

106 def __init__(self): 

107 Element.__init__(self) 

108 self._parent = None 

109 self._state = None 

110 self._pipe = None 

111 self._controls = None 

112 self._active_faults = None 

113 self._listeners = [] 

114 self._mod = cake.load_model() 

115 self._stations = model.load_stations('stations.txt') 

116 self._params = () 

117 

118 def bind_state(self, state): 

119 upd = self.update 

120 self._listeners.append(upd) 

121 for var in [ 

122 'visible', 'line_width', 'opacity', 

123 'lat', 'lon', 'depth']: 

124 

125 self.register_state_listener(self.update, state, var) 

126 

127 self._state = state 

128 

129 def unbind_state(self): 

130 self._listeners = [] 

131 

132 def get_name(self): 

133 return 'Rays' 

134 

135 def set_parent(self, parent): 

136 self._parent = parent 

137 if not self._active_faults: 

138 self._active_faults = ActiveFaults() 

139 

140 self._parent.add_panel( 

141 self.get_name(), 

142 self._get_controls(), 

143 visible=True, 

144 title_controls=[ 

145 self.get_title_control_remove(), 

146 self.get_title_control_visible()]) 

147 

148 self.update() 

149 

150 def unset_parent(self): 

151 self.unbind_state() 

152 if self._parent: 

153 if self._pipe: 

154 for actor in self._pipe.get_actors(): 

155 self._parent.remove_actor(actor) 

156 

157 self._pipe = None 

158 

159 if self._controls: 

160 self._parent.remove_panel(self._controls) 

161 self._controls = None 

162 

163 self._parent.update_view() 

164 self._parent = None 

165 

166 def move_here(self): 

167 pstate = self._parent.state 

168 state = self._state 

169 state.lat = pstate.lat 

170 state.lon = pstate.lon 

171 

172 def update(self, *args): 

173 

174 state = self._state 

175 params = (state.lat, state.lon, state.depth) 

176 

177 if self._pipe and (params != self._params or not state.visible): 

178 for actor in self._pipe.get_actors(): 

179 self._parent.remove_actor(actor) 

180 

181 self._pipe = None 

182 

183 if state.visible: 

184 if not self._pipe: 

185 print('update') 

186 

187 events = [model.Event( 

188 lat=state.lat, lon=state.lon, depth=state.depth)] 

189 

190 ray_data = [] 

191 for event in events: 

192 for station in self._stations: 

193 dist = event.distance_to(station) 

194 ray_data.append(( 

195 event, 

196 station, 

197 self._mod.arrivals( 

198 phases=[cake.PhaseDef('P')], 

199 distances=[dist*od.m2d], 

200 zstart=event.depth, 

201 zstop=0.0))) 

202 

203 self._pipe = RaysPipe(ray_data) 

204 self._params = params 

205 

206 for actor in self._pipe.get_actors(): 

207 self._parent.add_actor(actor) 

208 

209 if self._pipe: 

210 self._pipe.set_opacity(state.opacity) 

211 self._pipe.set_line_width(state.line_width) 

212 

213 self._parent.update_view() 

214 

215 def _get_controls(self): 

216 if self._controls is None: 

217 from ..state import state_bind_slider 

218 

219 frame = qw.QFrame() 

220 layout = qw.QGridLayout() 

221 layout.setAlignment(qc.Qt.AlignTop) 

222 frame.setLayout(layout) 

223 

224 state = self._state 

225 

226 iy = 0 

227 for (param, vmin, vmax) in [ 

228 ('lat', -90., 90.), 

229 ('lon', -180., 180.), 

230 ('depth', -10000., 100000.), 

231 ('line_width', 0., 5.), 

232 ('opacity', 0., 1.)]: 

233 

234 layout.addWidget(qw.QLabel(param.capitalize()), iy, 0) 

235 

236 slider = qw.QSlider(qc.Qt.Horizontal) 

237 slider.setSizePolicy(qw.QSizePolicy( 

238 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed)) 

239 

240 slider.setMinimum(int(round(vmin * 100.))) 

241 slider.setMaximum(int(round(vmax * 100.))) 

242 layout.addWidget(slider, iy, 1) 

243 

244 state_bind_slider(self, state, param, slider, factor=0.01) 

245 iy += 1 

246 

247 pb = qw.QPushButton('Move Here') 

248 layout.addWidget(pb, iy, 0) 

249 pb.clicked.connect(self.move_here) 

250 

251 iy += 1 

252 

253 layout.addWidget(qw.QFrame(), iy, 0) 

254 

255 self._controls = frame 

256 

257 return self._controls 

258 

259 

260__all__ = [ 

261 'RaysElement', 

262 'RaysState' 

263]