1# http://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

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

5''' 

6Effective seismological trace viewer. 

7''' 

8 

9import os 

10import sys 

11import logging 

12import gc 

13import tempfile 

14import shutil 

15import glob 

16 

17from os.path import join as pjoin 

18from optparse import OptionParser 

19 

20 

21from pyrocko import pile as pile_mod 

22from pyrocko import util 

23from pyrocko import model 

24from pyrocko import config 

25from pyrocko import io 

26from pyrocko.gui import marker 

27from pyrocko.io import stationxml 

28 

29 

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

31 

32app = None 

33 

34 

35def get_snuffler_instance(): 

36 from .snuffler_app import Snuffler 

37 from .qt_compat import qg 

38 import locale 

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

40 global app 

41 if app is None: 

42 qg.QSurfaceFormat.setDefaultFormat(qg.QSurfaceFormat()) 

43 app = Snuffler() 

44 return app 

45 

46 

47def extend_paths(paths): 

48 paths_r = [] 

49 for p in paths: 

50 paths_r.extend(glob.glob(p)) 

51 return paths_r 

52 

53 

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

55 ''' 

56 View pile in a snuffler window. 

57 

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

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

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

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

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

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

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

65 when working with many markers. 

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

67 ``None`` 

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

69 ``True``) 

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

71 choice). 

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

73 :param pattern: regex which filenames must match 

74 :param format: format of input files 

75 :param cache_dir: cache directory with trace meta information 

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

77 is active 

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

79 streams 

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

81 buffer dumps 

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

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

84 shown 

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

86 dialog 

87 ''' 

88 from .snuffler_app import SnufflerWindow, \ 

89 setup_acquisition_sources, PollInjector 

90 

91 if pile is None: 

92 pile = pile_mod.make_pile() 

93 

94 app = get_snuffler_instance() 

95 

96 kwargs_load = {} 

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

98 try: 

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

100 except KeyError: 

101 pass 

102 

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

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

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

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

107 

108 win = SnufflerWindow(pile, **kwargs) 

109 if launch_hook: 

110 if not isinstance(launch_hook, list): 

111 launch_hook = [launch_hook] 

112 for hook in launch_hook: 

113 hook(win) 

114 

115 sources = [] 

116 pollinjector = None 

117 tempdir = None 

118 if 'paths' in kwargs_load: 

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

120 if sources: 

121 if store_path is None: 

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

123 store_path = pjoin( 

124 tempdir, 

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

126 '%(tmin_ms)s.mseed') 

127 elif os.path.isdir(store_path): 

128 store_path = pjoin( 

129 store_path, 

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

131 '%(tmin_ms)s.mseed') 

132 

133 pollinjector = PollInjector( 

134 pile, 

135 fixation_length=store_interval, 

136 path=store_path) 

137 

138 for source in sources: 

139 source.start() 

140 pollinjector.add_source(source) 

141 

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

143 

144 if not win.is_closing(): 

145 app.install_sigint_handler() 

146 try: 

147 app.exec_() 

148 

149 finally: 

150 app.uninstall_sigint_handler() 

151 

152 for source in sources: 

153 source.stop() 

154 

155 if pollinjector: 

156 pollinjector.fixate_all() 

157 

158 ret = win.return_tag() 

159 

160 if want_markers: 

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

162 

163 del win 

164 gc.collect() 

165 

166 if tempdir: 

167 shutil.rmtree(tempdir) 

168 

169 if want_markers: 

170 return ret, markers 

171 else: 

172 return ret 

173 

174 

175def snuffler_from_commandline(args=None): 

176 if args is None: 

177 args = sys.argv[1:] 

178 

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

180 parser = OptionParser(usage=usage) 

181 

