1# http://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
5'''
6Effective seismological trace viewer.
7'''
9import os
10import sys
11import logging
12import gc
13import tempfile
14import shutil
15import glob
17from os.path import join as pjoin
18from optparse import OptionParser
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
30logger = logging.getLogger('pyrocko.gui.snuffler')
32app = None
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
47def extend_paths(paths):
48 paths_r = []
49 for p in paths:
50 paths_r.extend(glob.glob(p))
51 return paths_r
54def snuffle(pile=None, **kwargs):
55 '''
56 View pile in a snuffler window.
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
91 if pile is None:
92 pile = pile_mod.make_pile()
94 app = get_snuffler_instance()
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
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)
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)
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')
133 pollinjector = PollInjector(
134 pile,
135 fixation_length=store_interval,
136 path=store_path)
138 for source in sources:
139 source.start()
140 pollinjector.add_source(source)
142 win.get_view().load(**kwargs_load)
144 if not win.is_closing():
145 app.install_sigint_handler()
146 try:
147 app.exec_()
149 finally:
150 app.uninstall_sigint_handler()
152 for source in sources:
153 source.stop()
155 if pollinjector:
156 pollinjector.fixate_all()
158 ret = win.return_tag()
160 if want_markers:
161 markers = win.get_view().get_markers()
163 del win
164 gc.collect()
166 if tempdir:
167 shutil.rmtree(tempdir)
169 if want_markers:
170 return ret, markers
171 else:
172 return ret
175def snuffler_from_commandline(args=None):
176 if args is None:
177 args = sys.argv[1:]
179 usage = '''usage: %prog [options] waveforms ...'''
180 parser = OptionParser(usage=usage)
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'))
190 parser.add_option(
191 '--pattern',
192 dest='regex',
193 metavar='REGEX',
194 help='only include files whose paths match REGEX')
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')
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')
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')
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')
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')
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')")
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)')
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')
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]')
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]')
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')
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]')
289 parser.add_option(
290 '--opengl',
291 dest='opengl',
292 action='store_true',
293 default=None,
294 help='use OpenGL for drawing')
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')
303 parser.add_option(
304 '--debug',
305 dest='debug',
306 action='store_true',
307 default=False,
308 help='print debugging information to stderr')
310 options, args = parser.parse_args(list(args))
312 if options.debug:
313 util.setup_logging('snuffler', 'debug')
314 else:
315 util.setup_logging('snuffler', 'warning')
317 if options.hp_time in ('on', 'off'):
318 util.use_high_precision_time(options.hp_time == 'on')
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))
325 for stationxml_fn in extend_paths(options.stationxml_fns):
326 stations.extend(
327 stationxml.load_xml(
328 filename=stationxml_fn).get_pyrocko_stations())
330 events = []
331 for event_fn in extend_paths(options.event_fns):
332 events.extend(model.load_events(event_fn))
334 markers = []
335 for marker_fn in extend_paths(options.marker_fns):
336 markers.extend(marker.load_markers(marker_fn))
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)
357if __name__ == '__main__':
358 snuffler_from_commandline()