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 \
11 Object, Bool, Float, StringChoice, Timestamp, String, List
13from pyrocko import cake, table, model, geometry
14from pyrocko.client import fdsn
15from pyrocko.gui.qt_compat import qw, qc
17from pyrocko.gui.vtk_util import ScatterPipe
18from .. import common
20from .base import Element, ElementState
22guts_prefix = 'sparrow'
25def stations_to_points(stations):
26 coords = num.zeros((len(stations), 3))
28 for i, s in enumerate(stations):
29 coords[i, :] = s.lat, s.lon, -s.elevation
31 station_table = table.Table()
33 station_table.add_col(('coords', '', ('lat', 'lon', 'depth')), coords)
35 return geometry.latlondepth2xyz(
36 station_table.get_col('coords'),
37 planetradius=cake.earthradius)
40class FDSNSiteChoice(StringChoice):
41 choices = [key.upper() for key in fdsn.g_site_abbr.keys()]
44class StationSelection(Object):
45 pass
48class FDSNStationSelection(StationSelection):
49 site = String.T()
50 tmin = Timestamp.T()
51 tmax = Timestamp.T()
53 def get_stations(self):
54 return fdsn.station(
55 site=self.site,
56 format='text',
57 level='channel',
58 startbefore=self.tmin,
59 endafter=self.tmax
60 ).get_pyrocko_stations()
63class FileStationSelection(StationSelection):
64 paths = List.T(String.T())
66 def get_stations(self):
67 from pyrocko.io import stationxml
69 stations = []
70 for path in self.paths:
71 if path.split('.')[-1].lower() in ['xml']:
72 stxml = stationxml.load_xml(filename=path)
73 stations.extend(stxml.get_pyrocko_stations())
75 else:
76 stations.extend(model.load_stations(path))
78 return stations
81class StationsState(ElementState):
82 visible = Bool.T(default=True)
83 size = Float.T(default=5.0)
84 station_selection = StationSelection.T(optional=True)
86 @classmethod
87 def get_name(cls):
88 return 'Stations'
90 def create(self):
91 element = StationsElement()
92 return element
95class StationsElement(Element):
97 def __init__(self):
98 Element.__init__(self)
99 self._parent = None
100 self._pipe = None
101 self._state = None
102 self._controls = None
103 self._points = num.array([])
105 def bind_state(self, state):
106 Element.bind_state(self, state)
107 for var in ['visible', 'size', 'station_selection']:
108 self.register_state_listener3(self.update, state, var)
110 self._current_selection = None
112 def unbind_state(self):
113 self._listeners = []
115 def get_name(self):
116 return 'Stations'
118 def set_parent(self, parent):
119 self._parent = parent
120 self._parent.add_panel(
121 self.get_name(),
122 self._get_controls(),
123 visible=True,
124 title_controls=[
125 self.get_title_control_remove(),
126 self.get_title_control_visible()])
127 self.update()
129 def unset_parent(self):
130 self.unbind_state()
131 if self._parent:
132 if self._pipe:
133 self._parent.remove_actor(self._pipe.actor)
134 self._pipe = None
136 if self._controls:
137 self._parent.remove_panel(self._controls)
138 self._controls = None
140 self._parent.update_view()
141 self._parent = None
143 def update(self, *args):
144 state = self._state
145 if self._pipe and \
146 self._current_selection is not state.station_selection:
148 self._parent.remove_actor(self._pipe.actor)
149 self._pipe = None
151 if self._pipe and not state.visible:
152 self._parent.remove_actor(self._pipe.actor)
153 self._pipe.set_size(state.size)
155 if state.visible:
156 if self._current_selection is not state.station_selection:
157 stations = state.station_selection.get_stations()
158 points = stations_to_points(stations)
159 self._pipe = ScatterPipe(points)
160 self._parent.add_actor(self._pipe.actor)
161 elif self._pipe:
162 self._parent.add_actor(self._pipe.actor)
164 if self._pipe:
165 self._pipe.set_size(state.size)
167 self._parent.update_view()
168 self._current_selection = state.station_selection
170 def open_file_load_dialog(self):
171 caption = 'Select one or more files to open'
173 fns, _ = qw.QFileDialog.getOpenFileNames(
174 self._parent, caption, options=common.qfiledialog_options)
176 self._state.station_selection = FileStationSelection(
177 paths=[str(fn) for fn in fns])
179 def open_fdsn_load_dialog(self):
180 dialog = qw.QDialog(self._parent)
181 dialog.setWindowTitle('Get stations from FDSN web service')
183 layout = qw.QHBoxLayout(dialog)
185 layout.addWidget(qw.QLabel('Site'))
187 sites = [key.upper() for key in fdsn.g_site_abbr.keys()]
189 cb = qw.QComboBox()
190 for i, s in enumerate(sites):
191 cb.insertItem(i, s)
193 layout.addWidget(cb)
195 pb = qw.QPushButton('Cancel')
196 pb.clicked.connect(dialog.reject)
197 layout.addWidget(pb)
199 pb = qw.QPushButton('Ok')
200 pb.clicked.connect(dialog.accept)
201 layout.addWidget(pb)
203 dialog.exec_()
205 site = str(cb.currentText()).lower()
207 vstate = self._parent.state
209 if dialog.result() == qw.QDialog.Accepted:
210 self._state.station_selection = FDSNStationSelection(
211 site=site,
212 tmin=vstate.tmin,
213 tmax=vstate.tmax)
215 def _get_controls(self):
216 if not self._controls:
217 from ..state import state_bind_slider
219 frame = qw.QFrame()
220 layout = qw.QGridLayout()
221 frame.setLayout(layout)
223 layout.addWidget(qw.QLabel('Size'), 0, 0)
225 slider = qw.QSlider(qc.Qt.Horizontal)
226 slider.setSizePolicy(
227 qw.QSizePolicy(
228 qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed))
229 slider.setMinimum(0)
230 slider.setMaximum(10)
231 slider.setSingleStep(1)
232 slider.setPageStep(1)
233 layout.addWidget(slider, 0, 1)
234 state_bind_slider(self, self._state, 'size', slider)
236 lab = qw.QLabel('Load from:')
237 pb_file = qw.QPushButton('File')
238 pb_fdsn = qw.QPushButton('FDSN')
240 layout.addWidget(lab, 1, 0)
241 layout.addWidget(pb_file, 1, 1)
242 layout.addWidget(pb_fdsn, 1, 2)
244 pb_file.clicked.connect(self.open_file_load_dialog)
245 pb_fdsn.clicked.connect(self.open_fdsn_load_dialog)
247 layout.addWidget(qw.QFrame(), 2, 0, 1, 3)
249 self._controls = frame
251 return self._controls
254__all__ = [
255 'StationsElement',
256 'StationsState',
257]