Skip to content

Commit 7b36ed0

Browse files
committed
Add interface for access to all traceheaders
1 parent 6939da9 commit 7b36ed0

4 files changed

Lines changed: 439 additions & 3 deletions

File tree

python/docs/segyio.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ Trace header and attributes
3535
.. autoclass:: segyio.trace.Attributes()
3636
:special-members: __getitem__, __setitem__, __len__, __contains__, __iter__
3737

38+
.. autoclass:: segyio.trace.FileFieldAccessor()
39+
:special-members: __getitem__, __setitem__, __len__, __iter__
40+
41+
.. autoclass:: segyio.trace.RowFieldAccessor()
42+
:special-members: __getitem__, __setitem__, __getattr__, __setattr__, __len__, __iter__
43+
3844
Data line
3945
---------
4046
.. autoclass:: segyio.line.Line()

python/segyio/segy.py

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from .gather import Gather, Groups
1010
from .line import Line
1111
from .trace import Trace, Header, Attributes, Text, Stanza
12-
from .trace import RowLayoutEntries
12+
from .trace import RowLayoutEntries, FileFieldAccessor
1313
from .field import Field
1414

1515
from .tracesortingformat import TraceSortingFormat
@@ -93,6 +93,7 @@ def __init__(
9393
self.readonly,
9494
)
9595
self._header = Header(self)
96+
self._traceheader = FileFieldAccessor(self)
9697
self._iline = None
9798
self._xline = None
9899
self._gather = None
@@ -422,7 +423,7 @@ def tracefield(self):
422423
@property
423424
def header(self):
424425
"""
425-
Interact with segy in header mode
426+
Interact with segy in standard header mode
426427
427428
Returns
428429
-------
@@ -471,6 +472,55 @@ def header(self, val):
471472
"""
472473
self.header[:] = val
473474

