dataset.py 20.6 KB
Newer Older
Sebastian Heimann's avatar
Sebastian Heimann committed
1
2
3
4

import logging
from collections import defaultdict
import numpy as num
Sebastian Heimann's avatar
Sebastian Heimann committed
5
from pyrocko import util, pile, model, config, trace, snuffling, gui_util
Sebastian Heimann's avatar
Sebastian Heimann committed
6
7
8
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
9
guts_prefix = 'grond'
Sebastian Heimann's avatar
Sebastian Heimann committed
10
11
12
13
14
15
16
17
18

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


class InvalidObject(Exception):
    pass


class NotFound(Exception):
19
20
21
22
23
    def __init__(self, *value):
        self.value = value

    def __str__(self):
        return ' '.join([str(v) for v in self.value])
Sebastian Heimann's avatar
Sebastian Heimann committed
24
25


26
27
28
29
class DatasetError(Exception):
    pass


Sebastian Heimann's avatar
Sebastian Heimann committed
30
31
32
33
34
35
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
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
62
        self.pick_markers = []
Sebastian Heimann's avatar
Sebastian Heimann committed
63
64
65
66
        self.apply_correction_delays = True
        self.apply_correction_factors = True
        self.clip_handling = 'by_nsl'
        self.synthetic_test = None
67
        self._picks = None
Sebastian Heimann's avatar
Sebastian Heimann committed
68
69
70
71
72
73
74
75
        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
76
77
78
79
80
81
    def add_stations(
            self,
            stations=None,
            pyrocko_stations_filename=None,
            stationxml_filenames=None):

Sebastian Heimann's avatar
Sebastian Heimann committed
82
83
84
85
        if stations is not None:
            for station in stations:
                self.stations[station.nsl()] = station

Sebastian Heimann's avatar
Sebastian Heimann committed
86
        if pyrocko_stations_filename is not None:
87
88
            logger.debug('Loading stations from file %s'
                % pyrocko_stations_filename)
Sebastian Heimann's avatar
Sebastian Heimann committed
89
            for station in model.load_stations(pyrocko_stations_filename):
Sebastian Heimann's avatar
Sebastian Heimann committed
90
91
                self.stations[station.nsl()] = station

92
93
94
        if stationxml_filenames is not None and len(stationxml_filenames) > 0:
            logger.debug('Loading stations from StationXML file %s'
                % stationxml_filenames)
Sebastian Heimann's avatar
Sebastian Heimann committed
95
96
97
98
99
            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
100
101
102
103
104
    def add_events(self, events=None, filename=None):
        if events is not None:
            self.events.extend(events)

        if filename is not None:
105
            logger.debug('Loading events from file %s' % filename)
Sebastian Heimann's avatar
Sebastian Heimann committed
106
107
108
109
            self.events.extend(model.load_events(filename))

    def add_waveforms(self, paths, regex=None, fileformat='detect',
                      show_progress=True):
110
        logger.debug('Loading waveform data from %s' % paths)
Sebastian Heimann's avatar
Sebastian Heimann committed
111
112
113
114
115
116
117
118
        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)

119
    def add_responses(self, sacpz_dirname=None, stationxml_filenames=None):            
Sebastian Heimann's avatar
Sebastian Heimann committed
120
        if sacpz_dirname:
121
            logger.debug('Loading SAC PZ responses from %s' % sacpz_dirname)
Sebastian Heimann's avatar
Sebastian Heimann committed
122
123
124
125
126
            for x in enhanced_sacpz.iload_dirname(sacpz_dirname):
                self.responses[x.codes].append(x)

        if stationxml_filenames:
            for stationxml_filename in stationxml_filenames:
127
128
                logger.debug('Loading StationXML responses from %s' %
                    stationxml_filename)
Sebastian Heimann's avatar
Sebastian Heimann committed
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
                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):
155
        logger.debug('Loading blacklisted stations')
Sebastian Heimann's avatar
Sebastian Heimann committed
156
157
158
159
160
161
        for x in blacklist:
            if isinstance(x, basestring):
                x = tuple(x.split('.'))
            self.blacklist.add(x)

    def add_whitelist(self, whitelist):
162
        logger.debug('Loading whitelisted stations')
Sebastian Heimann's avatar
Sebastian Heimann committed
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
        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
182
183
184
185
    def add_picks(self, filename):
        self.pick_markers.extend(
            gui_util.load_markers(filename))

186
187
        self._picks = None

Sebastian Heimann's avatar
Sebastian Heimann committed
188
189
190
191
192
193
194
195
196
197
198
199
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
    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]

        raise NotFound('station', keys)

    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):
