/
stream.py
3646 lines (3168 loc) · 151 KB
/
stream.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
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
280
281
282
283
284
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
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
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
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# -*- coding: utf-8 -*-
"""
Module for handling ObsPy :class:`~obspy.core.stream.Stream` objects.
:copyright:
The ObsPy Development Team (devs@obspy.org)
:license:
GNU Lesser General Public License, Version 3
(https://www.gnu.org/copyleft/lesser.html)
"""
import collections
import copy
import fnmatch
import math
import pickle
import re
import warnings
from pathlib import Path
from glob import glob, has_magic
import numpy as np
from obspy.core import compatibility
from obspy.core.trace import Trace
from obspy.core.utcdatetime import UTCDateTime
from obspy.core.util.attribdict import AttribDict
from obspy.core.util.base import (ENTRY_POINTS, _get_function_from_entry_point,
_read_from_plugin, _generic_reader)
from obspy.core.util.decorator import (map_example_filename,
raise_if_masked, uncompress_file)
from obspy.core.util.misc import get_window_times, buffered_load_entry_point
from obspy.core.util.obspy_types import ObsPyException
_headonly_warning_msg = (
"Keyword headonly cannot be combined with starttime, endtime or dtype.")
@map_example_filename("pathname_or_url")
def read(pathname_or_url=None, format=None, headonly=False, starttime=None,
endtime=None, nearest_sample=True, dtype=None, apply_calib=False,
check_compression=True, **kwargs):
"""
Read waveform files into an ObsPy :class:`~obspy.core.stream.Stream`
object.
The :func:`~obspy.core.stream.read` function opens either one or multiple
waveform files given via file name or URL using the ``pathname_or_url``
attribute.
The format of the waveform file will be automatically detected if not
given. See the `Supported Formats`_ section below for available formats.
This function returns an ObsPy :class:`~obspy.core.stream.Stream` object, a
``list``-like object of multiple ObsPy :class:`~obspy.core.trace.Trace`
objects.
:type pathname_or_url: str, io.BytesIO, or pathlib.Path, optional
:param pathname_or_url: String containing a file name or a URL, a Path
object, or a open file-like object. Wildcards are allowed for a file
name. If this attribute is omitted, an example
:class:`~obspy.core.stream.Stream` object will be returned.
:type format: str, optional
:param format: Format of the file to read (e.g. ``"MSEED"``). See
the `Supported Formats`_ section below for a list of supported formats.
If format is set to ``None`` it will be automatically detected which
results in a slightly slower reading. If a format is specified, no
further format checking is done.
:type headonly: bool, optional
:param headonly: If set to ``True``, read only the data header. This is
most useful for scanning available meta information of huge data sets.
:type starttime: :class:`~obspy.core.utcdatetime.UTCDateTime`, optional
:param starttime: Specify the start time to read.
:type endtime: :class:`~obspy.core.utcdatetime.UTCDateTime`, optional
:param endtime: Specify the end time to read.
:type nearest_sample: bool, optional
:param nearest_sample: Only applied if `starttime` or `endtime` is given.
Select nearest sample or the one containing the specified time. For
more info, see :meth:`~obspy.core.trace.Trace.trim`.
:type dtype: :class:`numpy.dtype`, optional
:param dtype: Convert data of all traces into given numpy.dtype.
:type apply_calib: bool, optional
:param apply_calib: Automatically applies the calibration factor
``trace.stats.calib`` for each trace, if set. Defaults to ``False``.
:param check_compression: Check for compression on file and decompress
if needed. This may be disabled for a moderate speed up.
:type check_compression: bool, optional
:param kwargs: Additional keyword arguments passed to the underlying
waveform reader method.
:return: An ObsPy :class:`~obspy.core.stream.Stream` object.
.. rubric:: Basic Usage
In most cases a filename is specified as the only argument to
:func:`~obspy.core.stream.read`. For a quick start you may omit all
arguments and ObsPy will create and return a basic example seismogram.
Further usages of the :func:`~obspy.core.stream.read` function can
be seen in the `Further Examples`_ section underneath.
>>> from obspy import read
>>> st = read()
>>> print(st) # doctest: +ELLIPSIS
3 Trace(s) in Stream:
BW.RJOB..EHZ | 2009-08-24T00:20:03.000000Z - ... | 100.0 Hz, 3000 samples
BW.RJOB..EHN | 2009-08-24T00:20:03.000000Z - ... | 100.0 Hz, 3000 samples
BW.RJOB..EHE | 2009-08-24T00:20:03.000000Z - ... | 100.0 Hz, 3000 samples
.. rubric:: _`Supported Formats`
Additional ObsPy modules extend the functionality of the
:func:`~obspy.core.stream.read` function. The following table summarizes
all known file formats currently supported by ObsPy. The table order also
reflects the order of the autodetection routine if no format option is
specified.
Please refer to the `Linked Function Call`_ of each module for any extra
options available at the import stage.
%s
Next to the :func:`~obspy.core.stream.read` function the
:meth:`~obspy.core.stream.Stream.write` method of the returned
:class:`~obspy.core.stream.Stream` object can be used to export the data
to the file system.
.. rubric:: _`Further Examples`
Example waveform files may be retrieved via https://examples.obspy.org.
(1) Reading multiple local files using wildcards.
The following code uses wildcards, in this case it matches two files.
Both files are then read into a single
:class:`~obspy.core.stream.Stream` object.
>>> from obspy import read # doctest: +SKIP
>>> st = read("/path/to/loc_R*.z") # doctest: +SKIP
>>> print(st) # doctest: +SKIP
2 Trace(s) in Stream:
.RJOB..Z | 2005-08-31T02:33:49.850000Z - ... | 200.0 Hz, 12000 samples
.RNON..Z | 2004-06-09T20:05:59.850000Z - ... | 200.0 Hz, 12000 samples
(2) Reading a local file without format detection.
Using the ``format`` parameter disables the automatic detection and
enforces reading a file in a given format.
>>> from obspy import read
>>> st = read("/path/to/loc_RJOB20050831023349.z", format="GSE2")
>>> print(st) # doctest: +ELLIPSIS
1 Trace(s) in Stream:
.RJOB..Z | 2005-08-31T02:33:49.850000Z - ... | 200.0 Hz, 12000 samples
(3) Reading a remote file via HTTP protocol.
>>> from obspy import read
>>> st = read("https://examples.obspy.org/loc_RJOB20050831023349.z")
>>> print(st) # doctest: +ELLIPSIS
1 Trace(s) in Stream:
.RJOB..Z | 2005-08-31T02:33:49.850000Z - ... | 200.0 Hz, 12000 samples
(4) Reading a compressed files.
>>> from obspy import read
>>> st = read("/path/to/tspair.ascii.gz")
>>> print(st) # doctest: +ELLIPSIS
1 Trace(s) in Stream:
XX.TEST..BHZ | 2008-01-15T00:00:00.025000Z - ... | 40.0 Hz, 635 samples
>>> st = read("https://examples.obspy.org/slist.ascii.bz2")
>>> print(st) # doctest: +ELLIPSIS
1 Trace(s) in Stream:
XX.TEST..BHZ | 2008-01-15T00:00:00.025000Z - ... | 40.0 Hz, 635 samples
(5) Reading a file-like object.
>>> import requests
>>> import io
>>> example_url = "https://examples.obspy.org/loc_RJOB20050831023349.z"
>>> stringio_obj = io.BytesIO(requests.get(example_url).content)
>>> st = read(stringio_obj)
>>> print(st) # doctest: +ELLIPSIS
1 Trace(s) in Stream:
.RJOB..Z | 2005-08-31T02:33:49.850000Z - ... | 200.0 Hz, 12000 samples
(6) Using 'starttime' and 'endtime' parameters
>>> from obspy import read
>>> dt = UTCDateTime("2005-08-31T02:34:00")
>>> st = read("https://examples.obspy.org/loc_RJOB20050831023349.z",
... starttime=dt, endtime=dt+10)
>>> print(st) # doctest: +ELLIPSIS
1 Trace(s) in Stream:
.RJOB..Z | 2005-08-31T02:34:00.000000Z - ... | 200.0 Hz, 2001 samples
"""
# add default parameters to kwargs so sub-modules may handle them
kwargs['starttime'] = starttime
kwargs['endtime'] = endtime
kwargs['nearest_sample'] = nearest_sample
kwargs['check_compression'] = check_compression
kwargs['headonly'] = headonly
kwargs['format'] = format
if pathname_or_url is None:
# if no pathname or URL specified, return example stream
st = _create_example_stream(headonly=headonly)
else:
st = _generic_reader(pathname_or_url, _read, **kwargs)
if len(st) == 0:
# try to give more specific information why the stream is empty
if has_magic(pathname_or_url) and not glob(pathname_or_url):
raise Exception("No file matching file pattern: %s" %
pathname_or_url)
elif not has_magic(pathname_or_url) and \
not Path(pathname_or_url).is_file():
raise IOError(2, "No such file or directory", pathname_or_url)
# Only raise error if no start/end time has been set. This
# will return an empty stream if the user chose a time window with
# no data in it.
# XXX: Might cause problems if the data is faulty and the user
# set start/end time. Not sure what to do in this case.
elif not starttime and not endtime:
raise Exception("Cannot open file/files: %s" % pathname_or_url)
# Trim if times are given.
if headonly and (starttime or endtime or dtype):
warnings.warn(_headonly_warning_msg, UserWarning)
return st
if starttime:
st._ltrim(starttime, nearest_sample=nearest_sample)
if endtime:
st._rtrim(endtime, nearest_sample=nearest_sample)
# convert to dtype if given
if dtype:
for tr in st:
tr.data = np.require(tr.data, dtype)
# applies calibration factor
if apply_calib:
for tr in st:
tr.data = tr.data * tr.stats.calib
return st
@uncompress_file
def _read(filename, format=None, headonly=False, **kwargs):
"""
Read a single file into a ObsPy Stream object.
"""
stream, format = _read_from_plugin('waveform', filename, format=format,
headonly=headonly, **kwargs)
# set _format identifier for each element
for trace in stream:
trace.stats._format = format
return stream
def _create_example_stream(headonly=False):
"""
Create an example stream.
Data arrays are stored in NumPy's NPZ format. The header information are
fixed values.
PAZ of the used instrument, needed to demonstrate simulate_seismometer()
etc.::
paz = {'gain': 60077000.0,
'poles': [-0.037004+0.037016j, -0.037004-0.037016j, -251.33+0j,
-131.04-467.29j, -131.04+467.29j],
'sensitivity': 2516778400.0,
'zeros': [0j, 0j]}}
"""
data_dir = Path(__file__).parent / "data"
if not headonly:
path = data_dir / "example.npz"
data = np.load(path)
st = Stream()
for channel in ["EHZ", "EHN", "EHE"]:
header = {'network': "BW",
'station': "RJOB",
'location': "",
'npts': 3000,
'starttime': UTCDateTime(2009, 8, 24, 0, 20, 3),
'sampling_rate': 100.0,
'calib': 1.0,
'back_azimuth': 100.0,
'inclination': 30.0}
header['channel'] = channel
if not headonly:
st.append(Trace(data=data[channel], header=header))
else:
st.append(Trace(header=header))
from obspy import read_inventory
inv = read_inventory(data_dir / "BW_RJOB.xml")
st.attach_response(inv)
return st
class Stream(object):
"""
List like object of multiple ObsPy :class:`~obspy.core.trace.Trace`
objects.
:type traces: list of :class:`~obspy.core.trace.Trace`, optional
:param traces: Initial list of ObsPy :class:`~obspy.core.trace.Trace`
objects.
.. rubric:: Basic Usage
>>> trace1 = Trace()
>>> trace2 = Trace()
>>> stream = Stream(traces=[trace1, trace2])
>>> print(stream) # doctest: +ELLIPSIS
2 Trace(s) in Stream:
...
.. rubric:: Supported Operations
``stream = streamA + streamB``
Merges all traces within the two Stream objects ``streamA`` and
``streamB`` into the new Stream object ``stream``.
See also: :meth:`Stream.__add__`.
``stream += streamA``
Extends the Stream object ``stream`` with all traces from ``streamA``.
See also: :meth:`Stream.__iadd__`.
``len(stream)``
Returns the number of Traces in the Stream object ``stream``.
See also: :meth:`Stream.__len__`.
``str(stream)``
Contains the number of traces in the Stream object and returns the
value of each Trace's __str__ method.
See also: :meth:`Stream.__str__`.
"""
def __init__(self, traces=None):
self.traces = []
if isinstance(traces, Trace):
traces = [traces]
if traces:
self.traces.extend(traces)
def __add__(self, other):
"""
Add two streams or a stream with a single trace.
:type other: :class:`~obspy.core.stream.Stream` or
:class:`~obspy.core.trace.Trace`
:param other: Stream or Trace object to add.
:rtype: :class:`~obspy.core.stream.Stream`
:returns: New Stream object containing references to the traces of the
original streams
.. rubric:: Examples
1. Adding two Streams
>>> st1 = Stream([Trace(), Trace(), Trace()])
>>> len(st1)
3
>>> st2 = Stream([Trace(), Trace()])
>>> len(st2)
2
>>> stream = st1 + st2
>>> len(stream)
5
2. Adding Stream and Trace
>>> stream2 = st1 + Trace()
>>> len(stream2)
4
"""
if isinstance(other, Trace):
other = Stream([other])
if not isinstance(other, Stream):
raise TypeError
traces = self.traces + other.traces
return self.__class__(traces=traces)
def __iadd__(self, other):
"""
Add two streams with self += other.
It will extend the current Stream object with the traces of the given
Stream. Traces will not be copied but references to the original traces
will be appended.
:type other: :class:`~obspy.core.stream.Stream` or
:class:`~obspy.core.trace.Trace`
:param other: Stream or Trace object to add.
.. rubric:: Example
>>> stream = Stream([Trace(), Trace(), Trace()])
>>> len(stream)
3
>>> stream += Stream([Trace(), Trace()])
>>> len(stream)
5
>>> stream += Trace()
>>> len(stream)
6
"""
if isinstance(other, Trace):
other = Stream([other])
if not isinstance(other, Stream):
raise TypeError
self.extend(other.traces)
return self
def __mul__(self, num):
"""
Create a new Stream containing num copies of this stream.
:rtype num: int
:param num: Number of copies.
:returns: New ObsPy Stream object.
.. rubric:: Example
>>> from obspy import read
>>> st = read()
>>> len(st)
3
>>> st2 = st * 5
>>> len(st2)
15
"""
if not isinstance(num, int):
raise TypeError("Integer expected")
from obspy import Stream
st = Stream()
for _i in range(num):
st += self.copy()
return st
def __iter__(self):
"""
Return a robust iterator for stream.traces.
Doing this it is safe to remove traces from streams inside of
for-loops using stream's :meth:`~obspy.core.stream.Stream.remove`
method. Actually this creates a new iterator every time a trace is
removed inside the for-loop.
.. rubric:: Example
>>> from obspy import Stream
>>> st = Stream()
>>> for component in ["1", "Z", "2", "3", "Z", "N", "E", "4", "5"]:
... channel = "EH" + component
... tr = Trace(header={'station': 'TEST', 'channel': channel})
... st.append(tr) # doctest: +ELLIPSIS
<...Stream object at 0x...>
>>> print(st) # doctest: +ELLIPSIS
9 Trace(s) in Stream:
.TEST..EH1 | 1970-01-01T00:00:00.000000Z - ... | 1.0 Hz, 0 samples
.TEST..EHZ | 1970-01-01T00:00:00.000000Z - ... | 1.0 Hz, 0 samples
.TEST..EH2 | 1970-01-01T00:00:00.000000Z - ... | 1.0 Hz, 0 samples
.TEST..EH3 | 1970-01-01T00:00:00.000000Z - ... | 1.0 Hz, 0 samples
.TEST..EHZ | 1970-01-01T00:00:00.000000Z - ... | 1.0 Hz, 0 samples
.TEST..EHN | 1970-01-01T00:00:00.000000Z - ... | 1.0 Hz, 0 samples
.TEST..EHE | 1970-01-01T00:00:00.000000Z - ... | 1.0 Hz, 0 samples
.TEST..EH4 | 1970-01-01T00:00:00.000000Z - ... | 1.0 Hz, 0 samples
.TEST..EH5 | 1970-01-01T00:00:00.000000Z - ... | 1.0 Hz, 0 samples
>>> for tr in st:
... if tr.stats.channel[-1] not in ["Z", "N", "E"]:
... st.remove(tr) # doctest: +ELLIPSIS
<...Stream object at 0x...>
>>> print(st) # doctest: +ELLIPSIS
4 Trace(s) in Stream:
.TEST..EHZ | 1970-01-01T00:00:00.000000Z - ... | 1.0 Hz, 0 samples
.TEST..EHZ | 1970-01-01T00:00:00.000000Z - ... | 1.0 Hz, 0 samples
.TEST..EHN | 1970-01-01T00:00:00.000000Z - ... | 1.0 Hz, 0 samples
.TEST..EHE | 1970-01-01T00:00:00.000000Z - ... | 1.0 Hz, 0 samples
"""
return list(self.traces).__iter__()
def __nonzero__(self):
"""
A Stream is considered zero if has no Traces.
"""
return bool(len(self.traces))
def __len__(self):
"""
Return the number of Traces in the Stream object.
.. rubric:: Example
>>> stream = Stream([Trace(), Trace(), Trace()])
>>> len(stream)
3
"""
return len(self.traces)
count = __len__
def __str__(self, extended=False):
"""
Return short summary string of the current stream.
It will contain the number of Traces in the Stream and the return value
of each Trace's :meth:`~obspy.core.trace.Trace.__str__` method.
:type extended: bool, optional
:param extended: This method will show only 20 traces by default.
Enable this option to show all entries.
.. rubric:: Example
>>> stream = Stream([Trace(), Trace()])
>>> print(stream) # doctest: +ELLIPSIS
2 Trace(s) in Stream:
...
"""
# get longest id
if self.traces:
id_length = self and max(len(tr.id) for tr in self) or 0
else:
id_length = 0
out = str(len(self.traces)) + ' Trace(s) in Stream:\n'
if len(self.traces) <= 20 or extended is True:
out = out + "\n".join([_i.__str__(id_length) for _i in self])
else:
out = out + "\n" + self.traces[0].__str__() + "\n" + \
'...\n(%i other traces)\n...\n' % (len(self.traces) - 2) + \
self.traces[-1].__str__() + '\n\n[Use "print(' + \
'Stream.__str__(extended=True))" to print all Traces]'
return out
def _repr_pretty_(self, p, cycle):
p.text(self.__str__(extended=p.verbose))
def __eq__(self, other):
"""
Implements rich comparison of Stream objects for "==" operator.
Trace order does not effect the comparison because the traces are
sorted beforehand.
This function strictly compares the data and stats objects of each
trace contained by the streams. If less strict behavior is desired,
which may be the case for testing, consider using the
:func:`~obspy.core.util.testing.streams_almost_equal` function.
:type other: :class:`~obspy.core.stream.Stream`
:param other: Stream object for comparison.
:rtype: bool
:return: ``True`` if both Streams contain the same traces, i.e. after a
sort operation going through both streams every trace should be
equal according to Trace's
:meth:`~obspy.core.trace.Trace.__eq__` operator.
.. rubric:: Example
>>> from obspy import read
>>> st = read()
>>> st2 = st.copy()
>>> st is st2
False
>>> st == st2
True
"""
if not isinstance(other, Stream):
return False
# this is maybe still not 100% satisfactory, the question here is if
# two streams should be the same in comparison if one of the streams
# has a duplicate trace. Using sets at the moment, two equal traces
# in one of the Streams would lead to two non-equal Streams.
# This is a bit more conservative and most likely the expected behavior
# in most cases.
self_sorted = self.select()
self_sorted.sort()
other_sorted = other.select()
other_sorted.sort()
if self_sorted.traces != other_sorted.traces:
return False
return True
def __ne__(self, other):
"""
Implements rich comparison of Stream objects for "!=" operator.
.. rubric:: Example
>>> from obspy import read
>>> st = read()
>>> st2 = st.copy()
>>> st is st2
False
>>> st != st2
False
"""
# Calls __eq__() and returns the opposite.
return not self.__eq__(other)
def __lt__(self, other):
"""
Too ambiguous, throw an Error.
"""
raise NotImplementedError("Too ambiguous, therefore not implemented.")
def __le__(self, other):
"""
Too ambiguous, throw an Error.
"""
raise NotImplementedError("Too ambiguous, therefore not implemented.")
def __gt__(self, other):
"""
Too ambiguous, throw an Error.
"""
raise NotImplementedError("Too ambiguous, therefore not implemented.")
def __ge__(self, other):
"""
Too ambiguous, throw an Error.
"""
raise NotImplementedError("Too ambiguous, therefore not implemented.")
def __setitem__(self, index, trace):
"""
__setitem__ method of obspy.Stream objects.
"""
self.traces.__setitem__(index, trace)
def __getitem__(self, index):
"""
__getitem__ method of obspy.Stream objects.
:return: Trace objects
"""
if isinstance(index, slice):
return self.__class__(traces=self.traces.__getitem__(index))
else:
return self.traces.__getitem__(index)
def __delitem__(self, index):
"""
Passes on the __delitem__ method to the underlying list of traces.
"""
return self.traces.__delitem__(index)
def __getslice__(self, i, j, k=1):
"""
__getslice__ method of obspy.Stream objects.
:return: Stream object
"""
# see also https://docs.python.org/3/reference/datamodel.html
return self.__class__(traces=self.traces[max(0, i):max(0, j):k])
def append(self, trace):
"""
Append a single Trace object to the current Stream object.
:param trace: :class:`~obspy.core.trace.Trace` object.
.. rubric:: Example
>>> from obspy import read, Trace
>>> st = read()
>>> tr = Trace()
>>> tr.stats.station = 'TEST'
>>> st.append(tr) # doctest: +ELLIPSIS
<...Stream object at 0x...>
>>> print(st) # doctest: +ELLIPSIS
4 Trace(s) in Stream:
BW.RJOB..EHZ | 2009-08-24T00:20:03.000000Z ... | 100.0 Hz, 3000 samples
BW.RJOB..EHN | 2009-08-24T00:20:03.000000Z ... | 100.0 Hz, 3000 samples
BW.RJOB..EHE | 2009-08-24T00:20:03.000000Z ... | 100.0 Hz, 3000 samples
.TEST.. | 1970-01-01T00:00:00.000000Z ... | 1.0 Hz, 0 samples
"""
if isinstance(trace, Trace):
self.traces.append(trace)
else:
msg = 'Append only supports a single Trace object as an argument.'
raise TypeError(msg)
return self
def extend(self, trace_list):
"""
Extend the current Stream object with a list of Trace objects.
:param trace_list: list of :class:`~obspy.core.trace.Trace` objects or
:class:`~obspy.core.stream.Stream`.
.. rubric:: Example
>>> from obspy import read, Trace
>>> st = read()
>>> tr1 = Trace()
>>> tr1.stats.station = 'TEST1'
>>> tr2 = Trace()
>>> tr2.stats.station = 'TEST2'
>>> st.extend([tr1, tr2]) # doctest: +ELLIPSIS
<...Stream object at 0x...>
>>> print(st) # doctest: +ELLIPSIS
5 Trace(s) in Stream:
BW.RJOB..EHZ | 2009-08-24T00:20:03.000000Z ... | 100.0 Hz, 3000 samples
BW.RJOB..EHN | 2009-08-24T00:20:03.000000Z ... | 100.0 Hz, 3000 samples
BW.RJOB..EHE | 2009-08-24T00:20:03.000000Z ... | 100.0 Hz, 3000 samples
.TEST1.. | 1970-01-01T00:00:00.000000Z ... | 1.0 Hz, 0 samples
.TEST2.. | 1970-01-01T00:00:00.000000Z ... | 1.0 Hz, 0 samples
"""
if isinstance(trace_list, list):
for _i in trace_list:
# Make sure each item in the list is a trace.
if not isinstance(_i, Trace):
msg = 'Extend only accepts a list of Trace objects.'
raise TypeError(msg)
self.traces.extend(trace_list)
elif isinstance(trace_list, Stream):
self.traces.extend(trace_list.traces)
else:
msg = 'Extend only supports a list of Trace objects as argument.'
raise TypeError(msg)
return self
def get_gaps(self, min_gap=None, max_gap=None):
"""
Determine all trace gaps/overlaps of the Stream object.
:param min_gap: All gaps smaller than this value will be omitted. The
value is assumed to be in seconds. Defaults to None.
:param max_gap: All gaps larger than this value will be omitted. The
value is assumed to be in seconds. Defaults to None.
The returned list contains one item in the following form for each gap/
overlap: [network, station, location, channel, starttime of the gap,
end time of the gap, duration of the gap, number of missing samples]
Please be aware that no sorting and checking of stations, channels, ...
is done. This method only compares the start and end times of the
Traces and the start and end times of segments within Traces that
contain masked arrays (i.e., Traces that were merged without a fill
value).
.. rubric:: Example
Our example stream has no gaps:
>>> from obspy import read, UTCDateTime
>>> st = read()
>>> st.get_gaps()
[]
>>> st.print_gaps() # doctest: +ELLIPSIS
Source Last Sample ...
Total: 0 gap(s) and 0 overlap(s)
So let's make a copy of the first trace and cut both so that we end up
with a gappy stream:
>>> tr = st[0].copy()
>>> t = UTCDateTime("2009-08-24T00:20:13.0")
>>> st[0].trim(endtime=t) # doctest: +ELLIPSIS
<...Trace object at 0x...>
>>> tr.trim(starttime=t + 1) # doctest: +ELLIPSIS
<...Trace object at 0x...>
>>> st.append(tr) # doctest: +ELLIPSIS
<...Stream object at 0x...>
>>> st.get_gaps()[0] # doctest: +SKIP
[['BW', 'RJOB', '', 'EHZ', UTCDateTime(2009, 8, 24, 0, 20, 13),
UTCDateTime(2009, 8, 24, 0, 20, 14), 1.0, 99]]
>>> st.print_gaps() # doctest: +ELLIPSIS
Source Last Sample ...
BW.RJOB..EHZ 2009-08-24T00:20:13.000000Z ...
Total: 1 gap(s) and 0 overlap(s)
"""
# Create shallow copy of the traces to be able to sort them later on.
copied_traces = copy.copy(self.traces)
self.sort()
gap_list = []
for _i in range(len(self.traces)):
# if the trace is masked, break it up and run get_gaps on the
# resulting stream
if isinstance(self.traces[_i].data, np.ma.masked_array):
gap_list.extend(self.traces[_i].split().get_gaps())
if _i + 1 == len(self.traces):
# reached the last trace
break
# skip traces with different network, station, location or channel
if self.traces[_i].id != self.traces[_i + 1].id:
continue
# different sampling rates should always result in a gap or overlap
if self.traces[_i].stats.delta == self.traces[_i + 1].stats.delta:
same_sampling_rate = True
else:
same_sampling_rate = False
stats = self.traces[_i].stats
stime = min(stats['endtime'], self.traces[_i + 1].stats['endtime'])
etime = self.traces[_i + 1].stats['starttime']
# last sample of earlier trace represents data up to time of last
# sample (stats.endtime) plus one delta
delta = etime.timestamp - (stime.timestamp + stats.delta)
# Check that any overlap is not larger than the trace coverage
if delta < 0:
temp = self.traces[_i + 1].stats['endtime'].timestamp - \
etime.timestamp
if (delta * -1) > temp:
delta = -1 * temp
# Check gap/overlap criteria
if min_gap and delta < min_gap:
continue
if max_gap and delta > max_gap:
continue
# Number of missing samples
nsamples = int(compatibility.round_away(math.fabs(delta) *
stats['sampling_rate']))
if delta < 0:
nsamples = -nsamples
# skip if is equal to delta (1 / sampling rate)
if same_sampling_rate and nsamples == 0:
continue
# check if gap is already covered in trace before:
covered = False
# only need to check previous traces because the traces are sorted
for prev_trace in self.traces[:_i]:
prev_stats = prev_trace.stats
# look if trace is contained in other trace
prev_start = prev_stats['starttime']
prev_end = prev_stats['endtime']
if not (prev_start < stime < etime < prev_end):
continue
# don't look in traces of other measurements
elif self.traces[_i].id != prev_trace.id:
continue
else:
covered = True
break
if covered:
continue
gap_list.append([stats['network'], stats['station'],
stats['location'], stats['channel'],
stime, etime, delta, nsamples])
# Set the original traces to not alter the stream object.
self.traces = copied_traces
return gap_list
def insert(self, position, object):
"""
Insert either a single Trace or a list of Traces before index.
:param position: The Trace will be inserted at position.
:param object: Single Trace object or list of Trace objects.
"""
if isinstance(object, Trace):
self.traces.insert(position, object)
elif isinstance(object, list):
# Make sure each item in the list is a trace.
for _i in object:
if not isinstance(_i, Trace):
msg = 'Trace object or a list of Trace objects expected!'
raise TypeError(msg)
# Insert each item of the list.
for _i in range(len(object)):
self.traces.insert(position + _i, object[_i])
elif isinstance(object, Stream):
self.insert(position, object.traces)
else:
msg = 'Only accepts a Trace object or a list of Trace objects.'
raise TypeError(msg)
return self
def plot(self, *args, **kwargs):
"""
Create a waveform plot of the current ObsPy Stream object.
:param outfile: Output file string. Also used to automatically
determine the output format. Supported file formats depend on your
matplotlib backend. Most backends support png, pdf, ps, eps and
svg. Defaults to ``None``.
:param format: Format of the graph picture. If no format is given the
outfile parameter will be used to try to automatically determine
the output format. If no format is found it defaults to png output.
If no outfile is specified but a format is, than a binary
imagestring will be returned.
Defaults to ``None``.
:param starttime: Start time of the graph as a
:class:`~obspy.core.utcdatetime.UTCDateTime` object. If not set
the graph will be plotted from the beginning.
Defaults to ``None``.
:param endtime: End time of the graph as a
:class:`~obspy.core.utcdatetime.UTCDateTime` object. If not set
the graph will be plotted until the end.
Defaults to ``None``.
:param fig: Use an existing matplotlib figure instance.
Defaults to ``None``.
:param automerge: If automerge is True, Traces with the same id will be
merged.
Defaults to ``True``.
:param size: Size tuple in pixel for the output file. This corresponds
to the resolution of the graph for vector formats.
Defaults to ``(800, 250)`` pixel per channel for ``type='normal'``
or ``type='relative'``, ``(800, 600)`` for ``type='dayplot'``, and
``(1000, 600)`` for ``type='section'``.
:param dpi: Dots per inch of the output file. This also affects the
size of most elements in the graph (text, linewidth, ...).
Defaults to ``100``.
:param color: Color of the graph as a matplotlib color string as
described below. If ``type='dayplot'`` a list/tuple of color
strings is expected that will be periodically repeated for each
line plotted. If ``type='section'`` then the values ``'network'``,
``'station'`` or ``'channel'`` are also accepted, and traces will
be uniquely colored by the given information.
Defaults to ``'black'`` or to ``('#B2000F', '#004C12', '#847200',
'#0E01FF')`` for ``type='dayplot'``.
:param bgcolor: Background color of the graph.
Defaults to ``'white'``.
:param face_color: Face color of the matplotlib canvas.
Defaults to ``'white'``.
:param transparent: Make all backgrounds transparent (True/False). This
will override the ``bgcolor`` and ``face_color`` arguments.
Defaults to ``False``.
:param tick_format: The way the time axis is formatted.
Defaults to ``'%H:%M:%S'`` or ``'%.2f'`` if ``type='relative'``.
:param tick_rotation: Tick rotation in degrees.
Defaults to ``0``.
:param handle: Whether or not to return the matplotlib figure instance
after the plot has been created.
Defaults to ``False``.
:param method: By default, all traces with more than 400,000 samples
will be plotted with a fast method that cannot be zoomed.
Setting this argument to ``'full'`` will straight up plot the data.
This results in a potentially worse performance but the interactive
matplotlib view can be used properly.
Defaults to 'fast'.
:param type: Type may be set to either: ``'normal'`` to produce the
standard plot; ``'dayplot'`` to create a one-day plot for a single
Trace; ``'relative'`` to convert all date/time information to a
relative scale starting the seismogram at 0 seconds; ``'section'``
to plot all seismograms in a single coordinate system shifted
according to their distance from a reference point. Defaults to
``'normal'``.
:param equal_scale: If enabled all plots are equally scaled.
Defaults to ``True``.
:param show: If True, show the plot interactively after plotting. This
is ignored if any of ``outfile``, ``format``, ``handle``, or
``fig`` are specified.
Defaults to ``True``.
:param draw: If True, the figure canvas is explicitly re-drawn, which
ensures that *existing* figures are fresh. It makes no difference
for figures that are not yet visible.
Defaults to ``True``.
:param block: If True block call to showing plot. Only works if the
active matplotlib backend supports it.
Defaults to ``True``.
:param linewidth: Float value in points of the line width.
Defaults to ``1.0``.
:param linestyle: Line style.
Defaults to ``'-'``
:param grid_color: Color of the grid.
Defaults to ``'black'``.
:param grid_linewidth: Float value in points of the grid line width.
Defaults to ``0.5``.
:param grid_linestyle: Grid line style.
Defaults to ``':'``
**Dayplot Parameters**
The following parameters are only available if ``type='dayplot'`` is
set.
:param vertical_scaling_range: Determines how each line is scaled in
its given space. Every line will be centered around its mean value
and then clamped to fit its given space. This argument is the range
in data units that will be used to clamp the data. If the range is
smaller than the actual range, the lines' data may overshoot to
other lines which is usually a desired effect. Larger ranges will
result in a vertical padding.
If ``0``, the actual range of the data will be used and no
overshooting or additional padding will occur.
If ``None`` the range will be chosen to be the 99.5-percentile of
the actual range - so some values will overshoot.
Defaults to ``None``.
:param interval: This defines the interval length in minutes for one
line.
Defaults to ``15``.
:param time_offset: Only used if ``type='dayplot'``. The difference
between the timezone of the data (specified with the kwarg
``timezone``) and UTC time in hours. Will be displayed in a string.
Defaults to the current offset of the system time to UTC time.
:param timezone: Defines the name of the user defined time scale. Will
be displayed in a string together with the actual offset defined in
the kwarg ``time_offset``.
Defaults to ``'local time'``.
:param localization_dict: Enables limited localization of the dayplot
through the usage of a dictionary. To change the labels to, e.g.
German, use the following::
localization_dict={'time in': 'Zeit in', 'seconds': 'Sekunden',
'minutes': 'Minuten', 'hours': 'Stunden'}