dataset.py 21 KB
Newer Older
Sebastian Heimann's avatar
Sebastian Heimann committed
1

2
import copy
Sebastian Heimann's avatar
Sebastian Heimann committed
3
4
5
import logging
from collections import defaultdict
import numpy as num
Sebastian Heimann's avatar
Sebastian Heimann committed
6
from pyrocko import util, pile, model, config, trace, snuffling, gui_util
Sebastian Heimann's avatar
Sebastian Heimann committed
7
8
9
from pyrocko.fdsn import enhanced_sacpz, station as fs
from pyrocko.guts import Object, Tuple, String, Float, dump_all, load_all

Sebastian Heimann's avatar
Sebastian Heimann committed
10
guts_prefix = 'grond'
Sebastian Heimann's avatar
Sebastian Heimann committed
11
12
13
14
15
16
17
18
19

logger = logging.getLogger('grond.dataset')


class InvalidObject(Exception):
    pass


class NotFound(Exception):
20
21
22
    def __init__(self, reason, codes=None):
        self.reason = reason
        self.codes = codes
23
24

    def __str__(self):
25
26
27
28
29
        s = self.reason
        if self.codes:
            s += ' (%s)' % '.'.join(self.codes)

        return s
Sebastian Heimann's avatar
Sebastian Heimann committed
30
31


32
33
34
35
class DatasetError(Exception):
    pass


Sebastian Heimann's avatar
Sebastian Heimann committed
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
class StationCorrection(Object):
    codes = Tuple.T(4, String.T())
    delay = Float.T()
    factor = Float.T()


def load_station_corrections(filename):
    scs = load_all(filename=filename)
    for sc in scs:
        assert isinstance(sc, StationCorrection)

    return scs


def dump_station_corrections(station_corrections, filename):
    return dump_all(station_corrections, filename=filename)


class Dataset(object):

    def __init__(self):
        self.events = []
        self.pile = pile.Pile()
        self.stations = {}
        self.responses = defaultdict(list)
        self.responses_stationxml = []
        self.clippings = {}
        self.blacklist = set()
        self.whitelist_nslc = None
        self.whitelist_nsl = None
        self.station_corrections = {}
        self.station_factors = {}
Sebastian Heimann's avatar
Sebastian Heimann committed
68
        self.pick_markers = []
Sebastian Heimann's avatar
Sebastian Heimann committed
69
70
71
72
        self.apply_correction_delays = True
        self.apply_correction_factors = True
        self.clip_handling = 'by_nsl'
        self.synthetic_test = None
73
        self._picks = None
Sebastian Heimann's avatar
Sebastian Heimann committed
74
75
76
77
78
79
80
81
        self._cache = {}

    def empty_cache(self):
        self._cache = {}

    def set_synthetic_test(self, synthetic_test):
        self.synthetic_test = synthetic_test

Sebastian Heimann's avatar
Sebastian Heimann committed
82
83
84
85
86
87
    def add_stations(
            self,
            stations=None,
            pyrocko_stations_filename=None,
            stationxml_filenames=None):

Sebastian Heimann's avatar
Sebastian Heimann committed
88
89
90
91
        if stations is not None:
            for station in stations:
                self.stations[station.nsl()] = station

Sebastian Heimann's avatar
Sebastian Heimann committed
92
        if pyrocko_stations_filename is not None:
93
94
95
96
            logger.debug(
                'Loading stations from file %s' %
                pyrocko_stations_filename)

Sebastian Heimann's avatar
Sebastian Heimann committed
97
            for station in model.load_stations(pyrocko_stations_filename):
Sebastian Heimann's avatar
Sebastian Heimann committed
98
99
                self.stations[station.nsl()] = station

100
        if stationxml_filenames is not None and len(stationxml_filenames) > 0:
101
102
103
104
            logger.debug(
                'Loading stations from StationXML file %s' %
                stationxml_filenames)

Sebastian Heimann's avatar
Sebastian Heimann committed
105
106
107
108
109
            for stationxml_filename in stationxml_filenames:
                sx = fs.load_xml(filename=stationxml_filename)
                for station in sx.get_pyrocko_stations():
                    self.stations[station.nsl()] = station

