1# https://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

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

5 

6''' 

7Effective seismological trace viewer. 

8''' 

9 

10import os 

11import sys 

12import logging 

13import gc 

14import tempfile 

15import shutil 

16import glob 

17 

18from os.path import join as pjoin 

19from optparse import OptionParser 

20 

21 

22from pyrocko import pile as pile_mod 

23from pyrocko import util 

24from pyrocko import model 

25from pyrocko import config 

26from pyrocko import io 

27from pyrocko.io import stationxml 

28 

29from . import marker 

30 

31 

32logger = logging.getLogger('pyrocko.gui.snuffler.snuffler') 

33 

34app = None 

35 

36 

37def get_snuffler_instance(): 

38 from .snuffler_app import Snuffler 

39 from ..qt_compat import qg 

40 import locale 

41 locale.setlocale(locale.LC_ALL, 'C') 

42 global app 

43 if app is None: 

44 qg.QSurfaceFormat.setDefaultFormat(qg.QSurfaceFormat()) 

45 app = Snuffler() 

46 return app 

47 

48 

49def extend_paths(paths): 

50 paths_r = [] 

51 for p in paths: 

52 paths_r.extend(glob.glob(p)) 

53 return paths_r 

54 

55 

56def snuffle(pile=None, **kwargs): 

57 ''' 

58 View pile in a snuffler window. 

59 

60 :param pile: :py:class:`pile.Pile` object to be visualized 

61 :param stations: list of `pyrocko.model.Station` objects or ``None`` 

62 :param events: list of `pyrocko.model.Event` objects or ``None`` 

63 :param markers: list of `pyrocko.gui.snuffler.util.Marker` objects or 

64 ``None`` 

65 :param ntracks: float, number of tracks to be shown initially (default: 12) 

66 :param marker_editor_sortable: bool, whether to allow sorting in marker 

67 table (default True). Disabling this will give better performance 

68 when working with many markers. 

69 :param follow: time interval (in seconds) for real time follow mode or 

70 ``None`` 

71 :param controls: bool, whether to show the main controls (default: 

72 ``True``) 

73 :param opengl: bool, whether to use opengl (default: ``None`` - automatic 

74 choice). 

75 :param paths: list of files and directories to search for trace files 

76 :param pattern: regex which filenames must match 

77 :param format: format of input files 

78 :param cache_dir: cache directory with trace meta information 

79 :param force_cache: bool, whether to use the cache when attribute spoofing 

80 is active 

81 :param store_path: filename template, where to store trace data from input 

82 streams 

83 :param store_interval: float, time interval (in seconds) between stream 

84 buffer dumps 

85 :param want_markers: bool, whether markers should be returned 

86 :param launch_hook: callback function called before snuffler window is 

87 shown 

88 :param instant_close: bool, whether to bypass close window confirmation 

89 dialog 

90 ''' 

91 from .snuffler_app import SnufflerWindow, \ 

92 setup_acquisition_sources, PollInjector 

93 

94 if pile is None: 

95 pile = pile_mod.make_pile() 

96 

97 app = get_snuffler_instance() 

98 

99 kwargs_load = {} 

100 for k in ('paths', 'regex', 'format', 'cache_dir', 'force_cache'): 

101 try: 

102 kwargs_load[k] = kwargs.pop(k) 

103 except KeyError: 

104 pass 

105 

106 store_path = kwargs.pop('store_path', None) 

107 store_interval = kwargs.pop('store_interval', 600) 

108 want_markers = kwargs.pop('want_markers', False) 

109 launch_hook = kwargs.pop('launch_hook', None) 

110 

111 win = SnufflerWindow(pile, **kwargs) 

112 if launch_hook: 

113 if not isinstance(launch_hook, list): 

114 launch_hook = [launch_hook] 

115 for hook in launch_hook: 

116 hook(win) 

117 

118 sources = [] 

119 pollinjector = None 

120 tempdir = None 

121 if 'paths' in kwargs_load: 

122 sources.extend(setup_acquisition_sources(kwargs_load['paths'])) 

123 if sources: 

124 if store_path is None: 

125 tempdir = tempfile.mkdtemp('', 'snuffler-tmp-') 

126 store_path = pjoin( 

127 tempdir, 

128 'trace-%(network)s.%(station)s.%(location)s.%(channel)s.' 

129 '%(tmin_ms)s.mseed') 

130 elif os.path.isdir(store_path): 

131 store_path = pjoin( 

132 store_path, 

133 'trace-%(network)s.%(station)s.%(location)s.%(channel)s.' 

134 '%(tmin_ms)s.mseed') 

135 

136 interval = min( 

137 source.get_wanted_poll_interval() for source in sources) 

138 

139 pollinjector = PollInjector( 

140 pile, 

141 fixation_length=store_interval, 

142 path=store_path, 

143 interval=interval) 

144 

145 for source in sources: 

146 source.start() 

147 pollinjector.add_source(source) 

148 

149 win.get_view().load(**kwargs_load) 

150 

151 if not win.is_closing(): 

152 app.install_sigint_handler() 

153 try: 

154 app.exec_() 

155 

156 finally: 

157 app.uninstall_sigint_handler() 

158 

159 for source in sources: 

160 source.stop() 

161 

162 if pollinjector: 

163 pollinjector.fixate_all() 

164 

165 ret = win.return_tag() 

166 

167 if want_markers: 

168 markers = win.get_view().get_markers() 

169 

170 del win 

171 gc.collect() 

172 

173 if tempdir: 

174 shutil.rmtree(tempdir) 

175 

176 if want_markers: 

177 return ret, markers 

