1# https://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6from __future__ import absolute_import, print_function, division
8import numpy as num
10from pyrocko.guts import Bool, Float
11from pyrocko.gui.qt_compat import qw, qc
13from pyrocko.dataset.active_faults import ActiveFaults
14from pyrocko.gui import vtk_util
15from pyrocko import plot, orthodrome as od, model, cake
16import vtk
18from .base import Element, ElementState
20guts_prefix = 'sparrow'
22km = 1e3
25def color(x):
26 return num.array(plot.to01(plot.color(x)), dtype=num.float64)
29def to_latlondepth(event, station, rays):
31 lines = []
32 azimuth, _ = event.azibazi_to(station)
33 for ray in rays:
34 fanz, fanx, _ = ray.zxt_path_subdivided()
36 for zs, xs in zip(fanz, fanx):
37 lats, lons = od.azidist_to_latlon(
38 event.lat, event.lon, azimuth, xs)
40 line = num.zeros((xs.size, 3))
41 line[:, 0] = lats
42 line[:, 1] = lons
43 line[:, 2] = zs
45 lines.append(line)
47 return lines
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)
58 def create(self):
59 element = RaysElement()
60 element.bind_state(self)
61 return element
64class RaysPipe(object):
65 def __init__(self, ray_data):
67 self._opacity = 1.0
68 self._line_width = 1.0
69 self._actors = {}
71 mapper = vtk.vtkDataSetMapper()
72 lines = []
73 for event, station, rays in ray_data:
74 lines.extend(to_latlondepth(event, station, rays))
76 grid = vtk_util.make_multi_polyline(
77 lines_latlondepth=lines)
79 vtk_util.vtk_set_input(mapper, grid)
81 actor = vtk.vtkActor()
82 actor.SetMapper(mapper)
84 self._actors['ray'] = actor
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)
92 self._opacity = opacity
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)
100 self._line_width = width
102 def get_actors(self):
103 return [self._actors[k] for k in sorted(self._actors.keys())]
106class RaysElement(Element):
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 = ()
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']:
127 self.register_state_listener(self.update, state, var)
129 self._state = state
131 def unbind_state(self):
132 self._listeners = []
134 def get_name(self):
135 return 'Rays'
137 def set_parent(self, parent):
138 self._parent = parent
139 if not self._active_faults:
140 self._active_faults = ActiveFaults()
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()])
150 self.update()
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)
159 self._pipe = None
161 if self._controls:
162 self._parent.remove_panel(self._controls)
163 self._controls = None
165 self._parent.update_view()
166 self._parent = None
168 def move_here(self):
169 pstate = self._parent.state
170 state = self._state
171 state.lat = pstate.lat
172 state.lon = pstate.lon
174 def update(self, *args):
176 state = self._state
177 params = (state.lat, state.lon, state.depth)
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)
183 self._pipe = None
185 if state.visible:
186 if not self._pipe:
187 print('update')
189 events = [model.Event(
190 lat=state.lat, lon=state.lon, depth=state.depth)]
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)))
205 self._pipe = RaysPipe(ray_data)
206 self._params = params
208 for actor in self._pipe.get_actors():
209 self._parent.add_actor(actor)
211 if self._pipe:
212 self._pipe.set_opacity(state.opacity)
213 self._pipe.set_line_width(state.line_width)
215 self._parent.update_view()
217 def _get_controls(self):
218 if self._controls is None:
219 from ..state import state_bind_slider
221 frame = qw.QFrame()
222 layout = qw.QGridLayout()
223 layout.setAlignment(qc.Qt.AlignTop)
224 frame.setLayout(layout)
226 state = self._state
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.)]:
236 layout.addWidget(qw.QLabel(param.capitalize()), iy, 0)
238 slider = qw.QSlider(qc.Qt.Horizontal)
239 slider.setSizePolicy(qw.QSizePolicy(
240 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed))
242 slider.setMinimum(int(round(vmin * 100.)))
243 slider.setMaximum(int(round(vmax * 100.)))
244 layout.addWidget(slider, iy, 1)
246 state_bind_slider(self, state, param, slider, factor=0.01)
247 iy += 1
249 pb = qw.QPushButton('Move Here')
250 layout.addWidget(pb, iy, 0)
251 pb.clicked.connect(self.move_here)
253 iy += 1
255 layout.addWidget(qw.QFrame(), iy, 0)
257 self._controls = frame
259 return self._controls
262__all__ = [
263 'RaysElement',
264 'RaysState'
265]