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 import locale 

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

40 global app 

41 if app is None: 

42 app = Snuffler() 

43 return app 

44 

45 

46def extend_paths(paths): 

47 paths_r = [] 

48 for p in paths: 

49 paths_r.extend(glob.glob(p)) 

50 return paths_r 

51 

52 

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

54 ''' 

55 View pile in a snuffler window. 

56 

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

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

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

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

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

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

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

64 when working with many markers. 

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

66 ``None`` 

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

68 ``True``) 

69 :param opengl: bool, whether to use opengl (default: ``False``) 

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

71 :param pattern: regex which filenames must match 

72 :param format: format of input files 

73 :param cache_dir: cache directory with trace meta information 

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

75 is active 

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

77 streams 

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

79 buffer dumps 

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

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

82 shown 

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

84 dialog 

85 ''' 

86 from .snuffler_app import SnufflerWindow, \ 

87 setup_acquisition_sources, PollInjector 

88 

89 if pile is None: 

90 pile = pile_mod.make_pile() 

91 

92 app = get_snuffler_instance() 

93 

94 kwargs_load = {} 

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

96 try: 

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

98 except KeyError: 

99 pass 

100 

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

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

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

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

105 

106 win = SnufflerWindow(pile, **kwargs) 

107 if launch_hook: 

108 if not isinstance(launch_hook, list): 

109 launch_hook = [launch_hook] 

110 for hook in launch_hook: 

111 hook(win) 

112 

113 sources = [] 

114 pollinjector = None 

115 tempdir = None 

116 if 'paths' in kwargs_load: 

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

118 if sources: 

119 if store_path is None: 

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

121 store_path = pjoin( 

122 tempdir, 

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

124 '%(tmin_ms)s.mseed') 

125 elif os.path.isdir(store_path): 

126 store_path = pjoin( 

127 store_path, 

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

129 '%(tmin_ms)s.mseed') 

130 

131 pollinjector = PollInjector( 

132 pile, 

133 fixation_length=store_interval, 

134 path=store_path) 

135 

136 for source in sources: 

137 source.start() 

138 pollinjector.add_source(source) 

139 

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

141 

142 if not win.is_closing(): 

143 app.install_sigint_handler() 

144 app.exec_() 

145 app.uninstall_sigint_handler() 

146 

147 for source in sources: 

148 source.stop() 

149 

150 if pollinjector: 

151 pollinjector.fixate_all() 

152 

153 ret = win.return_tag() 

154 

155 if want_markers: 

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

157 

158 del win 

159 gc.collect() 

160 

161 if tempdir: 

162 shutil.rmtree(tempdir) 

163 

164 if want_markers: 

165 return ret, markers 

166 else: 

167 return ret 

168 

169 

170def snuffler_from_commandline(args=None): 

171 if args is None: 

172 args = sys.argv[1:] 

173 

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

175 parser = OptionParser(usage=usage) 

176 

177 parser.add_option( 

178 '--format', 

179 dest='format', 

180 default='detect', 

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

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

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

184 

185 parser.add_option( 

186 '--pattern', 

187 dest='regex', 

188 metavar='REGEX', 

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

190 

191 parser.add_option( 

192 '--stations', 

193 dest='station_fns', 

194 action='append', 

195 default=[], 

196 metavar='STATIONS', 

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

198 

199 parser.add_option( 

200 '--stationxml', 

201 dest='stationxml_fns', 

202 action='append', 

203 default=[], 

204 metavar='STATIONSXML', 

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

206 

207 parser.add_option( 

208 '--event', '--events', 

209 dest='event_fns', 

210 action='append', 

211 default=[], 

212 metavar='EVENT', 

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

214 

215 parser.add_option( 

216 '--markers', 

217 dest='marker_fns', 

218 action='append', 

219 default=[], 

220 metavar='MARKERS', 

221 help='read marker information file MARKERS') 

222 

223 parser.add_option( 

224 '--follow', 

225 type='float', 

226 dest='follow', 

227 metavar='N', 

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

229 

230 parser.add_option( 

231 '--cache', 

232 dest='cache_dir', 

233 default=config.config().cache_dir, 

234 metavar='DIR', 

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

236 '(default=\'%default\')') 

237 

238 parser.add_option( 

239 '--force-cache', 

240 dest='force_cache', 

241 action='store_true', 

242 default=False, 

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

244 '(may have silly consequences)') 

245 

246 parser.add_option( 

247 '--store-path', 

248 dest='store_path', 

249 metavar='PATH_TEMPLATE', 

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

251 

252 parser.add_option( 

253 '--store-interval', 

254 type='float', 

255 dest='store_interval', 

256 default=600, 

257 metavar='N', 

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

259 

260 parser.add_option( 

261 '--ntracks', 

262 type='int', 

263 dest='ntracks', 

264 default=24, 

265 metavar='N', 

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

267 

268 parser.add_option( 

269 '--disable-marker-sorting', 

270 action='store_false', 

271 dest='marker_editor_sortable', 

272 default=True, 

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

274 '100000+ markers') 

275 

276 parser.add_option( 

277 '--hptime', 

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

279 dest='hp_time', 

280 default='config', 

281 metavar='on|off|config', 

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

283 

284 parser.add_option( 

285 '--opengl', 

286 dest='opengl', 

287 action='store_true', 

288 default=False, 

289 help='use OpenGL for drawing') 

290 

291 parser.add_option( 

292 '--qt5', 

293 dest='gui_toolkit_qt5', 

294 action='store_true', 

295 default=False, 

296 help='use Qt5 for the GUI') 

297 

298 parser.add_option( 

299 '--qt4', 

300 dest='gui_toolkit_qt4', 

301 action='store_true', 

302 default=False, 

303 help='use Qt4 for the GUI') 

304 

305 parser.add_option( 

306 '--debug', 

307 dest='debug', 

308 action='store_true', 

309 default=False, 

310 help='print debugging information to stderr') 

311 

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

313 

314 if options.debug: 

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

316 else: 

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

318 

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

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

321 

322 if options.gui_toolkit_qt4: 

323 config.override_gui_toolkit = 'qt4' 

324 

325 if options.gui_toolkit_qt5: 

326 config.override_gui_toolkit = 'qt5' 

327 

328 this_pile = pile_mod.Pile() 

329 stations = [] 

330 for stations_fn in extend_paths(options.station_fns): 

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

332 

333 for stationxml_fn in extend_paths(options.stationxml_fns): 

334 stations.extend( 

335 stationxml.load_xml( 

336 filename=stationxml_fn).get_pyrocko_stations()) 

337 

338 events = [] 

339 for event_fn in extend_paths(options.event_fns): 

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

341 

342 markers = [] 

343 for marker_fn in extend_paths(options.marker_fns): 

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

345 

346 return snuffle( 

347 this_pile, 

348 stations=stations, 

349 events=events, 

350 markers=markers, 

351 ntracks=options.ntracks, 

352 marker_editor_sortable=options.marker_editor_sortable, 

353 follow=options.follow, 

354 controls=True, 

355 opengl=options.opengl, 

356 paths=args, 

357 cache_dir=options.cache_dir, 

358 regex=options.regex, 

359 format=options.format, 

360 force_cache=options.force_cache, 

361 store_path=options.store_path, 

362 store_interval=options.store_interval) 

363 

364 

365if __name__ == '__main__': 

366 snuffler_from_commandline()