178 else: 

179 return ret 

180 

181 

182def snuffler_from_commandline(args=None): 

183 if args is None: 

184 args = sys.argv[1:] 

185 

186 usage = '''usage: %prog [options] waveforms ...''' 

187 parser = OptionParser(usage=usage) 

188 

189 parser.add_option( 

190 '--format', 

191 dest='format', 

192 default='detect', 

193 choices=io.allowed_formats('load'), 

194 help='assume input files are of given FORMAT. Choices: %s' 

195 % io.allowed_formats('load', 'cli_help', 'detect')) 

196 

197 parser.add_option( 

198 '--pattern', 

199 dest='regex', 

200 metavar='REGEX', 

201 help='only include files whose paths match REGEX') 

202 

203 parser.add_option( 

204 '--stations', 

205 dest='station_fns', 

206 action='append', 

207 default=[], 

208 metavar='STATIONS', 

209 help='read station information from file STATIONS') 

210 

211 parser.add_option( 

212 '--stationxml', 

213 dest='stationxml_fns', 

214 action='append', 

215 default=[], 

216 metavar='STATIONSXML', 

217 help='read station information from XML file STATIONSXML') 

218 

219 parser.add_option( 

220 '--event', '--events', 

221 dest='event_fns', 

222 action='append', 

223 default=[], 

224 metavar='EVENT', 

225 help='read event information from file EVENT') 

226 

227 parser.add_option( 

228 '--markers', 

229 dest='marker_fns', 

230 action='append', 

231 default=[], 

232 metavar='MARKERS', 

233 help='read marker information file MARKERS') 

234 

235 parser.add_option( 

236 '--follow', 

237 type='float', 

238 dest='follow', 

239 metavar='N', 

240 help='follow real time with a window of N seconds') 

241 

242 parser.add_option( 

243 '--cache', 

244 dest='cache_dir', 

245 default=config.config().cache_dir, 

246 metavar='DIR', 

247 help='use directory DIR to cache trace metadata ' 

248 '(default=\'%default\')') 

249 

250 parser.add_option( 

251 '--force-cache', 

252 dest='force_cache', 

253 action='store_true', 

254 default=False, 

255 help='use the cache even when trace attribute spoofing is active ' 

256 '(may have silly consequences)') 

257 

258 parser.add_option( 

259 '--store-path', 

260 dest='store_path', 

261 metavar='PATH_TEMPLATE', 

262 help='store data received through streams to PATH_TEMPLATE') 

263 

264 parser.add_option( 

265 '--store-interval', 

266 type='float', 

267 dest='store_interval', 

268 default=600, 

269 metavar='N', 

270 help='dump stream data to file every N seconds [default: %default]') 

271 

272 parser.add_option( 

273 '--ntracks', 

274 type='int', 

275 dest='ntracks', 

276 default=24, 

277 metavar='N', 

278 help='initially use N waveform tracks in viewer [default: %default]') 

279 

280 parser.add_option( 

281 '--disable-marker-sorting', 

282 action='store_false', 

283 dest='marker_editor_sortable', 

284 default=True, 

285 help='disable sorting in marker table for improved performance with ' 

286 '100000+ markers') 

287 

288 parser.add_option( 

289 '--hptime', 

290 choices=('on', 'off', 'config'), 

291 dest='hp_time', 

292 default='config', 

293 metavar='on|off|config', 

294 help='set high precision time mode [default: %default]') 

295 

296 parser.add_option( 

297 '--opengl', 

298 dest='opengl', 

299 action='store_true', 

300 default=None, 

301 help='use OpenGL for drawing') 

302 

303 parser.add_option( 

304 '--no-opengl', 

305 dest='opengl', 

306 action='store_false', 

307 default=None, 

308 help='do not use OpenGL for drawing') 

309 

310 parser.add_option( 

311 '--debug', 

312 dest='debug', 

313 action='store_true', 

314 default=False, 

315 help='print debugging information to stderr') 

316 

317 options, args = parser.parse_args(list(args)) 

318 

319 if options.debug: 

320 util.setup_logging('snuffler', 'debug') 

321 else: 

322 util.setup_logging('snuffler', 'info') 

323 

324 if options.hp_time in ('on', 'off'): 

325 util.use_high_precision_time(options.hp_time == 'on') 

326 

327 this_pile = pile_mod.Pile() 

328 stations = [] 

329 for stations_fn in extend_paths(options.station_fns): 

330 stations.extend(model.station.load_stations(stations_fn)) 

331 

332 for stationxml_fn in extend_paths(options.stationxml_fns): 

333 stations.extend( 

334 stationxml.load_xml( 

335 filename=stationxml_fn).get_pyrocko_stations()) 

336 

337 events = [] 

338 for event_fn in extend_paths(options.event_fns): 

339 events.extend(model.load_events(event_fn)) 

340 

341 markers = [] 

342 for marker_fn in extend_paths(options.marker_fns): 

343 markers.extend(marker.load_markers(marker_fn)) 

344 

345 return snuffle( 

346 this_pile, 

347 stations=stations, 

348 events=events, 

349 markers=markers, 

350 ntracks=options.ntracks, 

351 marker_editor_sortable=options.marker_editor_sortable, 

352 follow=options.follow, 

353 controls=True, 

354 opengl=options.opengl, 

355 paths=args, 

356 cache_dir=options.cache_dir, 

357 regex=options.regex, 

358 format=options.format, 

359 force_cache=options.force_cache, 

360 store_path=options.store_path, 

361 store_interval=options.store_interval) 

362 

363 

364if __name__ == '__main__': 

365 snuffler_from_commandline()