Sebastian Heimann's avatar
Sebastian Heimann committed
110
111
112
113
114
    def add_events(self, events=None, filename=None):
        if events is not None:
            self.events.extend(events)

        if filename is not None:
115
            logger.debug('Loading events from file %s' % filename)
Sebastian Heimann's avatar
Sebastian Heimann committed
116
117
118
119
            self.events.extend(model.load_events(filename))

    def add_waveforms(self, paths, regex=None, fileformat='detect',
                      show_progress=True):
120
        logger.debug('Loading waveform data from %s' % paths)
Sebastian Heimann's avatar
Sebastian Heimann committed
121
122
123
124
125
126
127
128
        cachedirname = config.config().cache_dir
        fns = util.select_files(paths, regex=regex,
                                show_progress=show_progress)
        cache = pile.get_cache(cachedirname)
        self.pile.load_files(sorted(fns), cache=cache,
                             fileformat=fileformat,
                             show_progress=show_progress)

129
    def add_responses(self, sacpz_dirname=None, stationxml_filenames=None):
Sebastian Heimann's avatar
Sebastian Heimann committed
130
        if sacpz_dirname:
131
            logger.debug('Loading SAC PZ responses from %s' % sacpz_dirname)
Sebastian Heimann's avatar
Sebastian Heimann committed
132
133
134
135
136
            for x in enhanced_sacpz.iload_dirname(sacpz_dirname):
                self.responses[x.codes].append(x)

        if stationxml_filenames:
            for stationxml_filename in stationxml_filenames:
137
138
                logger.debug(
                    'Loading StationXML responses from %s' %
139
                    stationxml_filename)
140

Sebastian Heimann's avatar
Sebastian Heimann committed
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
                self.responses_stationxml.append(
                    fs.load_xml(filename=stationxml_filename))

    def add_clippings(self, markers_filename):
        markers = snuffling.load_markers(markers_filename)
        clippings = {}
        for marker in markers:
            nslc = marker.one_nslc()
            nsl = nslc[:3]
            if nsl not in clippings:
                clippings[nsl] = []

            if nslc not in clippings:
                clippings[nslc] = []

            clippings[nsl].append(marker.tmin)
            clippings[nslc].append(marker.tmin)

        for k, times in clippings.iteritems():
            atimes = num.array(times, dtype=num.float)
            if k not in self.clippings:
                self.clippings[k] = atimes
            else:
                self.clippings[k] = num.concatenate(self.clippings, atimes)

    def add_blacklist(self, blacklist):
167
        logger.debug('Loading blacklisted stations')
Sebastian Heimann's avatar
Sebastian Heimann committed
168
169
170
171
172
173
        for x in blacklist:
            if isinstance(x, basestring):
                x = tuple(x.split('.'))
            self.blacklist.add(x)

    def add_whitelist(self, whitelist):
174
        logger.debug('Loading whitelisted stations')
Sebastian Heimann's avatar
Sebastian Heimann committed
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
        if self.whitelist_nslc is None:
            self.whitelist_nslc = set()
            self.whitelist_nsl = set()
            self.whitelist_nsl_xx = set()

        for x in whitelist:
            if isinstance(x, basestring):
                x = tuple(x.split('.'))
            assert len(x) in (3, 4)
            if len(x) == 4:
                self.whitelist_nslc.add(x)
                self.whitelist_nsl_xx.add(x[:3])
            if len(x) == 3:
                self.whitelist_nsl.add(x)

    def add_station_corrections(self, filename):
        self.station_corrections.update(
            (sc.codes, sc) for sc in load_station_corrections(filename))

Sebastian Heimann's avatar
Sebastian Heimann committed
194
195
196
197
    def add_picks(self, filename):
        self.pick_markers.extend(
            gui_util.load_markers(filename))

198
199
        self._picks = None

