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
10import os
11import sys
12import logging
13import gc
14import tempfile
15import shutil
16import glob
18from os.path import join as pjoin
19from optparse import OptionParser
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
31logger = logging.getLogger('pyrocko.gui.snuffler')
33app = None
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
48def extend_paths(paths):
49 paths_r = []
50 for p in paths:
51 paths_r.extend(glob.glob(p))
52 return paths_r
55def snuffle(pile=None, **kwargs):
56 '''
57 View pile in a snuffler window.
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
92 if pile is None:
93 pile = pile_mod.make_pile()
95 app = get_snuffler_instance()
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
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)
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)
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')
134 pollinjector = PollInjector(
135 pile,
136 fixation_length=store_interval,
137 path=store_path)
139 for source in sources:
140 source.start()
141 pollinjector.add_source(source)
143 win.get_view().load(**kwargs_load)
145 if not win.is_closing():
146 app.install_sigint_handler()
147 try:
148 app.exec_()
150 finally:
151 app.uninstall_sigint_handler()
153 for source in sources:
154 source.stop()
156 if pollinjector:
157 pollinjector.fixate_all()
159 ret = win.return_tag()
161 if want_markers:
162 markers = win.get_view().get_markers()
164 del win
165 gc.collect()
167 if tempdir:
168 shutil.rmtree(tempdir)
170 if want_markers:
171 return ret, markers
172 else:
173 return ret
176def snuffler_from_commandline(args=None):
177 if args is None:
178 args = sys.argv[1:]
180 usage = '''usage: %prog [options] waveforms ...'''
181 parser = OptionParser(usage=usage)
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'))
191 parser.add_option(
192 '--pattern',
193 dest='regex',
194 metavar='REGEX',
195 help='only include files whose paths match REGEX')
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')
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')
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')
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')
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')
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\')')
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)')
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')
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]')
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]')
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')
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]')
290 parser.add_option(
291 '--opengl',
292 dest='opengl',
293 action='store_true',
294 default=None,
295 help='use OpenGL for drawing')
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')
304 parser.add_option(
305 '--debug',
306 dest='debug',
307 action='store_true',
308 default=False,
309 help='print debugging information to stderr')
311 options, args = parser.parse_args(list(args))
313 if options.debug:
314 util.setup_logging('snuffler', 'debug')
315 else:
316 util.setup_logging('snuffler', 'warning')
318 if options.hp_time in ('on', 'off'):
319 util.use_high_precision_time(options.hp_time == 'on')
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))
326 for stationxml_fn in extend_paths(options.stationxml_fns):
327 stations.extend(
328 stationxml.load_xml(
329 filename=stationxml_fn).get_pyrocko_stations())
331 events = []
332 for event_fn in extend_paths(options.event_fns):
333 events.extend(model.load_events(event_fn))
335 markers = []
336 for marker_fn in extend_paths(options.marker_fns):
337 markers.extend(marker.load_markers(marker_fn))
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)
358if __name__ == '__main__':
359 snuffler_from_commandline()