280
281
282
283
284
        if (self.responses is None or len(self.responses) == 0) \
            and (self.responses_stationxml is None \
            or len(self.responses_stationxml) == 0):
            raise NotFound('no response information available')

Sebastian Heimann's avatar
Sebastian Heimann committed
285
286
287
288
289
290
291
292
293
294
295
296
297
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
337
        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:
            raise NotFound('no response', (net, sta, loc, cha))
        else:
            raise NotFound('multiple responses', (net, sta, loc, cha))

    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
338
339
340
341
342
343
344
    def get_waveform_raw(
            self, obj,
            tmin=None,
            tmax=None,
            tpad=0.,
            toffset_noise_extract=0.):

Sebastian Heimann's avatar
Sebastian Heimann committed
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
        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
366
367
368
            tmin=tmin+toffset_noise_extract,
            tmax=tmax+toffset_noise_extract,
            tpad=tpad,
Sebastian Heimann's avatar
Sebastian Heimann committed
369
            trace_selector=lambda tr: tr.nslc_id == (net, sta, loc, cha),
370
            want_incomplete=False)
Sebastian Heimann's avatar
Sebastian Heimann committed
371

Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
372
373
374
375
        if toffset_noise_extract != 0.0:
            for tr in trs:
                tr.shift(-toffset_noise_extract)

Sebastian Heimann's avatar
Sebastian Heimann committed
376
377
378
379
380
381
382
383
384
385
386
        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
387
388
389
390
            tfade=0., freqlimits=None, deltat=None,
            toffset_noise_extract=0.):

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

Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
392
393
394
        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
395
396
397

        if deltat is not None:
            tr.downsample_to(deltat, snap=True)
Sebastian Heimann's avatar
Sebastian Heimann committed
398
            tr.deltat = deltat
Sebastian Heimann's avatar
Sebastian Heimann committed
399
400
401
402
403

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

404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
    def get_projections(self, station, source, target, tmin, tmax):

        # 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(
                'cannot determine projection of data components', nslc)

        return projections

Sebastian Heimann's avatar
Sebastian Heimann committed
438
439
440
441
442
443
444
445
446
    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
447
448
        assert quantity == 'displacement'  # others not yet implemented

Sebastian Heimann's avatar
Sebastian Heimann committed
449
450
451
452
453
454
455
456
        if cache is True:
            cache = self._cache

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

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

457
458
459
460
461
462
463
464
        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
465
466
467
468
469
470
471
472
473
474
475
476
477
        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
478
479
480
481
482
483
484
485
        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
486

Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
487
488
489
490
491
492
493
494
495
496
497
                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
498
499
500
501
502
503
504
505
506
507
508
509

        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

510
        projections = self.get_projections(station, source, target, tmin, tmax)
511

Sebastian Heimann's avatar
Sebastian Heimann committed
512
513
        try:
            trs_projected = []
514
            for matrix, in_channels, out_channels in projections:
Sebastian Heimann's avatar
Sebastian Heimann committed
515
516
517
518
519
520
521
522
523
                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
524
                            toffset_noise_extract=toffset_noise_extract,
Sebastian Heimann's avatar
Sebastian Heimann committed
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
                            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
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
            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
558
559
560
561
562
563
564
565
566
567
568
569
570
571
            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

            raise NotFound('waveform', station.nsl() + (channel,))

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

Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
572
    def get_events(self, magmin=None, event_names=None):
Sebastian Heimann's avatar
Sebastian Heimann committed
573
574
        evs = []
        for ev in self.events:
Sebastian Heimann's avatar
wip    
Sebastian Heimann committed
575
576
            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
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
                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:
            raise NotFound

        return ev_x

593
594
595
596
597
598
599
600
601
602
    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
603

604
605
                    names.add(name)
                    hash_to_name[marker.get_event_hash()] = name
Sebastian Heimann's avatar
Sebastian Heimann committed
606

607
608
609
610
            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
611

612
613
                    nsl = marker.one_nslc()[:3]
                    phasename = marker.get_phasename()
Sebastian Heimann's avatar
Sebastian Heimann committed
614

615
616
617
618
                    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
619

620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
                    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
636

Sebastian Heimann's avatar
Sebastian Heimann committed
637
638

__all__ = '''
639
    DatasetError
Sebastian Heimann's avatar
Sebastian Heimann committed
640
641
642
643
644
645
646
    InvalidObject
    NotFound
    StationCorrection
    Dataset
    load_station_corrections
    dump_station_corrections
'''.split()