Sebastian Heimann's avatar
Sebastian Heimann committed
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
    def is_blacklisted(self, obj):
        try:
            nslc = self.get_nslc(obj)
            if nslc in self.blacklist:
                return True

        except InvalidObject:
            pass

        nsl = self.get_nsl(obj)
        return (
            nsl in self.blacklist or
            nsl[1:2] in self.blacklist or
            nsl[:2] in self.blacklist)

    def is_whitelisted(self, obj):
        if self.whitelist_nslc is None:
            return True

        nsl = self.get_nsl(obj)
        try:
            nslc = self.get_nslc(obj)
            if nslc in self.whitelist_nslc:
                return True

            return nsl in self.whitelist_nsl

        except InvalidObject:
            return nsl in self.whitelist_nsl_xx or nsl in self.whitelist_nsl

    def has_clipping(self, nsl_or_nslc, tmin, tmax):
        if nsl_or_nslc not in self.clippings:
            return False

        atimes = self.clippings[nsl_or_nslc]
        return num.any(num.logical_and(tmin < atimes, atimes <= tmax))

    def get_nsl(self, obj):
        if isinstance(obj, trace.Trace):
            net, sta, loc, _ = obj.nslc_id
        elif isinstance(obj, model.Station):
            net, sta, loc = obj.nsl()
        elif isinstance(obj, tuple) and len(obj) in (3, 4):
            net, sta, loc = obj[:3]
        else:
            raise InvalidObject(
                'cannot get nsl code from given object of type %s' % type(obj))

        return net, sta, loc

    def get_nslc(self, obj):
        if isinstance(obj, trace.Trace):
            return obj.nslc_id
        elif isinstance(obj, tuple) and len(obj) == 4:
            return obj
        else:
            raise InvalidObject(
                'cannot get nslc code from given object %s' % type(obj))

    def get_tmin_tmax(self, obj):
        if isinstance(obj, trace.Trace):
            return obj.tmin, obj.tmax
        else:
            raise InvalidObject(
                'cannot get tmin and tmax from given object of type %s' %
                type(obj))

    def get_station(self, obj):
        if self.is_blacklisted(obj):
            raise NotFound('station is blacklisted', self.get_nsl(obj))

        if not self.is_whitelisted(obj):
            raise NotFound('station is not on whitelist', self.get_nsl(obj))

        if isinstance(obj, model.Station):
            return obj

        net, sta, loc = self.get_nsl(obj)

        keys = [(net, sta, loc), (net, sta, ''), ('', sta, '')]
        for k in keys:
            if k in self.stations:
                return self.stations[k]

284
        raise NotFound('no station information', keys)
Sebastian Heimann's avatar
Sebastian Heimann committed
285
286
287
288
289
290
291

    def get_stations(self):
        return [self.stations[k] for k in sorted(self.stations)
                if not self.is_blacklisted(self.stations[k])
                and self.is_whitelisted(self.stations[k])]

    def get_response(self, obj):
292
        if (self.responses is None or len(self.responses) == 0) \
293
294
295
                and (self.responses_stationxml is None
                     or len(self.responses_stationxml) == 0):

296
297
            raise NotFound('no response information available')

Sebastian Heimann's avatar
Sebastian Heimann committed
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
        if self.is_blacklisted(obj):
            raise NotFound('response is blacklisted', self.get_nslc(obj))

        if not self.is_whitelisted(obj):
            raise NotFound('response is not on whitelist', self.get_nslc(obj))

        net, sta, loc, cha = self.get_nslc(obj)
        tmin, tmax = self.get_tmin_tmax(obj)

        keys_x = [
            (net, sta, loc, cha), (net, sta, '', cha), ('', sta, '', cha)]

        keys = []
        for k in keys_x:
            if k not in keys:
                keys.append(k)

        candidates = []
        for k in keys:
            if k in self.responses:
                for x in self.responses[k]:
                    if x.tmin < tmin and (x.tmax is None or tmax < x.tmax):
                        candidates.append(x.response)

        for sx in self.responses_stationxml:
            try:
                candidates.append(
                    sx.get_pyrocko_response(
                        (net, sta, loc, cha),
                        timespan=(tmin, tmax),
                        fake_input_units='M'))

            except fs.NoResponseInformation, fs.MultipleResponseInformation:
                pass

        if len(candidates) == 1:
            return candidates[0]

        elif len(candidates) == 0:
337
            raise NotFound('no response found', (net, sta, loc, cha))
Sebastian Heimann's avatar
Sebastian Heimann committed
338
        else:
339
            raise NotFound('multiple responses found', (net, sta, loc, cha))