182 parser.add_option( 

183 '--format', 

184 dest='format', 

185 default='detect', 

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

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

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

189 

190 parser.add_option( 

191 '--pattern', 

192 dest='regex', 

193 metavar='REGEX', 

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

195 

196 parser.add_option( 

197 '--stations', 

198 dest='station_fns', 

199 action='append', 

200 default=[], 

201 metavar='STATIONS', 

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

203 

204 parser.add_option( 

205 '--stationxml', 

206 dest='stationxml_fns', 

207 action='append', 

208 default=[], 

209 metavar='STATIONSXML', 

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

211 

212 parser.add_option( 

213 '--event', '--events', 

214 dest='event_fns', 

215 action='append', 

216 default=[], 

217 metavar='EVENT', 

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

219 

220 parser.add_option( 

221 '--markers', 

222 dest='marker_fns', 

223 action='append', 

224 default=[], 

225 metavar='MARKERS', 

226 help='read marker information file MARKERS') 

227 

228 parser.add_option( 

229 '--follow', 

230 type='float', 

231 dest='follow', 

232 metavar='N', 

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

234 

235 parser.add_option( 

236 '--cache', 

237 dest='cache_dir', 

238 default=config.config().cache_dir, 

239 metavar='DIR', 

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

241 '(default=\'%default\')') 

242 

243 parser.add_option( 

244 '--force-cache', 

245 dest='force_cache', 

246 action='store_true', 

247 default=False, 

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

249 '(may have silly consequences)') 

250 

251 parser.add_option( 

252 '--store-path', 

253 dest='store_path', 

254 metavar='PATH_TEMPLATE', 

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

256 

257 parser.add_option( 

258 '--store-interval', 

259 type='float', 

260 dest='store_interval', 

261 default=600, 

262 metavar='N', 

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

264 

265 parser.add_option( 

266 '--ntracks', 

267 type='int', 

268 dest='ntracks', 

269 default=24, 

270 metavar='N', 

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

272 

273 parser.add_option( 

274 '--disable-marker-sorting', 

275 action='store_false', 

276 dest='marker_editor_sortable', 

277 default=True, 

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

279 '100000+ markers') 

280 

281 parser.add_option( 

282 '--hptime', 

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

284 dest='hp_time', 

285 default='config', 

286 metavar='on|off|config', 

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

288 

289 parser.add_option( 

290 '--opengl', 

291 dest='opengl', 

292 action='store_true', 

293 default=None, 

294 help='use OpenGL for drawing') 

295 

296 parser.add_option( 

297 '--no-opengl', 

298 dest='opengl', 

299 action='store_false', 

300 default=None, 

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

302 

303 parser.add_option( 

304 '--debug', 

305 dest='debug', 

306 action='store_true', 

307 default=False, 

308 help='print debugging information to stderr') 

309 

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

311 

312 if options.debug: 

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

314 else: 

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

316 

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

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

319 

320 this_pile = pile_mod.Pile() 

321 stations = [] 

322 for stations_fn in extend_paths(options.station_fns): 

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

324 

325 for stationxml_fn in extend_paths(options.stationxml_fns): 

326 stations.extend( 

327 stationxml.load_xml( 

328 filename=stationxml_fn).get_pyrocko_stations()) 

329 

330 events = [] 

331 for event_fn in extend_paths(options.event_fns): 

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

333 

334 markers = [] 

335 for marker_fn in extend_paths(options.marker_fns): 

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

337 

338 return snuffle( 

339 this_pile, 

340 stations=stations, 

341 events=events, 

342 markers=markers, 

343 ntracks=options.ntracks, 

344 marker_editor_sortable=options.marker_editor_sortable, 

345 follow=options.follow, 

346 controls=True, 

347 opengl=options.opengl, 

348 paths=args, 

349 cache_dir=options.cache_dir, 

350 regex=options.regex, 

351 format=options.format, 

352 force_cache=options.force_cache, 

353 store_path=options.store_path, 

354 store_interval=options.store_interval) 

355 

356 

357if __name__ == '__main__': 

358 snuffler_from_commandline()