1# http://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

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

5''' 

6Effective seismological trace viewer. 

7''' 

8from __future__ import absolute_import 

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.gui import marker 

28from pyrocko.io import stationxml 

29 

30 

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

32 

33app = None 

34 

35 

36def get_snuffler_instance(): 

37 from .snuffler_app import Snuffler 

38 from .qt_compat import qg 

39 import locale 

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

41 global app 

42 if app is None: 

43 qg.QSurfaceFormat.setDefaultFormat(qg.QSurfaceFormat()) 

44 app = Snuffler() 

45 return app 

46 

47 

48def extend_paths(paths): 

49 paths_r = [] 

50 for p in paths: 

51 paths_r.extend(glob.glob(p)) 

52 return paths_r 

53 

54 

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

56 ''' 

57 View pile in a snuffler window. 

58 

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

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

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

62 :param markers: list of `pyrocko.gui.util.Marker` objects or ``None`` 

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

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

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

66 when working with many markers. 

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

68 ``None`` 

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

70 ``True``) 

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

72 choice). 

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

74 :param pattern: regex which filenames must match 

75 :param format: format of input files 

76 :param cache_dir: cache directory with trace meta information 

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

78 is active 

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

80 streams 

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

82 buffer dumps 

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

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

85 shown 

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

87 dialog 

88 ''' 

89 from .snuffler_app import SnufflerWindow, \ 

90 setup_acquisition_sources, PollInjector 

91 

92 if pile is None: 

93 pile = pile_mod.make_pile() 

94 

95 app = get_snuffler_instance() 

96 

97 kwargs_load = {} 

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

99 try: 

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

101 except KeyError: 

102 pass 

103 

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

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

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

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

108 

109 win = SnufflerWindow(pile, **kwargs) 

110 if launch_hook: 

111 if not isinstance(launch_hook, list): 

112 launch_hook = [launch_hook] 

113 for hook in launch_hook: 

114 hook(win) 

115 

116 sources = [] 

117 pollinjector = None 

118 tempdir = None 

119 if 'paths' in kwargs_load: 

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

121 if sources: 

122 if store_path is None: 

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

124 store_path = pjoin( 

125 tempdir, 

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

127 '%(tmin_ms)s.mseed') 

128 elif os.path.isdir(store_path): 

129 store_path = pjoin( 

130 store_path, 

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

132 '%(tmin_ms)s.mseed') 

133 

134 pollinjector = PollInjector( 

135 pile, 

136 fixation_length=store_interval, 

137 path=store_path) 

138 

139 for source in sources: 

140 source.start() 

141 pollinjector.add_source(source) 

142 

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

144 

145 if not win.is_closing(): 

146 app.install_sigint_handler() 

147 app.exec_() 

148 app.uninstall_sigint_handler() 

149 

150 for source in sources: 

151 source.stop() 

152 

153 if pollinjector: 

154 pollinjector.fixate_all() 

155 

156 ret = win.return_tag() 

157 

158 if want_markers: 

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

160 

161 del win 

162 gc.collect() 

163 

164 if tempdir: 

165 shutil.rmtree(tempdir) 

166 

167 if want_markers: 

168 return ret, markers 

169 else: 

170 return ret 

171 

172 

173def snuffler_from_commandline(args=None): 

174 if args is None: 

175 args = sys.argv[1:] 

176 

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

178 parser = OptionParser(usage=usage) 

179 

180 parser.add_option( 

181 '--format', 

182 dest='format', 

183 default='detect', 

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

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

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

187 

188 parser.add_option( 

189 '--pattern', 

190 dest='regex', 

191 metavar='REGEX', 

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

193 

194 parser.add_option( 

195 '--stations', 

196 dest='station_fns', 

197 action='append', 

198 default=[], 

199 metavar='STATIONS', 

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

201 

202 parser.add_option( 

203 '--stationxml', 

204 dest='stationxml_fns', 

205 action='append', 

206 default=[], 

207 metavar='STATIONSXML', 

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

209 

210 parser.add_option( 

211 '--event', '--events', 

212 dest='event_fns', 

213 action='append', 

214 default=[], 

215 metavar='EVENT', 

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

217 

218 parser.add_option( 

219 '--markers', 

220 dest='marker_fns', 

221 action='append', 

222 default=[], 

223 metavar='MARKERS', 

224 help='read marker information file MARKERS') 

225 

226 parser.add_option( 

227 '--follow', 

228 type='float', 

229 dest='follow', 

230 metavar='N', 

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

232 

233 parser.add_option( 

234 '--cache', 

235 dest='cache_dir', 

236 default=config.config().cache_dir, 

237 metavar='DIR', 

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

239 '(default=\'%default\')') 

240 

241 parser.add_option( 

242 '--force-cache', 

243 dest='force_cache', 

244 action='store_true', 

245 default=False, 

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

247 '(may have silly consequences)') 

248 

249 parser.add_option( 

250 '--store-path', 

251 dest='store_path', 

252 metavar='PATH_TEMPLATE', 

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

254 

255 parser.add_option( 

256 '--store-interval', 

257 type='float', 

258 dest='store_interval', 

259 default=600, 

260 metavar='N', 

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

262 

263 parser.add_option( 

264 '--ntracks', 

265 type='int', 

266 dest='ntracks', 

267 default=24, 

268 metavar='N', 

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

270 

271 parser.add_option( 

272 '--disable-marker-sorting', 

273 action='store_false', 

274 dest='marker_editor_sortable', 

275 default=True, 

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

277 '100000+ markers') 

278 

279 parser.add_option( 

280 '--hptime', 

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

282 dest='hp_time', 

283 default='config', 

284 metavar='on|off|config', 

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

286 

287 parser.add_option( 

288 '--opengl', 

289 dest='opengl', 

290 action='store_true', 

291 default=None, 

292 help='use OpenGL for drawing') 

293 

294 parser.add_option( 

295 '--no-opengl', 

296 dest='opengl', 

297 action='store_false', 

298 default=None, 

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

300 

301 parser.add_option( 

302 '--debug', 

303 dest='debug', 

304 action='store_true', 

305 default=False, 

306 help='print debugging information to stderr') 

307 

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

309 

310 if options.debug: 

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

312 else: 

313 util.setup_logging('snuffler', 'warning') 

314 

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

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

317 

318 this_pile = pile_mod.Pile() 

319 stations = [] 

320 for stations_fn in extend_paths(options.station_fns): 

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

322 

323 for stationxml_fn in extend_paths(options.stationxml_fns): 

324 stations.extend( 

325 stationxml.load_xml( 

326 filename=stationxml_fn).get_pyrocko_stations()) 

327 

328 events = [] 

329 for event_fn in extend_paths(options.event_fns): 

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

331 

332 markers = [] 

333 for marker_fn in extend_paths(options.marker_fns): 

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

335 

336 return snuffle( 

337 this_pile, 

338 stations=stations, 

339 events=events, 

340 markers=markers, 

341 ntracks=options.ntracks, 

342 marker_editor_sortable=options.marker_editor_sortable, 

343 follow=options.follow, 

344 controls=True, 

345 opengl=options.opengl, 

346 paths=args, 

347 cache_dir=options.cache_dir, 

348 regex=options.regex, 

349 format=options.format, 

350 force_cache=options.force_cache, 

351 store_path=options.store_path, 

352 store_interval=options.store_interval) 

353 

354 

355if __name__ == '__main__': 

356 snuffler_from_commandline()