Sebastian Heimann's avatar
Sebastian Heimann committed
340
341
342
343
344
345
346
347
348
349
350

    def get_waveforms_raw(self, obj, tmin=None, tmax=None, tpad=0.):
        net, sta, loc = self.get_nsl(obj)

        trs = self.pile.all(
            tmin=tmin, tmax=tmax, tpad=tpad,
            trace_selector=lambda tr: tr.nslc_id[:3] == (net, sta, loc),
            want_incomplete=False)

        return trs

Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
351
352
353
354
355
356
357
    def get_waveform_raw(
            self, obj,
            tmin=None,
            tmax=None,
            tpad=0.,
            toffset_noise_extract=0.):

Sebastian Heimann's avatar
Sebastian Heimann committed
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
        net, sta, loc, cha = self.get_nslc(obj)

        if self.is_blacklisted((net, sta, loc, cha)):
            raise NotFound(
                'waveform is blacklisted', (net, sta, loc, cha))

        if not self.is_whitelisted((net, sta, loc, cha)):
            raise NotFound(
                'waveform is not on whitelist', (net, sta, loc, cha))

        if self.clip_handling == 'by_nsl':
            if self.has_clipping((net, sta, loc), tmin, tmax):
                raise NotFound(
                    'waveform clipped', (net, sta, loc))

        elif self.clip_handling == 'by_nslc':
            if self.has_clipping((net, sta, loc, cha), tmin, tmax):
                raise NotFound(
                    'waveform clipped', (net, sta, loc, cha))

        trs = self.pile.all(
Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
379
380
381
            tmin=tmin+toffset_noise_extract,
            tmax=tmax+toffset_noise_extract,
            tpad=tpad,
Sebastian Heimann's avatar
Sebastian Heimann committed
382
            trace_selector=lambda tr: tr.nslc_id == (net, sta, loc, cha),
383
            want_incomplete=False)
Sebastian Heimann's avatar
Sebastian Heimann committed
384

Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
385
386
387
388
        if toffset_noise_extract != 0.0:
            for tr in trs:
                tr.shift(-toffset_noise_extract)

Sebastian Heimann's avatar
Sebastian Heimann committed
389
390
391
392
393
394
395
396
397
398
399
        if len(trs) == 1:
            return trs[0]

        else:
            raise NotFound(
                'waveform missing or incomplete', (net, sta, loc, cha))

    def get_waveform_restituted(
            self,
            obj, quantity='displacement',
            tmin=None, tmax=None, tpad=0.,
Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
400
401
402
403
            tfade=0., freqlimits=None, deltat=None,
            toffset_noise_extract=0.):

        assert quantity == 'displacement'  # others not yet implemented
Sebastian Heimann's avatar
Sebastian Heimann committed
404

Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
405
406
407
        tr = self.get_waveform_raw(
            obj, tmin=tmin, tmax=tmax, tpad=tpad+tfade,
            toffset_noise_extract=toffset_noise_extract)
Sebastian Heimann's avatar
Sebastian Heimann committed
408
409
410

        if deltat is not None:
            tr.downsample_to(deltat, snap=True)
Sebastian Heimann's avatar
Sebastian Heimann committed
411
            tr.deltat = deltat
Sebastian Heimann's avatar
Sebastian Heimann committed
412
413
414
415
416

        resp = self.get_response(tr)
        return tr.transfer(tfade=tfade, freqlimits=freqlimits,
                           transfer_function=resp, invert=True)

417
418
    def _get_projections(
            self, station, backazimuth, source, target, tmin, tmax):
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447

        # fill in missing channel information (happens when station file
        # does not contain any channel information)
        if not station.get_channels():
            station = copy.deepcopy(station)

            nsl = station.nsl()
            trs = self.pile.all(
                tmin=tmin, tmax=tmax,
                trace_selector=lambda tr: tr.nslc_id[:3] == nsl,
                load_data=False)

            channels = list(set(tr.channel for tr in trs))
            station.set_channels_by_name(*channels)

        projections = []
        projections.extend(station.guess_projections_to_enu(
            out_channels=('E', 'N', 'Z')))

        if source is not None and target is not None:
            backazimuth = source.azibazi_to(target)[1]

        if backazimuth is not None:
            projections.extend(station.guess_projections_to_rtu(
                out_channels=('R', 'T', 'Z'),
                backazimuth=backazimuth))

        if not projections:
            raise NotFound(
448
449
                'cannot determine projection of data components',
                station.nsl())
