1# https://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6import numpy as num
8from pyrocko.guts import Bool, Float
9from pyrocko.gui.qt_compat import qw, qc
11from pyrocko.dataset.active_faults import ActiveFaults
12from pyrocko.gui import vtk_util
13from pyrocko import plot, orthodrome as od, model, cake
14import vtk
16from .base import Element, ElementState
18guts_prefix = 'sparrow'
20km = 1e3
23def color(x):
24 return num.array(plot.to01(plot.color(x)), dtype=num.float64)
27def to_latlondepth(event, station, rays):
29 lines = []
30 azimuth, _ = event.azibazi_to(station)
31 for ray in rays:
32 fanz, fanx, _ = ray.zxt_path_subdivided()
34 for zs, xs in zip(fanz, fanx):
35 lats, lons = od.azidist_to_latlon(
36 event.lat, event.lon, azimuth, xs)
38 line = num.zeros((xs.size, 3))
39 line[:, 0] = lats
40 line[:, 1] = lons
41 line[:, 2] = zs
43 lines.append(line)
45 return lines
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)
56 def create(self):
57 element = RaysElement()
58 element.bind_state(self)
59 return element
62class RaysPipe(object):
63 def __init__(self, ray_data):
65 self._opacity = 1.0
66 self._line_width = 1.0
67 self._actors = {}
69 mapper = vtk.vtkDataSetMapper()
70 lines = []
71 for event, station, rays in ray_data:
72 lines.extend(to_latlondepth(event, station, rays))
74 grid = vtk_util.make_multi_polyline(
75 lines_latlondepth=lines)
77 vtk_util.vtk_set_input(mapper, grid)
79 actor = vtk.vtkActor()
80 actor.SetMapper(mapper)
82 self._actors['ray'] = actor
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)
90 self._opacity = opacity
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)
98 self._line_width = width
100 def get_actors(self):
101 return [self._actors[k] for k in sorted(self._actors.keys())]
104class RaysElement(Element):
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 = ()
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']:
125 self.register_state_listener(self.update, state, var)
127 self._state = state
129 def unbind_state(self):
130 self._listeners = []
132 def get_name(self):
133 return 'Rays'
135 def set_parent(self, parent):
136 self._parent = parent
137 if not self._active_faults:
138 self._active_faults = ActiveFaults()
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()])
148 self.update()
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)
157 self._pipe = None
159 if self._controls:
160 self._parent.remove_panel(self._controls)
161 self._controls = None
163 self._parent.update_view()
164 self._parent = None
166 def move_here(self):
167 pstate = self._parent.state
168 state = self._state
169 state.lat = pstate.lat
170 state.lon = pstate.lon
172 def update(self, *args):
174 state = self._state
175 params = (state.lat, state.lon, state.depth)
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)
181 self._pipe = None
183 if state.visible:
184 if not self._pipe:
185 print('update')
187 events = [model.Event(
188 lat=state.lat, lon=state.lon, depth=state.depth)]
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)))
203 self._pipe = RaysPipe(ray_data)
204 self._params = params
206 for actor in self._pipe.get_actors():
207 self._parent.add_actor(actor)
209 if self._pipe:
210 self._pipe.set_opacity(state.opacity)
211 self._pipe.set_line_width(state.line_width)
213 self._parent.update_view()
215 def _get_controls(self):
216 if self._controls is None:
217 from ..state import state_bind_slider
219 frame = qw.QFrame()
220 layout = qw.QGridLayout()
221 layout.setAlignment(qc.Qt.AlignTop)
222 frame.setLayout(layout)
224 state = self._state
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.)]:
234 layout.addWidget(qw.QLabel(param.capitalize()), iy, 0)
236 slider = qw.QSlider(qc.Qt.Horizontal)
237 slider.setSizePolicy(qw.QSizePolicy(
238 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed))
240 slider.setMinimum(int(round(vmin * 100.)))
241 slider.setMaximum(int(round(vmax * 100.)))
242 layout.addWidget(slider, iy, 1)
244 state_bind_slider(self, state, param, slider, factor=0.01)
245 iy += 1
247 pb = qw.QPushButton('Move Here')
248 layout.addWidget(pb, iy, 0)
249 pb.clicked.connect(self.move_here)
251 iy += 1
253 layout.addWidget(qw.QFrame(), iy, 0)
255 self._controls = frame
257 return self._controls
260__all__ = [
261 'RaysElement',
262 'RaysState'
263]