1# https://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

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

5 

6from __future__ import absolute_import, print_function, division 

7 

8import numpy as num 

9 

10from pyrocko.guts import \ 

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

12 

13from pyrocko import cake, table, model, geometry 

14from pyrocko.client import fdsn 

15from pyrocko.gui.qt_compat import qw, qc 

16 

17from pyrocko.gui.vtk_util import ScatterPipe 

18from .. import common 

19 

20from .base import Element, ElementState 

21 

22guts_prefix = 'sparrow' 

23 

24 

25def stations_to_points(stations): 

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

27 

28 for i, s in enumerate(stations): 

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

30 

31 station_table = table.Table() 

32 

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

34 

35 return geometry.latlondepth2xyz( 

36 station_table.get_col('coords'), 

37 planetradius=cake.earthradius) 

38 

39 

40class FDSNSiteChoice(StringChoice): 

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

42 

43 

44class StationSelection(Object): 

45 pass 

46 

47 

48class FDSNStationSelection(StationSelection): 

49 site = String.T() 

50 tmin = Timestamp.T() 

51 tmax = Timestamp.T() 

52 

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

61 

62 

63class FileStationSelection(StationSelection): 

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

65 

66 def get_stations(self): 

67 from pyrocko.io import stationxml 

68 

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

74 

75 else: 

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

77 

78 return stations 

79 

80 

81class StationsState(ElementState): 

82 visible = Bool.T(default=True) 

83 size = Float.T(default=5.0) 

84 station_selection = StationSelection.T(optional=True) 

85 

86 @classmethod 

87 def get_name(cls): 

88 return 'Stations' 

89 

90 def create(self): 

91 element = StationsElement() 

92 return element 

93 

94 

95class StationsElement(Element): 

96 

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

104 

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) 

109 

110 self._current_selection = None 

111 

112 def unbind_state(self): 

113 self._listeners = [] 

114 

115 def get_name(self): 

116 return 'Stations' 

117 

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

128 

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 

135 

136 if self._controls: 

137 self._parent.remove_panel(self._controls) 

138 self._controls = None 

139 

140 self._parent.update_view() 

141 self._parent = None 

142 

143 def update(self, *args): 

144 state = self._state 

145 if self._pipe and \ 

146 self._current_selection is not state.station_selection: 

147 

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

149 self._pipe = None 

150 

151 if self._pipe and not state.visible: 

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

153 self._pipe.set_size(state.size) 

154 

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) 

163 

164 if self._pipe: 

165 self._pipe.set_size(state.size) 

166 

167 self._parent.update_view() 

168 self._current_selection = state.station_selection 

169 

170 def open_file_load_dialog(self): 

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

172 

173 fns, _ = qw.QFileDialog.getOpenFileNames( 

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

175 

176 self._state.station_selection = FileStationSelection( 

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

178 

179 def open_fdsn_load_dialog(self): 

180 dialog = qw.QDialog(self._parent) 

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

182 

183 layout = qw.QHBoxLayout(dialog) 

184 

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

186 

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

188 

189 cb = qw.QComboBox() 

190 for i, s in enumerate(sites): 

191 cb.insertItem(i, s) 

192 

193 layout.addWidget(cb) 

194 

195 pb = qw.QPushButton('Cancel') 

196 pb.clicked.connect(dialog.reject) 

197 layout.addWidget(pb) 

198 

199 pb = qw.QPushButton('Ok') 

200 pb.clicked.connect(dialog.accept) 

201 layout.addWidget(pb) 

202 

203 dialog.exec_() 

204 

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

206 

207 vstate = self._parent.state 

208 

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

210 self._state.station_selection = FDSNStationSelection( 

211 site=site, 

212 tmin=vstate.tmin, 

213 tmax=vstate.tmax) 

214 

215 def _get_controls(self): 

216 if not self._controls: 

217 from ..state import state_bind_slider 

218 

219 frame = qw.QFrame() 

220 layout = qw.QGridLayout() 

221 frame.setLayout(layout) 

222 

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

224 

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) 

235 

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

237 pb_file = qw.QPushButton('File') 

238 pb_fdsn = qw.QPushButton('FDSN') 

239 

240 layout.addWidget(lab, 1, 0) 

241 layout.addWidget(pb_file, 1, 1) 

242 layout.addWidget(pb_fdsn, 1, 2) 

243 

244 pb_file.clicked.connect(self.open_file_load_dialog) 

245 pb_fdsn.clicked.connect(self.open_fdsn_load_dialog) 

246 

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

248 

249 self._controls = frame 

250 

251 return self._controls 

252 

253 

254__all__ = [ 

255 'StationsElement', 

256 'StationsState', 

257]