450
451
452

        return projections

Sebastian Heimann's avatar
Sebastian Heimann committed
453
454
455
456
457
458
459
460
461
    def get_waveform(
            self,
            obj, quantity='displacement',
            tmin=None, tmax=None, tpad=0.,
            tfade=0., freqlimits=None, deltat=None, cache=None,
            backazimuth=None,
            source=None,
            target=None):

Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
462
463
        assert quantity == 'displacement'  # others not yet implemented

Sebastian Heimann's avatar
Sebastian Heimann committed
464
465
466
467
468
469
470
471
        if cache is True:
            cache = self._cache

        _, _, _, channel = self.get_nslc(obj)
        station = self.get_station(self.get_nsl(obj))

        nslc = station.nsl() + (channel,)

472
473
474
475
476
477
478
479
        if self.is_blacklisted(nslc):
            raise NotFound(
                'waveform is blacklisted', nslc)

        if not self.is_whitelisted(nslc):
            raise NotFound(
                'waveform is not on whitelist', nslc)

Sebastian Heimann's avatar
Sebastian Heimann committed
480
481
482
483
484
485
486
487
488
489
490
491
492
        if tmin is not None:
            tmin = float(tmin)

        if tmax is not None:
            tmax = float(tmax)

        if cache is not None and (nslc, tmin, tmax) in cache:
            obj = cache[nslc, tmin, tmax]
            if isinstance(obj, Exception):
                raise obj
            else:
                return obj

Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
493
494
495
496
497
498
499
500
        syn_test = self.synthetic_test
        toffset_noise_extract = 0.0
        if syn_test:
            if syn_test.ignore_data_availability:
                if syn_test.add_real_noise:
                    raise DatasetError(
                        'ignore_data_availability=True and '
                        'add_real_noise=True cannot be combined.')
Sebastian Heimann's avatar
Sebastian Heimann committed
501

Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
502
503
504
505
506
507
508
509
510
511
512
                tr = syn_test.get_waveform(
                    nslc, tmin, tmax,
                    tfade=tfade, freqlimits=freqlimits)

                if cache is not None:
                    cache[tr.nslc_id, tmin, tmax] = tr

                return tr

            if syn_test.add_real_noise:
                toffset_noise_extract = syn_test.toffset_real_noise
Sebastian Heimann's avatar
Sebastian Heimann committed
513
514
515
516
517
518
519
520
521
522
523
524

        abs_delays = []
        for ocha in 'ENZRT':
            sc = self.station_corrections.get(station.nsl() + (channel,), None)
            if sc:
                abs_delays.append(abs(sc.delay))

        if abs_delays:
            abs_delay_max = max(abs_delays)
        else:
            abs_delay_max = 0.0

525
526
        projections = self._get_projections(
            station, backazimuth, source, target, tmin, tmax)
527

Sebastian Heimann's avatar
Sebastian Heimann committed
528
529
        try:
            trs_projected = []
530
            for matrix, in_channels, out_channels in projections:
Sebastian Heimann's avatar
Sebastian Heimann committed
531
532
533
534
535
536
537
538
539
                deps = trace.project_dependencies(
                    matrix, in_channels, out_channels)

                trs = []
                if channel in deps:
                    for cha in deps[channel]:
                        trs.append(self.get_waveform_restituted(
                            station.nsl() + (cha,),
                            tmin=tmin, tmax=tmax, tpad=tpad+abs_delay_max,
Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
540
                            toffset_noise_extract=toffset_noise_extract,
Sebastian Heimann's avatar
Sebastian Heimann committed
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
                            tfade=tfade, freqlimits=freqlimits, deltat=deltat))

                    trs_projected.extend(
                        trace.project(trs, matrix, in_channels, out_channels))

            for tr in trs_projected:
                sc = self.station_corrections.get(tr.nslc_id, None)
                if sc:
                    if self.apply_correction_factors:
                        tr.ydata /= sc.factor

                    if self.apply_correction_delays:
                        tr.shift(-sc.delay)

                if tmin is not None and tmax is not None:
                    tr.chop(tmin, tmax)

Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
            if syn_test:
                trs_projected_synthetic = []
                for tr in trs_projected:
                    tr_syn = syn_test.get_waveform(
                        tr.nslc_id, tmin, tmax,
                        tfade=tfade, freqlimits=freqlimits)

                    if tr_syn:
                        if syn_test.add_real_noise:
                            tr_syn = tr_syn.copy()
                            tr_syn.add(tr)

                        trs_projected_synthetic.append(tr_syn)

                trs_projected = trs_projected_synthetic

