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._mod = cake.load_model() 

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

115 self._params = () 

116 

117 def bind_state(self, state): 

118 self.talkie_connect( 

119 state, 

120 ['visible', 'line_width', 'opacity', 'lat', 'lon', 'depth'], 

121 self.update) 

122 

123 self._state = state 

124 

125 def get_name(self): 

126 return 'Rays' 

127 

128 def set_parent(self, parent): 

129 self._parent = parent 

130 if not self._active_faults: 

131 self._active_faults = ActiveFaults() 

132 

133 self._parent.add_panel( 

134 self.get_title_label(), 

135 self._get_controls(), 

136 visible=True, 

137 title_controls=[ 

138 self.get_title_control_remove(), 

139 self.get_title_control_visible()]) 

140 

141 self.update() 

142 

143 def unset_parent(self): 

144 self.unbind_state() 

145 if self._parent: 

146 if self._pipe: 

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

148 self._parent.remove_actor(actor) 

149 

150 self._pipe = None 

151 

152 if self._controls: 

153 self._parent.remove_panel(self._controls) 

154 self._controls = None 

155 

156 self._parent.update_view() 

157 self._parent = None 

158 

159 def move_here(self): 

160 pstate = self._parent.state 

161 state = self._state 

162 state.lat = pstate.lat 

163 state.lon = pstate.lon 

164 

165 def update(self, *args): 

166 

167 state = self._state 

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

169 

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

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

172 self._parent.remove_actor(actor) 

173 

174 self._pipe = None 

175 

176 if state.visible: 

177 if not self._pipe: 

178 print('update') 

179 

180 events = [model.Event( 

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

182 

183 ray_data = [] 

184 for event in events: 

185 for station in self._stations: 

186 dist = event.distance_to(station) 

187 ray_data.append(( 

188 event, 

189 station, 

190 self._mod.arrivals( 

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

192 distances=[dist*od.m2d], 

193 zstart=event.depth, 

194 zstop=0.0))) 

195 

196 self._pipe = RaysPipe(ray_data) 

197 self._params = params 

198 

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

200 self._parent.add_actor(actor) 

201 

202 if self._pipe: 

203 self._pipe.set_opacity(state.opacity) 

204 self._pipe.set_line_width(state.line_width) 

205 

206 self._parent.update_view() 

207 

208 def _get_controls(self): 

209 if self._controls is None: 

210 from ..state import state_bind_slider 

211 

212 frame = qw.QFrame() 

213 layout = qw.QGridLayout() 

214 layout.setAlignment(qc.Qt.AlignTop) 

215 frame.setLayout(layout) 

216 

217 state = self._state 

218 

219 iy = 0 

220 for (param, vmin, vmax) in [ 

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

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

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

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

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

226 

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

228 

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

230 slider.setSizePolicy(qw.QSizePolicy( 

231 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed)) 

232 

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

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

235 layout.addWidget(slider, iy, 1) 

236 

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

238 iy += 1 

239 

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

241 layout.addWidget(pb, iy, 0) 

242 pb.clicked.connect(self.move_here) 

243 

244 iy += 1 

245 

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

247 

248 self._controls = frame 

249 

250 return self._controls 

251 

252 

253__all__ = [ 

254 'RaysElement', 

255 'RaysState' 

256]