1# https://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6import numpy as num
8from pyrocko.guts import \
9 Object, Bool, Float, StringChoice, Timestamp, String, List
11from pyrocko import cake, table, model, geometry
12from pyrocko.client import fdsn
13from pyrocko.gui.qt_compat import qw, qc
15from pyrocko.gui.vtk_util import ScatterPipe
16from .. import common
18from .base import Element, ElementState
20guts_prefix = 'sparrow'
23def stations_to_points(stations):
24 coords = num.zeros((len(stations), 3))
26 for i, s in enumerate(stations):
27 coords[i, :] = s.lat, s.lon, -s.elevation
29 station_table = table.Table()
31 station_table.add_col(('coords', '', ('lat', 'lon', 'depth')), coords)
33 return geometry.latlondepth2xyz(
34 station_table.get_col('coords'),
35 planetradius=cake.earthradius)
38class FDSNSiteChoice(StringChoice):
39 choices = [key.upper() for key in fdsn.g_site_abbr.keys()]
42class StationSelection(Object):
43 pass
46class FDSNStationSelection(StationSelection):
47 site = String.T()
48 tmin = Timestamp.T()
49 tmax = Timestamp.T()
51 def get_stations(self):
52 return fdsn.station(
53 site=self.site,
54 format='text',
55 level='channel',
56 startbefore=self.tmin,
57 endafter=self.tmax
58 ).get_pyrocko_stations()
61class FileStationSelection(StationSelection):
62 paths = List.T(String.T())
64 def get_stations(self):
65 from pyrocko.io import stationxml
67 stations = []
68 for path in self.paths:
69 if path.split('.')[-1].lower() in ['xml']:
70 stxml = stationxml.load_xml(filename=path)
71 stations.extend(stxml.get_pyrocko_stations())
73 else:
74 stations.extend(model.load_stations(path))
76 return stations
79class StationsState(ElementState):
80 visible = Bool.T(default=True)
81 size = Float.T(default=5.0)
82 station_selection = StationSelection.T(optional=True)
84 @classmethod
85 def get_name(cls):
86 return 'Stations'
88 def create(self):
89 element = StationsElement()
90 return element
93class StationsElement(Element):
95 def __init__(self):
96 Element.__init__(self)
97 self._parent = None
98 self._pipe = None
99 self._state = None
100 self._controls = None
101 self._points = num.array([])
103 def bind_state(self, state):
104 Element.bind_state(self, state)
105 for var in ['visible', 'size', 'station_selection']:
106 self.register_state_listener3(self.update, state, var)
108 self._current_selection = None
110 def unbind_state(self):
111 self._listeners = []
113 def get_name(self):
114 return 'Stations'
116 def set_parent(self, parent):
117 self._parent = parent
118 self._parent.add_panel(
119 self.get_name(),
120 self._get_controls(),
121 visible=True,
122 title_controls=[
123 self.get_title_control_remove(),
124 self.get_title_control_visible()])
125 self.update()
127 def unset_parent(self):
128 self.unbind_state()
129 if self._parent:
130 if self._pipe:
131 self._parent.remove_actor(self._pipe.actor)
132 self._pipe = None
134 if self._controls:
135 self._parent.remove_panel(self._controls)
136 self._controls = None
138 self._parent.update_view()
139 self._parent = None
141 def update(self, *args):
142 state = self._state
143 if self._pipe and \
144 self._current_selection is not state.station_selection:
146 self._parent.remove_actor(self._pipe.actor)
147 self._pipe = None
149 if self._pipe and not state.visible:
150 self._parent.remove_actor(self._pipe.actor)
151 self._pipe.set_size(state.size)
153 if state.visible:
154 if self._current_selection is not state.station_selection:
155 stations = state.station_selection.get_stations()
156 points = stations_to_points(stations)
157 self._pipe = ScatterPipe(points)
158 self._parent.add_actor(self._pipe.actor)
159 elif self._pipe:
160 self._parent.add_actor(self._pipe.actor)
162 if self._pipe:
163 self._pipe.set_size(state.size)
165 self._parent.update_view()
166 self._current_selection = state.station_selection
168 def open_file_load_dialog(self):
169 caption = 'Select one or more files to open'
171 fns, _ = qw.QFileDialog.getOpenFileNames(
172 self._parent, caption, options=common.qfiledialog_options)
174 self._state.station_selection = FileStationSelection(
175 paths=[str(fn) for fn in fns])
177 def open_fdsn_load_dialog(self):
178 dialog = qw.QDialog(self._parent)
179 dialog.setWindowTitle('Get stations from FDSN web service')
181 layout = qw.QHBoxLayout(dialog)
183 layout.addWidget(qw.QLabel('Site'))
185 sites = [key.upper() for key in fdsn.g_site_abbr.keys()]
187 cb = qw.QComboBox()
188 for i, s in enumerate(sites):
189 cb.insertItem(i, s)
191 layout.addWidget(cb)
193 pb = qw.QPushButton('Cancel')
194 pb.clicked.connect(dialog.reject)
195 layout.addWidget(pb)
197 pb = qw.QPushButton('Ok')
198 pb.clicked.connect(dialog.accept)
199 layout.addWidget(pb)
201 dialog.exec_()
203 site = str(cb.currentText()).lower()
205 vstate = self._parent.state
207 if dialog.result() == qw.QDialog.Accepted:
208 self._state.station_selection = FDSNStationSelection(
209 site=site,
210 tmin=vstate.tmin,
211 tmax=vstate.tmax)
213 def _get_controls(self):
214 if not self._controls:
215 from ..state import state_bind_slider
217 frame = qw.QFrame()
218 layout = qw.QGridLayout()
219 frame.setLayout(layout)
221 layout.addWidget(qw.QLabel('Size'), 0, 0)
223 slider = qw.QSlider(qc.Qt.Horizontal)
224 slider.setSizePolicy(
225 qw.QSizePolicy(
226 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed))
227 slider.setMinimum(0)
228 slider.setMaximum(10)
229 slider.setSingleStep(1)
230 slider.setPageStep(1)
231 layout.addWidget(slider, 0, 1)
232 state_bind_slider(self, self._state, 'size', slider)
234 lab = qw.QLabel('Load from:')
235 pb_file = qw.QPushButton('File')
236 pb_fdsn = qw.QPushButton('FDSN')
238 layout.addWidget(lab, 1, 0)
239 layout.addWidget(pb_file, 1, 1)
240 layout.addWidget(pb_fdsn, 1, 2)
242 pb_file.clicked.connect(self.open_file_load_dialog)
243 pb_fdsn.clicked.connect(self.open_fdsn_load_dialog)
245 layout.addWidget(qw.QFrame(), 2, 0, 1, 3)
247 self._controls = frame
249 return self._controls
252__all__ = [
253 'StationsElement',
254 'StationsState',
255]