Sebastian Heimann's avatar
Sebastian Heimann committed
574
575
576
577
578
579
580
581
            if cache is not None:
                for tr in trs_projected:
                    cache[tr.nslc_id, tmin, tmax] = tr

            for tr in trs_projected:
                if tr.channel == channel:
                    return tr

582
583
            raise NotFound(
                'waveform not available', station.nsl() + (channel,))
Sebastian Heimann's avatar
Sebastian Heimann committed
584
585
586
587
588

        except NotFound, e:
            cache[nslc, tmin, tmax] = e
            raise

Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
589
    def get_events(self, magmin=None, event_names=None):
Sebastian Heimann's avatar
Sebastian Heimann committed
590
591
        evs = []
        for ev in self.events:
Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
592
593
            if ((magmin is None or ev.magnitude >= magmin) and
                    (event_names is None or ev.name in event_names)):
Sebastian Heimann's avatar
Sebastian Heimann committed
594
595
596
597
598
599
600
601
602
603
604
605
                evs.append(ev)

        return evs

    def get_event(self, t, magmin=None):
        evs = self.get_events(magmin=magmin)
        ev_x = None
        for ev in evs:
            if ev_x is None or abs(ev.time - t) < abs(ev_x.time - t):
                ev_x = ev

        if not ev_x:
606
607
608
            raise NotFound(
                'no event information matching criteria (t=%s, magmin=%s)' %
                (t, magmin))
Sebastian Heimann's avatar
Sebastian Heimann committed
609
610
611

        return ev_x

612
613
614
615
616
617
618
619
620
621
    def get_picks(self):
        if self._picks is None:
            hash_to_name = {}
            names = set()
            for marker in self.pick_markers:
                if isinstance(marker, gui_util.EventMarker):
                    name = marker.get_event().name
                    if name in names:
                        raise DatasetError(
                            'duplicate event name "%s" in picks' % name)
Sebastian Heimann's avatar
Sebastian Heimann committed
622

623
624
                    names.add(name)
                    hash_to_name[marker.get_event_hash()] = name
Sebastian Heimann's avatar
Sebastian Heimann committed
625

626
627
628
629
            picks = {}
            for marker in self.pick_markers:
                if isinstance(marker, gui_util.PhaseMarker):
                    ehash = marker.get_event_hash()
Sebastian Heimann's avatar
Sebastian Heimann committed
630

631
632
                    nsl = marker.one_nslc()[:3]
                    phasename = marker.get_phasename()
Sebastian Heimann's avatar
Sebastian Heimann committed
633

634
635
636
637
                    if ehash is None or ehash not in hash_to_name:
                        raise DatasetError(
                            'unassociated pick %s.%s.%s, %s' %
                            (nsl + (phasename, )))
Sebastian Heimann's avatar
Sebastian Heimann committed
638

639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
                    eventname = hash_to_name[ehash]

                    if (nsl, phasename, eventname) in picks:
                        raise DatasetError(
                            'duplicate pick %s.%s.%s, %s' %
                            (nsl + (phasename, )))

                    picks[nsl, phasename, eventname] = marker

            self._picks = picks

        return self._picks

    def get_pick(self, eventname, obj, phasename):
        nsl = self.get_nsl(obj)
        return self.get_picks().get((nsl, phasename, eventname), None)
Sebastian Heimann's avatar
Sebastian Heimann committed
655

Sebastian Heimann's avatar
Sebastian Heimann committed
656
657

__all__ = '''
658
    DatasetError
Sebastian Heimann's avatar
Sebastian Heimann committed
659
660
661
662
663
664
665
    InvalidObject
    NotFound
    StationCorrection
    Dataset
    load_station_corrections
    dump_station_corrections
'''.split()