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 numpy as num 

9 

10from pyrocko.guts import Bool, Float 

11from pyrocko.gui.qt_compat import qw, qc 

12 

13from pyrocko.dataset.active_faults import ActiveFaults 

14from pyrocko.gui import vtk_util 

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

16import vtk 

17 

18from .base import Element, ElementState 

19 

20guts_prefix = 'sparrow' 

21 

22km = 1e3 

23 

24 

25def color(x): 

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

27 

28 

29def to_latlondepth(event, station, rays): 

30 

31 lines = [] 

32 azimuth, _ = event.azibazi_to(station) 

33 for ray in rays: 

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

35 

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

37 lats, lons = od.azidist_to_latlon( 

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

39 

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

41 line[:, 0] = lats 

42 line[:, 1] = lons 

43 line[:, 2] = zs 

44 

45 lines.append(line) 

46 

47 return lines 

48 

49 

50class RaysState(ElementState): 

51 visible = Bool.T(default=True) 

52 line_width = Float.T(default=1.0) 

53 opacity = Float.T(default=1.0) 

54 lat = Float.T(default=0.0) 

55 lon = Float.T(default=0.0) 

56 depth = Float.T(default=10000.0) 

57 

58 def create(self): 

59 element = RaysElement() 

60 element.bind_state(self) 

61 return element 

62 

63 

64class RaysPipe(object): 

65 def __init__(self, ray_data): 

66 

67 self._opacity = 1.0 

68 self._line_width = 1.0 

69 self._actors = {} 

70 

71 mapper = vtk.vtkDataSetMapper() 

72 lines = [] 

73 for event, station, rays in ray_data: 

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

75 

76 grid = vtk_util.make_multi_polyline( 

77 lines_latlondepth=lines) 

78 

79 vtk_util.vtk_set_input(mapper, grid) 

80 

81 actor = vtk.vtkActor() 

82 actor.SetMapper(mapper) 

83 

84 self._actors['ray'] = actor 

85 

86 def set_opacity(self, opacity): 

87 opacity = float(opacity) 

88 if self._opacity != opacity: 

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

90 actor.GetProperty().SetOpacity(opacity) 

91 

92 self._opacity = opacity 

93 

94 def set_line_width(self, width): 

95 width = float(width) 

96 if self._line_width != width: 

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

98 actor.GetProperty().SetLineWidth(width) 

99 

100 self._line_width = width 

101 

102 def get_actors(self): 

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

104 

105 

106class RaysElement(Element): 

107 

108 def __init__(self): 

109 Element.__init__(self) 

110 self._parent = None 

111 self._state = None 

112 self._pipe = None 

113 self._controls = None 

114 self._active_faults = None 

115 self._listeners = [] 

116 self._mod = cake.load_model() 

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

118 self._params = () 

119 

120 def bind_state(self, state): 

121 upd = self.update 

122 self._listeners.append(upd) 

123 for var in [ 

124 'visible', 'line_width', 'opacity', 

125 'lat', 'lon', 'depth']: 

126 

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

128 

129 self._state = state 

130 

131 def unbind_state(self): 

132 self._listeners = [] 

133 

134 def get_name(self): 

135 return 'Rays' 

136 

137 def set_parent(self, parent): 

138 self._parent = parent 

139 if not self._active_faults: 

140 self._active_faults = ActiveFaults() 

141 

142 self._parent.add_panel( 

143 self.get_name(), 

144 self._get_controls(), 

145 visible=True, 

146 title_controls=[ 

147 self.get_title_control_remove(), 

148 self.get_title_control_visible()]) 

149 

150 self.update() 

151 

152 def unset_parent(self): 

153 self.unbind_state() 

154 if self._parent: 

155 if self._pipe: 

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

157 self._parent.remove_actor(actor) 

158 

159 self._pipe = None 

160 

161 if self._controls: 

162 self._parent.remove_panel(self._controls) 

163 self._controls = None 

164 

165 self._parent.update_view() 

166 self._parent = None 

167 

168 def move_here(self): 

169 pstate = self._parent.state 

170 state = self._state 

171 state.lat = pstate.lat 

172 state.lon = pstate.lon 

173 

174 def update(self, *args): 

175 

176 state = self._state 

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

178 

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

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

181 self._parent.remove_actor(actor) 

182 

183 self._pipe = None 

184 

185 if state.visible: 

186 if not self._pipe: 

187 print('update') 

188 

189 events = [model.Event( 

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

191 

192 ray_data = [] 

193 for event in events: 

194 for station in self._stations: 

195 dist = event.distance_to(station) 

196 ray_data.append(( 

197 event, 

198 station, 

199 self._mod.arrivals( 

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

201 distances=[dist*od.m2d], 

202 zstart=event.depth, 

203 zstop=0.0))) 

204 

205 self._pipe = RaysPipe(ray_data) 

206 self._params = params 

207 

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

209 self._parent.add_actor(actor) 

210 

211 if self._pipe: 

212 self._pipe.set_opacity(state.opacity) 

213 self._pipe.set_line_width(state.line_width) 

214 

215 self._parent.update_view() 

216 

217 def _get_controls(self): 

218 if self._controls is None: 

219 from ..state import state_bind_slider 

220 

221 frame = qw.QFrame() 

222 layout = qw.QGridLayout() 

223 layout.setAlignment(qc.Qt.AlignTop) 

224 frame.setLayout(layout) 

225 

226 state = self._state 

227 

228 iy = 0 

229 for (param, vmin, vmax) in [ 

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

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

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

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

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

235 

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

237 

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

239 slider.setSizePolicy(qw.QSizePolicy( 

240 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed)) 

241 

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

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

244 layout.addWidget(slider, iy, 1) 

245 

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

247 iy += 1 

248 

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

250 layout.addWidget(pb, iy, 0) 

251 pb.clicked.connect(self.move_here) 

252 

253 iy += 1 

254 

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

256 

257 self._controls = frame 

258 

259 return self._controls 

260 

261 

262__all__ = [ 

263 'RaysElement', 

264 'RaysState' 

265]