1# https://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

4# ---|P------/S----------~Lg---------- 

5 

6import numpy as num 

7 

8from pyrocko.guts import \ 

9 Object, Bool, Float, StringChoice, Timestamp, String, List 

10 

11from pyrocko import cake, table, model, geometry 

12from pyrocko.client import fdsn 

13from pyrocko.gui.qt_compat import qw, qc 

14 

15from pyrocko.gui.vtk_util import ScatterPipe 

16from .. import common 

17 

18from .base import Element, ElementState 

19 

20guts_prefix = 'sparrow' 

21 

22 

23def stations_to_points(stations): 

24 coords = num.zeros((len(stations), 3)) 

25 

26 for i, s in enumerate(stations): 

27 coords[i, :] = s.lat, s.lon, -s.elevation 

28 

29 station_table = table.Table() 

30 

31 station_table.add_col(('coords', '', ('lat', 'lon', 'depth')), coords) 

32 

33 return geometry.latlondepth2xyz( 

34 station_table.get_col('coords'), 

35 planetradius=cake.earthradius) 

36 

37 

38class FDSNSiteChoice(StringChoice): 

39 choices = [key.upper() for key in fdsn.g_site_abbr.keys()] 

40 

41 

42class StationSelection(Object): 

43 pass 

44 

45 

46class FDSNStationSelection(StationSelection): 

47 site = String.T() 

48 tmin = Timestamp.T() 

49 tmax = Timestamp.T() 

50 

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() 

59 

60 

61class FileStationSelection(StationSelection): 

62 paths = List.T(String.T()) 

63 

64 def get_stations(self): 

65 from pyrocko.io import stationxml 

66 

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()) 

72 

73 else: 

74 stations.extend(model.load_stations(path)) 

75 

76 return stations 

77 

78 

79class StationsState(ElementState): 

80 visible = Bool.T(default=True) 

81 size = Float.T(default=5.0) 

82 station_selection = StationSelection.T(optional=True) 

83 

84 @classmethod 

85 def get_name(cls): 

86 return 'Stations' 

87 

88 def create(self): 

89 element = StationsElement() 

90 return element 

91 

92 

93class StationsElement(Element): 

94 

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([]) 

102 

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) 

107 

108 self._current_selection = None 

109 

110 def unbind_state(self): 

111 self._listeners = [] 

112 

113 def get_name(self): 

114 return 'Stations' 

115 

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() 

126 

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 

133 

134 if self._controls: 

135 self._parent.remove_panel(self._controls) 

136 self._controls = None 

137 

138 self._parent.update_view() 

139 self._parent = None 

140 

141 def update(self, *args): 

142 state = self._state 

143 if self._pipe and \ 

144 self._current_selection is not state.station_selection: 

145 

146 self._parent.remove_actor(self._pipe.actor) 

147 self._pipe = None 

148 

149 if self._pipe and not state.visible: 

150 self._parent.remove_actor(self._pipe.actor) 

151 self._pipe.set_size(state.size) 

152 

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) 

161 

162 if self._pipe: 

163 self._pipe.set_size(state.size) 

164 

165 self._parent.update_view() 

166 self._current_selection = state.station_selection 

167 

168 def open_file_load_dialog(self): 

169 caption = 'Select one or more files to open' 

170 

171 fns, _ = qw.QFileDialog.getOpenFileNames( 

172 self._parent, caption, options=common.qfiledialog_options) 

173 

174 self._state.station_selection = FileStationSelection( 

175 paths=[str(fn) for fn in fns]) 

176 

177 def open_fdsn_load_dialog(self): 

178 dialog = qw.QDialog(self._parent) 

179 dialog.setWindowTitle('Get stations from FDSN web service') 

180 

181 layout = qw.QHBoxLayout(dialog) 

182 

183 layout.addWidget(qw.QLabel('Site')) 

184 

185 sites = [key.upper() for key in fdsn.g_site_abbr.keys()] 

186 

187 cb = qw.QComboBox() 

188 for i, s in enumerate(sites): 

189 cb.insertItem(i, s) 

190 

191 layout.addWidget(cb) 

192 

193 pb = qw.QPushButton('Cancel') 

194 pb.clicked.connect(dialog.reject) 

195 layout.addWidget(pb) 

196 

197 pb = qw.QPushButton('Ok') 

198 pb.clicked.connect(dialog.accept) 

199 layout.addWidget(pb) 

200 

201 dialog.exec_() 

202 

203 site = str(cb.currentText()).lower() 

204 

205 vstate = self._parent.state 

206 

207 if dialog.result() == qw.QDialog.Accepted: 

208 self._state.station_selection = FDSNStationSelection( 

209 site=site, 

210 tmin=vstate.tmin, 

211 tmax=vstate.tmax) 

212 

213 def _get_controls(self): 

214 if not self._controls: 

215 from ..state import state_bind_slider 

216 

217 frame = qw.QFrame() 

218 layout = qw.QGridLayout() 

219 frame.setLayout(layout) 

220 

221 layout.addWidget(qw.QLabel('Size'), 0, 0) 

222 

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) 

233 

234 lab = qw.QLabel('Load from:') 

235 pb_file = qw.QPushButton('File') 

236 pb_fdsn = qw.QPushButton('FDSN') 

237 

238 layout.addWidget(lab, 1, 0) 

239 layout.addWidget(pb_file, 1, 1) 

240 layout.addWidget(pb_fdsn, 1, 2) 

241 

242 pb_file.clicked.connect(self.open_file_load_dialog) 

243 pb_fdsn.clicked.connect(self.open_fdsn_load_dialog) 

244 

245 layout.addWidget(qw.QFrame(), 2, 0, 1, 3) 

246 

247 self._controls = frame 

248 

249 return self._controls 

250 

251 

252__all__ = [ 

253 'StationsElement', 

254 'StationsState', 

255]