475+
@property
476+
def traceheader(self):
477+
"""
478+
Interact with segy in traceheader mode.
479+
480+
Works similar to :meth:`.header` but is applicable for all trace
481+
headers, not just standard ones.
482+
483+
Examples
484+
--------
485+
Read field values from trace header extension 1 at trace 5:
486+
487+
>>> traceheader = f.traceheader[5][1]
488+
>>> traceheader.rec_x
489+
... 100.5
490+
>>> traceheader.rec_y
491+
... 150.75
492+
493+
Notes
494+
-----
495+
.. versionadded:: 2.0
496+
"""
497+
return self._traceheader
498+
499+
@traceheader.setter
500+
def traceheader(self, val):
501+
"""headers macro assignment
502+
503+
Operating on all headers of a file.
504+
505+
If the right-hand-side headers are exhausted before all the destination
506+
file headers the behavior is undefined and may change in the future.
507+
508+
Examples
509+
--------
510+
Copy all headers from file g to file f:
511+
512+
>>> f.traceheader = g.traceheader
513+
514+
Copy all headers from trace 3 to trace 5:
515+
516+
>>> f.traceheader[5] = f.traceheader[3]
517+
518+
Copy standard header from trace 2 to trace 4:
519+
520+
>>> f.traceheader[4][0] = f.traceheader[2][0]
521+
"""
522+
self.traceheader[:] = val
523+
474524
def attributes(self, field):
475525
"""File-wide attribute (standard header word) reading
476526

python/segyio/trace.py

Lines changed: 251 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import numpy as np
1414

1515
from .line import HeaderLine
16-
from .field import Field
16+
from .field import Field, HeaderFieldAccessor
1717
from .utils import castarray
1818

1919
class Sequence(Sequence):
@@ -773,6 +773,256 @@ def xline(self, value):
773773
for i, src in zip(self.segyfile.xlines, value):
774774
self.xline[i] = src
775775

776+
777+
class FileFieldAccessor(Sequence):
778+
"""
779+
Sequence of rows of trace headers in a SEG-Y file.
780+
781+
Provides access to all trace headers defined in the SEG-Y file.
782+
783+
Notes
784+
-----
785+
.. versionadded:: 2.0
786+
787+
"""
788+
789+
def __init__(self, segyfile):
790+
self.segyfile = segyfile
791+
super().__init__(segyfile.tracecount)
792+
793+
def __getitem__(self, i):
794+
"""traceheader[i]
795+
796+
All headers belonging to ith trace, starting at 0.
797+
798+
Parameters
799+
----------
800+
i : int or slice
801+
802+
Returns
803+
-------
804+
traceheaders : RowFieldAccessor
805+
806+
Examples
807+
--------
808+
Accessing headers in trace 10:
809+
810+
>>> traceheader[10]
811+
"""
812+
try:
813+
trace_index = self.wrapindex(i)
814+
return RowFieldAccessor(segyfile=self.segyfile, trace_index=trace_index)
815+
816+
except TypeError:
817+
try:
818+
trace_indices = i.indices(len(self))
819+
except AttributeError:
820+
msg = 'trace indices must be integers or slices, not {}'
821+
raise TypeError(msg.format(type(i).__name__))
822+
823+
def gen():
824+
for trace_index in range(*trace_indices):
825+
yield RowFieldAccessor(segyfile=self.segyfile, trace_index=trace_index)
826+
827+
return gen()
828+
829+
def __setitem__(self, i, val):
830+
"""traceheader[i] = val
831+
832+
Write the ith header of the file, starting at 0.
833+
834+
Parameters
835+
----------
836+
i : int or slice
837+
val : another FileFieldAccessor or array like
838+
839+
Examples
840+
--------
841+
Copy all trace's headers to a different trace:
842+
843+
>>> traceheader[28] = traceheader[29]
844+
"""
845+
if not isinstance(i, slice):
846+
trace_index = self.wrapindex(i)
847+
if not isinstance(val, RowFieldAccessor):
848+
raise TypeError(
849+
"Unassignable type. f.traceheader[i] can only be assigned f.traceheader[j]")
850+
self[trace_index][:] = val
851+
return
852+
853+
trace_indices = range(*i.indices(len(self)))
854+
for trace_index, value in zip(trace_indices, val):
855+
self[trace_index] = value
856+
857+
858+
class RowFieldAccessor(Sequence):
859+
"""
860+
Sequence of trace headers in a single trace (row).
861+
862+
Provides access to all trace headers defined in the trace.
863+
864+
Notes
865+
-----
866+
.. versionadded:: 2.0
867+
868+
"""
869+
870+
def __init__(self, segyfile, trace_index):
871+
self.segyfile = segyfile
872+
self.trace_index = trace_index
873+
super().__init__(segyfile.traceheader_count)
874+
875+
def __getitem__(self, i):
876+
"""traceheader[i]
877+
878+
ith header of the trace, starting at 0.
879+
880+
Note that this function loads trace header into memory, so if you wish
881+
to access multiple fields, caching the result of this function is more
882+
efficient than calling it anew with each of the fields.
883+
884+
Parameters
885+
----------
886+
i : int or slice
887+
888+
Returns
889+
-------
890+
field : Field
891+
dict_like header
892+
893+
Examples
894+
--------
895+
Reading a header:
896+
897+
>>> traceheader[10]
898+
899+
Read a field in the first 5 headers:
900+
901+
>>> [x[25] for x in traceheader[:5]]
902+
[1, 2, 3, 4]
903+
"""
904+
try:
905+
return HeaderFieldAccessor.trace(
906+
traceno=self.trace_index, traceheader_index=self.wrapindex(i), segyfile=self.segyfile
907+
)
908+
909+
except TypeError:
910+
try:
911+
traceheader_indices = i.indices(len(self))
912+
except AttributeError:
913+
msg = 'trace header indices must be integers or slices, not {}'
914+
raise TypeError(msg.format(type(i).__name__))
915+
916+
def gen():
917+
# see Header.__getitem__ for logic explanation
918+
x = HeaderFieldAccessor.trace(
919+
traceno=self.trace_index, traceheader_index=None, segyfile=self.segyfile
920+
)
921+
buf = bytearray(x.buf)
922+
for traceheader_index in range(*traceheader_indices):
923+
buf = x.fetch(buf, self.trace_index, traceheader_index)
924+
x.buf[:] = buf
925+
x.traceheader_index = traceheader_index
926+
traceheader_layout = x.segyfile.tracefield[traceheader_index]
927+
x._keys = [field.offset() for field in traceheader_layout]
928+
yield x
929+
930+
return gen()
931+
932+
def __getattr__(self, name):
933+
"""traceheaders.name
934+
935+
Header of the trace by name `name`. `name` must be a name defined by
936+
the file layouts.
937+
938+
Wrapper around :meth:`.__getitem__`, so refer to it for more
939+
information.
940+
941+
Parameters
942+
----------
943+
name : str
944+
945+
Returns
946+
-------
947+
field : Field
948+
dict_like header
949+
950+
Examples
951+
--------
952+
Reading a header:
953+
954+
>>> traceheaders.SEG00001
955+
"""
956+
index = self.segyfile._traceheader_names.index(name)
957+
return self[index]
958+
959+
def __setitem__(self, i, val):
960+
"""traceheader[i] = val
961+
962+
Write the ith header of the trace, starting at 0.
963+
964+
Parameters
965+
----------
966+
i : int or slice
967+
val : Field or array_like of dict_like
968+
969+
Examples
970+
--------
971+
Copy standard header to a different trace:
972+
973+
>>> f.traceheader[0][0] = f.traceheader[1][0]
974+
975+
Writing fields via `traceheader[trace_index][traceheader_index] =
976+
{(offset: value)}` is not supported. Use :class:`segyio.field.Field`
977+
interface instead:
978+
979+
>>> f.traceheader[0][0].update({ 37: 5, 1: 2484 })
980+
981+
"""
982+
if not isinstance(i, slice):
983+
traceheader_index = self.wrapindex(i)
984+
field_sequence = self[traceheader_index]
985+
if isinstance(val, Field):
986+
field_sequence.update(val)
987+
return
988+
raise TypeError(
989+
"Unassignable type. f.traceheader[i][j] can only be assigned another Field.")
990+
991+
traceheader_indices = range(*i.indices(len(self)))
992+
993+
for traceheader_index, value in zip(traceheader_indices, val):
994+
self[traceheader_index] = value
995+
996+
def __setattr__(self, name, value):
997+
"""traceheaders.name = value
998+
999+
Write header of the trace by name `name`. `name` must be a name defined by
1000+
the file layouts.
1001+
1002+
Wrapper around :meth:`.__setitem__`, so refer to it for more
1003+
information.
1004+
1005+
Parameters
1006+
----------
1007+
name : str
1008+
1009+
Examples
1010+
--------
1011+
Copying a header:
1012+
1013+
>>> f.traceheader[0].SEG00001 = f.traceheader[1].SEG00001
1014+
"""
1015+
if name == "segyfile":
1016+
super().__setattr__(name, value)
1017+
return
1018+
1019+
try:
1020+
index = self.segyfile._traceheader_names.index(name)
1021+
self[index] = value
1022+
except ValueError:
1023+
super().__setattr__(name, value)
1024+
1025+
7761026
class Attributes(Sequence):
7771027
"""File-wide attribute (header word) reading
7781028

0 commit comments

Comments
 (0)