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 app.exec_()
148 app.uninstall_sigint_handler()
150 for source in sources:
151 source.stop()
153 if pollinjector:
154 pollinjector.fixate_all()
156 ret = win.return_tag()
158 if want_markers:
159 markers = win.get_view().get_markers()
161 del win
162 gc.collect()
164 if tempdir:
165 shutil.rmtree(tempdir)
167 if want_markers:
168 return ret, markers
169 else:
170 return ret
173def snuffler_from_commandline(args=None):
174 if args is None:
175 args = sys.argv[1:]
177 usage = '''usage: %prog [options] waveforms ...'''
178 parser = OptionParser(usage=usage)
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'))
188 parser.add_option(
189 '--pattern',
190 dest='regex',
191 metavar='REGEX',
192 help='only include files whose paths match REGEX')
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')
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')
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')
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')
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')
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\')')
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)')
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')
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]')
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]')
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')
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]')
287 parser.add_option(
288 '--opengl',
289 dest='opengl',
290 action='store_true',
291 default=None,
292 help='use OpenGL for drawing')
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')
301 parser.add_option(
302 '--debug',
303 dest='debug',
304 action='store_true',
305 default=False,
306 help='print debugging information to stderr')
308 options, args = parser.parse_args(list(args))
310 if options.debug:
311 util.setup_logging('snuffler', 'debug')
312 else:
313 util.setup_logging('snuffler', 'warning')
315 if options.hp_time in ('on', 'off'):
316 util.use_high_precision_time(options.hp_time == 'on')
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))
323 for stationxml_fn in extend_paths(options.stationxml_fns):
324 stations.extend(
325 stationxml.load_xml(
326 filename=stationxml_fn).get_pyrocko_stations())
328 events = []
329 for event_fn in extend_paths(options.event_fns):
330 events.extend(model.load_events(event_fn))
332 markers = []
333 for marker_fn in extend_paths(options.marker_fns):
334 markers.extend(marker.load_markers(marker_fn))
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)
355if __name__ == '__main__':
356 snuffler_from_commandline()