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 try: 

148 app.exec_() 

149 

150 finally: 

151 app.uninstall_sigint_handler() 

152 

153 for source in sources: 

154 source.stop() 

155 

156 if pollinjector: 

157 pollinjector.fixate_all() 

158 

159 ret = win.return_tag() 

160 

161 if want_markers: 

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

163 

164 del win 

165 gc.collect() 

166 

167 if tempdir: 

168 shutil.rmtree(tempdir) 

169 

170 if want_markers: 

171 return ret, markers 

172 else: 

173 return ret 

174 

175 

176def snuffler_from_commandline(args=None): 

177 if args is None: 

178 args = sys.argv[1:] 

179 

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

181 parser = OptionParser(usage=usage) 

182 

183 parser.add_option( 

184 '--format', 

185 dest='format', 

186 default='detect', 

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

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

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

190 

191 parser.add_option( 

192 '--pattern', 

193 dest='regex', 

194 metavar='REGEX', 

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

196 

197 parser.add_option( 

198 '--stations', 

199 dest='station_fns', 

200 action='append', 

201 default=[], 

202 metavar='STATIONS', 

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

204 

205 parser.add_option( 

206 '--stationxml', 

207 dest='stationxml_fns', 

208 action='append', 

209 default=[], 

210 metavar='STATIONSXML', 

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

212 

213 parser.add_option( 

214 '--event', '--events', 

215 dest='event_fns', 

216 action='append', 

217 default=[], 

218 metavar='EVENT', 

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

220 

221 parser.add_option( 

222 '--markers', 

223 dest='marker_fns', 

224 action='append', 

225 default=[], 

226 metavar='MARKERS', 

227 help='read marker information file MARKERS') 

228 

229 parser.add_option( 

230 '--follow', 

231 type='float', 

232 dest='follow', 

233 metavar='N', 

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

235 

236 parser.add_option( 

237 '--cache', 

238 dest='cache_dir', 

239 default=config.config().cache_dir, 

240 metavar='DIR', 

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

242 '(default=\'%default\')') 

243 

244 parser.add_option( 

245 '--force-cache', 

246 dest='force_cache', 

247 action='store_true', 

248 default=False, 

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

250 '(may have silly consequences)') 

251 

252 parser.add_option( 

253 '--store-path', 

254 dest='store_path', 

255 metavar='PATH_TEMPLATE', 

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

257 

258 parser.add_option( 

259 '--store-interval', 

260 type='float', 

261 dest='store_interval', 

262 default=600, 

263 metavar='N', 

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

265 

266 parser.add_option( 

267 '--ntracks', 

268 type='int', 

269 dest='ntracks', 

270 default=24, 

271 metavar='N', 

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

273 

274 parser.add_option( 

275 '--disable-marker-sorting', 

276 action='store_false', 

277 dest='marker_editor_sortable', 

278 default=True, 

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

280 '100000+ markers') 

281 

282 parser.add_option( 

283 '--hptime', 

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

285 dest='hp_time', 

286 default='config', 

287 metavar='on|off|config', 

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

289 

290 parser.add_option( 

291 '--opengl', 

292 dest='opengl', 

293 action='store_true', 

294 default=None, 

295 help='use OpenGL for drawing') 

296 

297 parser.add_option( 

298 '--no-opengl', 

299 dest='opengl', 

300 action='store_false', 

301 default=None, 

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

303 

304 parser.add_option( 

305 '--debug', 

306 dest='debug', 

307 action='store_true', 

308 default=False, 

309 help='print debugging information to stderr') 

310 

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

312 

313 if options.debug: 

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

315 else: 

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

317 

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

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

320 

321 this_pile = pile_mod.Pile() 

322 stations = [] 

323 for stations_fn in extend_paths(options.station_fns): 

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

325 

326 for stationxml_fn in extend_paths(options.stationxml_fns): 

327 stations.extend( 

328 stationxml.load_xml( 

329 filename=stationxml_fn).get_pyrocko_stations()) 

330 

331 events = [] 

332 for event_fn in extend_paths(options.event_fns): 

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

334 

335 markers = [] 

336 for marker_fn in extend_paths(options.marker_fns): 

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

338 

339 return snuffle( 

340 this_pile, 

341 stations=stations, 

342 events=events, 

343 markers=markers, 

344 ntracks=options.ntracks, 

345 marker_editor_sortable=options.marker_editor_sortable, 

346 follow=options.follow, 

347 controls=True, 

348 opengl=options.opengl, 

349 paths=args, 

350 cache_dir=options.cache_dir, 

351 regex=options.regex, 

352 format=options.format, 

353 force_cache=options.force_cache, 

354 store_path=options.store_path, 

355 store_interval=options.store_interval) 

356 

357 

358if __name__ == '__main__': 

359 snuffler_from_commandline()