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._mod = cake.load_model()
114 self._stations = model.load_stations('stations.txt')
115 self._params = ()
117 def bind_state(self, state):
118 self.talkie_connect(
119 state,
120 ['visible', 'line_width', 'opacity', 'lat', 'lon', 'depth'],
121 self.update)
123 self._state = state
125 def get_name(self):
126 return 'Rays'
128 def set_parent(self, parent):
129 self._parent = parent
130 if not self._active_faults:
131 self._active_faults = ActiveFaults()
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()])
141 self.update()
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)
150 self._pipe = None
152 if self._controls:
153 self._parent.remove_panel(self._controls)
154 self._controls = None
156 self._parent.update_view()
157 self._parent = None
159 def move_here(self):
160 pstate = self._parent.state
161 state = self._state
162 state.lat = pstate.lat
163 state.lon = pstate.lon
165 def update(self, *args):
167 state = self._state
168 params = (state.lat, state.lon, state.depth)
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)
174 self._pipe = None
176 if state.visible:
177 if not self._pipe:
178 print('update')
180 events = [model.Event(
181 lat=state.lat, lon=state.lon, depth=state.depth)]
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)))
196 self._pipe = RaysPipe(ray_data)
197 self._params = params
199 for actor in self._pipe.get_actors():
200 self._parent.add_actor(actor)
202 if self._pipe:
203 self._pipe.set_opacity(state.opacity)
204 self._pipe.set_line_width(state.line_width)
206 self._parent.update_view()
208 def _get_controls(self):
209 if self._controls is None:
210 from ..state import state_bind_slider
212 frame = qw.QFrame()
213 layout = qw.QGridLayout()
214 layout.setAlignment(qc.Qt.AlignTop)
215 frame.setLayout(layout)
217 state = self._state
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.)]:
227 layout.addWidget(qw.QLabel(param.capitalize()), iy, 0)
229 slider = qw.QSlider(qc.Qt.Horizontal)
230 slider.setSizePolicy(qw.QSizePolicy(
231 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed))
233 slider.setMinimum(int(round(vmin * 100.)))
234 slider.setMaximum(int(round(vmax * 100.)))
235 layout.addWidget(slider, iy, 1)
237 state_bind_slider(self, state, param, slider, factor=0.01)
238 iy += 1
240 pb = qw.QPushButton('Move Here')
241 layout.addWidget(pb, iy, 0)
242 pb.clicked.connect(self.move_here)
244 iy += 1
246 layout.addWidget(qw.QFrame(), iy, 0)
248 self._controls = frame
250 return self._controls
253__all__ = [
254 'RaysElement',
255 'RaysState'
256]