mirror of
https://github.com/wassname/segpy.git
synced 2026-06-27 18:44:26 +08:00
Mostly PEP8 cleanups in the course of reading the code.
I took the liberty of adding a few specific exception types, some documentation, and things like that, but this is mostly a cosmetic chance.
This commit is contained in:
+95
-35
@@ -29,23 +29,28 @@ class CatalogBuilder:
|
||||
def add(self, index, value):
|
||||
"""Add an item.
|
||||
|
||||
Each index must be unique if create() is to be subsequently called successfully,
|
||||
although duplicate index values will be accepted by this call without complaint.
|
||||
Each index must be unique if create() is to be subsequently
|
||||
called successfully, although duplicate index values will be
|
||||
accepted by this call without complaint.
|
||||
|
||||
"""
|
||||
self._catalog.append((index, value))
|
||||
|
||||
def create(self):
|
||||
"""Create a possibly more optimized representation of the mapping.
|
||||
|
||||
In this worst case, this method returns an object which is essentially an immutable dictionary. In the best
|
||||
case, the space savings can be vast.
|
||||
In this worst case, this method returns an object which is
|
||||
essentially an immutable dictionary. In the best case, the
|
||||
space savings can be vast.
|
||||
|
||||
Returns:
|
||||
A mapping, if a unique mapping from indexes to values is possible, otherwise None.
|
||||
A mapping, if a unique mapping from indexes to values is
|
||||
possible, otherwise None.
|
||||
|
||||
"""
|
||||
|
||||
# This method examines the contents of the mapping using various heuristics to come up with
|
||||
# a better representation.
|
||||
# This method examines the contents of the mapping using
|
||||
# various heuristics to come up with a better representation.
|
||||
|
||||
if len(self._catalog) < 2:
|
||||
return DictionaryCatalog(self._catalog)
|
||||
@@ -56,7 +61,8 @@ class CatalogBuilder:
|
||||
if contains_duplicates(index for index, value in self._catalog):
|
||||
return None
|
||||
|
||||
if all(isinstance(index, Sequence) and (len(index) == 2) for index, value in self._catalog):
|
||||
if all(isinstance(index, Sequence) and (len(index) == 2)
|
||||
for index, value in self._catalog):
|
||||
return self._create_catalog_2()
|
||||
|
||||
return self._create_catalog_1()
|
||||
@@ -79,10 +85,18 @@ class CatalogBuilder:
|
||||
|
||||
if index_stride is not None and value_stride is None:
|
||||
# Regular index - regular keys and arbitrary values
|
||||
return RegularCatalog(index_min, index_max, index_stride, (value for index, value in self._catalog))
|
||||
return RegularCatalog(index_min,
|
||||
index_max,
|
||||
index_stride,
|
||||
(value for index, value in self._catalog))
|
||||
|
||||
assert (index_stride is not None) and (value_stride is not None)
|
||||
catalog = LinearRegularCatalog(index_min, index_max, index_stride, value_min, value_max, value_stride)
|
||||
catalog = LinearRegularCatalog(index_min,
|
||||
index_max,
|
||||
index_stride,
|
||||
value_min,
|
||||
value_max,
|
||||
value_stride)
|
||||
return catalog
|
||||
|
||||
def _create_catalog_2(self):
|
||||
@@ -128,8 +142,9 @@ class CatalogBuilder:
|
||||
class RowMajorCatalog(Mapping):
|
||||
"""A mapping which assumes a row-major ordering of a two-dimensional matrix.
|
||||
|
||||
This is the ordering of items in a two-dimensional matrix where in the (i, j)
|
||||
key tuple the j value changes fastest when iterating through the items in order.
|
||||
This is the ordering of items in a two-dimensional matrix where in
|
||||
the (i, j) key tuple the j value changes fastest when iterating
|
||||
through the items in order.
|
||||
|
||||
A RowMajorCatalog predicts the value v from the key (i, j) according to the
|
||||
following formula:
|
||||
@@ -189,14 +204,16 @@ class RowMajorCatalog(Mapping):
|
||||
|
||||
def __getitem__(self, key):
|
||||
i, j = key
|
||||
if not (self._i_min <= i <= self._i_max) and (self._j_min <= j <= self._j_max):
|
||||
if not (self._i_min <= i <= self._i_max) and \
|
||||
(self._j_min <= j <= self._j_max):
|
||||
raise KeyError("{!r} key {!r} out of range".format(self, key))
|
||||
value = (i - self._i_min) * self._j_max + (j - self._j_min) + self._c
|
||||
return value
|
||||
|
||||
def __contains__(self, key):
|
||||
i, j = key
|
||||
return (self._i_min <= i <= self._i_max) and (self._j_min <= j <= self._j_max)
|
||||
return (self._i_min <= i <= self._i_max) and \
|
||||
(self._j_min <= j <= self._j_max)
|
||||
|
||||
def __len__(self):
|
||||
return (self._i_max - self._i_min) * (self._j_max - self._j_min)
|
||||
@@ -207,8 +224,9 @@ class RowMajorCatalog(Mapping):
|
||||
yield (i, j)
|
||||
|
||||
def __repr__(self):
|
||||
return '{}({}, {}, {}, {}, {})'.format(self.__class__.__name__,
|
||||
self._i_min, self._i_max, self._j_min, self._j_max, self._c)
|
||||
return '{}({}, {}, {}, {}, {})'.format(
|
||||
self.__class__.__name__,
|
||||
self._i_min, self._i_max, self._j_min, self._j_max, self._c)
|
||||
|
||||
|
||||
class DictionaryCatalog(Mapping):
|
||||
@@ -231,7 +249,8 @@ class DictionaryCatalog(Mapping):
|
||||
return item in self._items
|
||||
|
||||
def __repr__(self):
|
||||
return '{}({})'.format(self.__class__.__name__, repr.repr(self._items.items()))
|
||||
return '{}({})'.format(
|
||||
self.__class__.__name__, repr.repr(self._items.items()))
|
||||
|
||||
|
||||
class RegularCatalog(Mapping):
|
||||
@@ -251,10 +270,16 @@ class RegularCatalog(Mapping):
|
||||
key_max: The maximum key.
|
||||
key_stride: The difference between successive keys.
|
||||
values: An iterable series of values corresponding to the keys.
|
||||
|
||||
Raises:
|
||||
ValueError: There is any inconsistency in the keys, stride,
|
||||
and/or values.
|
||||
"""
|
||||
key_range = key_max - key_min
|
||||
if key_range % key_stride != 0:
|
||||
raise ("RegularIndex key range {!r} is not a multiple of stride {!r}".format(key_stride, key_range))
|
||||
raise ValueError("RegularIndex key range {!r} is not "
|
||||
"a multiple of stride {!r}".format(
|
||||
key_stride, key_range))
|
||||
self._key_min = key_min
|
||||
self._key_max = key_max
|
||||
self._key_stride = key_stride
|
||||
@@ -276,21 +301,29 @@ class RegularCatalog(Mapping):
|
||||
return len(self._values)
|
||||
|
||||
def __contains__(self, key):
|
||||
return (self._key_min <= key <= self._key_max) and ((key - self._key_min) % self._key_stride == 0)
|
||||
return (self._key_min <= key <= self._key_max) and \
|
||||
((key - self._key_min) % self._key_stride == 0)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(range(self._key_min, self._key_max + 1, self._index_stride))
|
||||
return iter(range(self._key_min,
|
||||
self._key_max + 1,
|
||||
self._index_stride))
|
||||
|
||||
def __repr__(self):
|
||||
return '{}({}, {}, {}, {})'.format(self.__class__.__name, self._key_min, self._key_max, self._key_stride,
|
||||
repr.repr(self._values))
|
||||
return '{}({}, {}, {}, {})'.format(
|
||||
self.__class__.__name,
|
||||
self._key_min,
|
||||
self._key_max,
|
||||
self._key_stride,
|
||||
repr.repr(self._values))
|
||||
|
||||
|
||||
class LinearRegularCatalog(Mapping):
|
||||
"""A mapping which assumes a linear relationship between keys and values.
|
||||
|
||||
This is the ordering of items in a two-dimensional matrix where in the (i, j)
|
||||
key tuple the j value changes fastest when iterating through the items in order.
|
||||
This is the ordering of items in a two-dimensional matrix where in
|
||||
the (i, j) key tuple the j value changes fastest when iterating
|
||||
through the items in order.
|
||||
|
||||
A LinearRegularCatalog predicts the value v from the key according to the
|
||||
following formula:
|
||||
@@ -298,7 +331,13 @@ class LinearRegularCatalog(Mapping):
|
||||
v = (value_max - value_min) / (key_max - key_min) * (key - key_min) + value_min
|
||||
"""
|
||||
|
||||
def __init__(self, key_min, key_max, key_stride, value_min, value_max, value_stride):
|
||||
def __init__(self,
|
||||
key_min,
|
||||
key_max,
|
||||
key_stride,
|
||||
value_min,
|
||||
value_max,
|
||||
value_stride):
|
||||
"""Initialize a LinearRegularCatalog.
|
||||
|
||||
Args:
|
||||
@@ -307,19 +346,29 @@ class LinearRegularCatalog(Mapping):
|
||||
key_stride: The difference between successive keys.
|
||||
value_min: The minimum value.
|
||||
value_max: The maximum value.
|
||||
|
||||
Raises:
|
||||
ValueError: There is any inconsistency in the keys, strides,
|
||||
and/or values.
|
||||
"""
|
||||
key_range = key_max - key_min
|
||||
if key_range % key_stride != 0:
|
||||
raise ("{} key range {!r} is not a multiple of key stride {!r}".format(
|
||||
self.__class__.__name__, key_stride, key_range))
|
||||
raise ValueError("{} key range {!r} is not "
|
||||
"a multiple of key stride {!r}".format(
|
||||
self.__class__.__name__,
|
||||
key_stride,
|
||||
key_range))
|
||||
self._key_min = key_min
|
||||
self._key_max = key_max
|
||||
self._key_stride = key_stride
|
||||
|
||||
value_range = value_max - value_min
|
||||
if value_range % value_stride != 0:
|
||||
raise ("{} value range {!r} is not a multiple of value stride {!r}".format(
|
||||
self.__class__.__name__, value_stride, value_range))
|
||||
raise ValueError("{} value range {!r} is not "
|
||||
"a multiple of value stride {!r}".format(
|
||||
self.__class__.__name__,
|
||||
value_stride,
|
||||
value_range))
|
||||
self._value_min = value_min
|
||||
self._value_max = value_max
|
||||
self._value_stride = value_stride
|
||||
@@ -327,10 +376,14 @@ class LinearRegularCatalog(Mapping):
|
||||
num_keys = (self._key_max - self._key_min) // self._key_stride
|
||||
num_values = (self._value_max - self._value_min) // self._value_stride
|
||||
if num_keys != num_values:
|
||||
raise ("{} inconsistent number of keys {} and values {}".format(
|
||||
self.__class__.__name__, num_keys, num_values))
|
||||
raise ValueError("{} inconsistent number of "
|
||||
"keys {} and values {}".format(
|
||||
self.__class__.__name__,
|
||||
num_keys,
|
||||
num_values))
|
||||
|
||||
self._m = Fraction(self._value_max - self._value_min, self._key_max - self._key_min)
|
||||
self._m = Fraction(self._value_max - self._value_min,
|
||||
self._key_max - self._key_min)
|
||||
|
||||
def __getitem__(self, key):
|
||||
if not (self._key_min <= key <= self._key_max):
|
||||
@@ -347,11 +400,18 @@ class LinearRegularCatalog(Mapping):
|
||||
return 1 + (self._key_max - self._key_min) // self._key_stride
|
||||
|
||||
def __contains__(self, key):
|
||||
return (self._key_min <= key <= self._key_max) and ((key - self._key_min) % self._key_stride == 0)
|
||||
return (self._key_min <= key <= self._key_max) and \
|
||||
((key - self._key_min) % self._key_stride == 0)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(range(self._key_min, self._key_max + 1, self._key_stride))
|
||||
|
||||
def __repr__(self):
|
||||
return '{}({}, {}, {}, {}, {}, {})'.format(self.__class__.__name__,
|
||||
self._key_min, self._key_max, self._key_stride, self._value_min, self._value_max, self._value_stride)
|
||||
return '{}({}, {}, {}, {}, {}, {})'.format(
|
||||
self.__class__.__name__,
|
||||
self._key_min,
|
||||
self._key_max,
|
||||
self._key_stride,
|
||||
self._value_min,
|
||||
self._value_max,
|
||||
self._value_stride)
|
||||
|
||||
@@ -3,31 +3,50 @@ from __future__ import print_function
|
||||
from portability import seekable
|
||||
from util import file_length, filename_from_handle
|
||||
from datatypes import DATA_SAMPLE_FORMAT, CTYPE_DESCRIPTION, CTYPES
|
||||
from toolkit import (extract_revision, bytes_per_sample, read_reel_header, catalog_traces, read_binary_values,
|
||||
compile_trace_header_format, TraceHeader, REEL_HEADER_NUM_BYTES, TRACE_HEADER_NUM_BYTES)
|
||||
from toolkit import (extract_revision,
|
||||
bytes_per_sample,
|
||||
read_reel_header,
|
||||
catalog_traces,
|
||||
read_binary_values,
|
||||
compile_trace_header_format,
|
||||
TraceHeader,
|
||||
REEL_HEADER_NUM_BYTES,
|
||||
TRACE_HEADER_NUM_BYTES)
|
||||
|
||||
|
||||
def create_reader(fh, endian='>'):
|
||||
"""Create a SegYReader (or one of its subclasses) based on performing a scan of SEG Y data.
|
||||
"""Create a SegYReader (or one of its subclasses) based on performing
|
||||
a scan of SEG Y data.
|
||||
|
||||
This function is the preferred method for creating SegYReader objects. It reads basic header
|
||||
information and attempts to build indexes for traces, CDP numbers (for 2D surveys), and inline
|
||||
and cross line co-ordinates (for 3D surveys) to facilitate subsequent random-access to traces.
|
||||
This function is the preferred method for creating SegYReader
|
||||
objects. It reads basic header information and attempts to build
|
||||
indexes for traces, CDP numbers (for 2D surveys), and inline and
|
||||
cross line co-ordinates (for 3D surveys) to facilitate subsequent
|
||||
random-access to traces.
|
||||
|
||||
Args:
|
||||
fh: A file-like-object open in binary mode positioned such that the
|
||||
beginning of the reel header will be the next byte to be read. For disk-based
|
||||
SEG Y files, this is the beginning of the file.
|
||||
fh: A file-like-object open in binary mode positioned such
|
||||
that the beginning of the reel header will be the next
|
||||
byte to be read. For disk-based SEG Y files, this is the
|
||||
beginning of the file.
|
||||
|
||||
endian: '>' for big-endian data (the standard and default), '<' for
|
||||
little-endian (non-standard)
|
||||
endian: '>' for big-endian data (the standard and default), '<'
|
||||
for little-endian (non-standard)
|
||||
|
||||
Raises:
|
||||
ValueError: ``fh`` is unsuitable for some reason, such as not
|
||||
being open, not being seekable, not being in
|
||||
binary mode, or being too short.
|
||||
|
||||
Returns:
|
||||
A SegYReader object. Depending on the exact type of the SegYReader returned different capabilities may be
|
||||
available. Inspect the returned object to determine these capabilities, or be prepared for capabilities not
|
||||
defined in the SegYReader base class to be unavailable. The underlying file-like object must remain open
|
||||
for the duration of use of the returned reader object. It is the caller's responsibility to close the
|
||||
underlying file.
|
||||
A SegYReader object. Depending on the exact type of the
|
||||
SegYReader returned different capabilities may be
|
||||
available. Inspect the returned object to determine these
|
||||
capabilities, or be prepared for capabilities not defined in
|
||||
the SegYReader base class to be unavailable. The underlying
|
||||
file-like object must remain open for the duration of use of
|
||||
the returned reader object. It is the caller's responsibility
|
||||
to close the underlying file.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -37,18 +56,23 @@ def create_reader(fh, endian='>'):
|
||||
|
||||
"""
|
||||
if fh.encoding is not None:
|
||||
raise TypeError("SegYReader must be provided with a binary mode file object")
|
||||
raise TypeError(
|
||||
"SegYReader must be provided with a binary mode file object")
|
||||
|
||||
if not seekable(fh):
|
||||
raise TypeError("SegYReader must be provided with a seekable file object")
|
||||
raise TypeError(
|
||||
"SegYReader must be provided with a seekable file object")
|
||||
|
||||
if fh.closed:
|
||||
raise ValueError("SegYReader must be provided with an open file object")
|
||||
raise ValueError(
|
||||
"SegYReader must be provided with an open file object")
|
||||
|
||||
num_file_bytes = file_length(fh)
|
||||
if num_file_bytes < REEL_HEADER_NUM_BYTES:
|
||||
raise ValueError("SEG Y file {!r} of {} bytes is too short".format(filename_from_handle(fh),
|
||||
num_file_bytes))
|
||||
raise ValueError(
|
||||
"SEG Y file {!r} of {} bytes is too short".format(
|
||||
filename_from_handle(fh),
|
||||
num_file_bytes))
|
||||
if endian not in ('<', '>'):
|
||||
raise ValueError("Unrecognised endian value {!r}".format(endian))
|
||||
|
||||
@@ -59,37 +83,43 @@ def create_reader(fh, endian='>'):
|
||||
trace_catalog, cdp_catalog, line_catalog = catalog_traces(fh, bps, endian)
|
||||
|
||||
if cdp_catalog is not None and line_catalog is None:
|
||||
return SegYReader2D(fh, reel_header, trace_catalog, cdp_catalog, endian)
|
||||
return SegYReader2D(fh, reel_header, trace_catalog,
|
||||
cdp_catalog, endian)
|
||||
|
||||
if cdp_catalog is None and line_catalog is not None:
|
||||
return SegYReader3D(fh, reel_header, trace_catalog, line_catalog, endian)
|
||||
return SegYReader3D(fh, reel_header, trace_catalog,
|
||||
line_catalog, endian)
|
||||
|
||||
return SegYReader(fh, reel_header, trace_catalog, endian)
|
||||
|
||||
|
||||
class SegYReader(object):
|
||||
|
||||
"""A basic SEG Y reader.
|
||||
|
||||
Use to obtain read the reel header, the trace headers or trace values. Traces can be accessed
|
||||
only by trace index.
|
||||
Use to obtain read the reel header, the trace headers or trace
|
||||
values. Traces can be accessed only by trace index.
|
||||
"""
|
||||
|
||||
def __init__(self, fh, reel_header, trace_catalog, endian='>'):
|
||||
"""Initialize a SegYReader around a file-like-object.
|
||||
|
||||
Note:
|
||||
Usually a SegYReader is most easily constructed using the create_reader() function.
|
||||
Usually a SegYReader is most easily constructed using the
|
||||
create_reader() function.
|
||||
|
||||
Args:
|
||||
fh: A file-like object, which must support seeking and support binary reading.
|
||||
fh: A file-like object, which must support seeking and
|
||||
support binary reading.
|
||||
|
||||
reel_header: A dictionary containing reel header data.
|
||||
|
||||
trace_catalog: A mapping from zero-based trace index to the byte-offset to
|
||||
individual traces within the file.
|
||||
trace_catalog: A mapping from zero-based trace index to
|
||||
the byte-offset to individual traces within the file.
|
||||
|
||||
endian: '>' for big-endian data (the standard and default), '<' for
|
||||
little-endian (non-standard)
|
||||
|
||||
"""
|
||||
self._fh = fh
|
||||
self._endian = endian
|
||||
@@ -99,13 +129,15 @@ class SegYReader(object):
|
||||
self._trace_catalog = trace_catalog
|
||||
|
||||
self._revision = extract_revision(self._reel_header)
|
||||
self._bytes_per_sample = bytes_per_sample(self._reel_header, self.revision)
|
||||
self._bytes_per_sample = bytes_per_sample(
|
||||
self._reel_header, self.revision)
|
||||
|
||||
def trace_indexes(self):
|
||||
"""An iterator over zero-based trace indexes.
|
||||
|
||||
Returns:
|
||||
An iterator which yields integers in the range zero to num_traces() - 1
|
||||
An iterator which yields integers in the range zero to
|
||||
num_traces() - 1
|
||||
"""
|
||||
return iter(self._trace_catalog)
|
||||
|
||||
@@ -120,8 +152,8 @@ class SegYReader(object):
|
||||
trace_index: An integer in the range zero to num_traces() - 1
|
||||
|
||||
Returns:
|
||||
A 2-tuple containing a TraceHeader as the first item and a sequence of numeric trace samples
|
||||
as the second item.
|
||||
A 2-tuple containing a TraceHeader as the first item and a
|
||||
sequence of numeric trace samples as the second item.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -134,7 +166,8 @@ class SegYReader(object):
|
||||
dsf = self._reel_header['DataSampleFormat']
|
||||
ctype = DATA_SAMPLE_FORMAT[dsf]
|
||||
pos = self._trace_catalog[trace_index] + TRACE_HEADER_NUM_BYTES
|
||||
trace_values = read_binary_values(self._fh, pos, ctype, num_samples, self._endian)
|
||||
trace_values = read_binary_values(
|
||||
self._fh, pos, ctype, num_samples, self._endian)
|
||||
return trace_header, trace_values
|
||||
|
||||
def read_trace_header(self, trace_index):
|
||||
@@ -155,7 +188,8 @@ class SegYReader(object):
|
||||
pos = self._trace_catalog[trace_index]
|
||||
self._fh.seek(pos)
|
||||
data = self._fh.read(TRACE_HEADER_NUM_BYTES)
|
||||
trace_header = TraceHeader._make(self._trace_header_format.unpack(data))
|
||||
trace_header = TraceHeader._make(
|
||||
self._trace_header_format.unpack(data))
|
||||
return trace_header
|
||||
|
||||
@property
|
||||
@@ -163,7 +197,9 @@ class SegYReader(object):
|
||||
"""The spatial dimensionality of the data.
|
||||
|
||||
Returns:
|
||||
3 for 3D seismic volumes, 2 for 2D seismic lines, 1 for a single trace, otherwise 0.
|
||||
3 for 3D seismic volumes, 2 for 2D seismic lines, 1 for a
|
||||
single trace, otherwise 0.
|
||||
|
||||
"""
|
||||
return self._dimensionality()
|
||||
|
||||
@@ -222,31 +258,40 @@ class SegYReader(object):
|
||||
class SegYReader3D(SegYReader):
|
||||
"""A reader for 3D seismic data.
|
||||
|
||||
In addition to the capabilities provided by the SegYReader base class, this reader
|
||||
provides an index to facilitate random access to individual traces via crossline and
|
||||
inline co-ordinates.
|
||||
In addition to the capabilities provided by the SegYReader base
|
||||
class, this reader provides an index to facilitate random access
|
||||
to individual traces via crossline and inline co-ordinates.
|
||||
"""
|
||||
|
||||
def __init__(self, fh, reel_header, trace_catalog, line_catalog, endian='>'):
|
||||
def __init__(self,
|
||||
fh,
|
||||
reel_header,
|
||||
trace_catalog,
|
||||
line_catalog,
|
||||
endian='>'):
|
||||
"""Initialize a SegYReader3D around a file-like-object.
|
||||
|
||||
Note:
|
||||
Usually a SegYReader is most easily constructed using the create_reader() function.
|
||||
Usually a SegYReader is most easily constructed using the
|
||||
create_reader() function.
|
||||
|
||||
Args:
|
||||
fh: A file-like object, which must support seeking and support binary reading.
|
||||
fh: A file-like object, which must support seeking and
|
||||
support binary reading.
|
||||
|
||||
reel_header: A dictionary containing reel header data.
|
||||
|
||||
trace_catalog: A mapping from zero-based trace indexes to the byte-offset to
|
||||
individual traces within the file.
|
||||
trace_catalog: A mapping from zero-based trace indexes to
|
||||
the byte-offset to individual traces within the file.
|
||||
|
||||
line_catalog: A mapping from (xline, inline) tuples to trace_indexes.
|
||||
line_catalog: A mapping from (xline, inline) tuples to
|
||||
trace_indexes.
|
||||
|
||||
endian: '>' for big-endian data (the standard and default), '<' for
|
||||
little-endian (non-standard)
|
||||
"""
|
||||
super(SegYReader3D, self).__init__(fh, reel_header, trace_catalog, endian)
|
||||
super(SegYReader3D, self).__init__(
|
||||
fh, reel_header, trace_catalog, endian)
|
||||
self._line_catalog = line_catalog
|
||||
|
||||
def _dimensionality(self):
|
||||
@@ -271,7 +316,8 @@ class SegYReader3D(SegYReader):
|
||||
return len(set(i for i, j in self._line_catalog))
|
||||
|
||||
def inline_xlines(self):
|
||||
"""An iterator over all (xline_number, inline_number) tuples corresponding to traces.
|
||||
"""An iterator over all (xline_number, inline_number) tuples
|
||||
corresponding to traces.
|
||||
"""
|
||||
return iter(self._line_catalog)
|
||||
|
||||
@@ -279,12 +325,14 @@ class SegYReader3D(SegYReader):
|
||||
"""Obtain the trace index given an xline and a inline.
|
||||
|
||||
Note:
|
||||
Do not assume that all combinations of crossline and inline co-ordinates are valid.
|
||||
The volume may not be rectangular. Valid values can be obtained from the
|
||||
Do not assume that all combinations of crossline and
|
||||
inline co-ordinates are valid. The volume may not be
|
||||
rectangular. Valid values can be obtained from the
|
||||
inline_xlines() iterator.
|
||||
|
||||
Furthermore, inline and crossline numbers should not be relied upon to be zero- or one-based
|
||||
indexes (although they may be).
|
||||
Furthermore, inline and crossline numbers should not be
|
||||
relied upon to be zero- or one-based indexes (although
|
||||
they may be).
|
||||
|
||||
Args:
|
||||
xline: A crossline number.
|
||||
@@ -298,26 +346,34 @@ class SegYReader3D(SegYReader):
|
||||
|
||||
class SegYReader2D(SegYReader):
|
||||
|
||||
def __init__(self, fh, reel_header, trace_catalog, cdp_catalog, endian='>'):
|
||||
def __init__(self,
|
||||
fh,
|
||||
reel_header,
|
||||
trace_catalog,
|
||||
cdp_catalog,
|
||||
endian='>'):
|
||||
"""Initialize a SegYReader2D around a file-like-object.
|
||||
|
||||
Note:
|
||||
Usually a SegYReader is most easily constructed using the create_reader() function.
|
||||
Usually a SegYReader is most easily constructed using the
|
||||
create_reader() function.
|
||||
|
||||
Args:
|
||||
fh: A file-like object, which must support seeking and support binary reading.
|
||||
fh: A file-like object, which must support seeking and
|
||||
support binary reading.
|
||||
|
||||
reel_header: A dictionary containing reel header data.
|
||||
|
||||
trace_catalog: A mapping from zero-based trace index to the byte-offset to
|
||||
individual traces within the file.
|
||||
trace_catalog: A mapping from zero-based trace index to
|
||||
the byte-offset to individual traces within the file.
|
||||
|
||||
cdp_catalog: A mapping from CDP numbers to trace_indexes.
|
||||
|
||||
endian: '>' for big-endian data (the standard and default), '<' for
|
||||
little-endian (non-standard)
|
||||
"""
|
||||
super(SegYReader2D, self).__init__(fh, reel_header, trace_catalog, endian)
|
||||
super(SegYReader2D, self).__init__(
|
||||
fh, reel_header, trace_catalog, endian)
|
||||
self._cdp_catalog = cdp_catalog
|
||||
|
||||
def _dimensionality(self):
|
||||
@@ -335,12 +391,14 @@ class SegYReader2D(SegYReader):
|
||||
"""Obtain the trace index given an xline and a inline.
|
||||
|
||||
Note:
|
||||
Do not assume that all combinations of crossline and inline co-ordinates are valid.
|
||||
The volume may not be rectangular. Valid values can be obtained from the
|
||||
Do not assume that all combinations of crossline and
|
||||
inline co-ordinates are valid. The volume may not be
|
||||
rectangular. Valid values can be obtained from the
|
||||
inline_xlines() iterator.
|
||||
|
||||
Furthermore, inline and crossline numbers should not be relied upon to be zero- or one-based
|
||||
indexes (although they may be).
|
||||
Furthermore, inline and crossline numbers should not be
|
||||
relied upon to be zero- or one-based indexes (although
|
||||
they may be).
|
||||
|
||||
Args:
|
||||
xline: A crossline number.
|
||||
@@ -364,7 +422,8 @@ def main(argv=None):
|
||||
print("Filename: ", segy_reader.filename)
|
||||
print("SEG Y revision: ", segy_reader.revision)
|
||||
print("Number of traces: ", segy_reader.num_traces())
|
||||
print("Data format: ", segy_reader.data_sample_format_description)
|
||||
print("Data format: ",
|
||||
segy_reader.data_sample_format_description)
|
||||
print("Dimensionality: ", segy_reader.dimensionality)
|
||||
|
||||
try:
|
||||
@@ -380,4 +439,3 @@ def main(argv=None):
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
+41
-19
@@ -120,7 +120,8 @@ def catalog_traces(fh, bps, endian='>'):
|
||||
little-endian (non-standard)
|
||||
|
||||
Returns:
|
||||
An immutable sequence containing byte offsets to the beginning of each trace.
|
||||
A tuple of the form `(trace-catalog, cdp-catalog, line-catalog)` where
|
||||
each catalog is an instance of ``collections.Mapping``.
|
||||
"""
|
||||
trace_header_format = compile_trace_header_format(endian)
|
||||
|
||||
@@ -140,7 +141,9 @@ def catalog_traces(fh, bps, endian='>'):
|
||||
samples_bytes = num_samples * bps
|
||||
trace_catalog_builder.add(trace_number, pos_begin)
|
||||
# Should we check the data actually exists?
|
||||
line_catalog.add((trace_header.Inline3D, trace_header.Crossline3D), trace_number)
|
||||
line_catalog.add((trace_header.Inline3D,
|
||||
trace_header.Crossline3D),
|
||||
trace_number)
|
||||
cdp_catalog.add(trace_header.cdp, trace_number)
|
||||
pos_end = pos_begin + TRACE_HEADER_NUM_BYTES + samples_bytes
|
||||
pos_begin = pos_end
|
||||
@@ -173,9 +176,12 @@ def read_binary_values(fh, pos, ctype='l', count=1, endian='>'):
|
||||
buf = fh.read(block_size)
|
||||
|
||||
if len(buf) < block_size:
|
||||
raise EOFError("{} bytes requested but only {} available".format(block_size, len(buf)))
|
||||
raise EOFError("{} bytes requested but only {} available".format(
|
||||
block_size, len(buf)))
|
||||
|
||||
values = unpack_ibm_floats(buf, count) if fmt == 'ibm' else unpack_values(buf, count, item_size, fmt)
|
||||
values = (unpack_ibm_floats(buf, count)
|
||||
if fmt == 'ibm'
|
||||
else unpack_values(buf, count, item_size, fmt))
|
||||
assert len(values) == count
|
||||
return values
|
||||
|
||||
@@ -184,7 +190,8 @@ def unpack_ibm_floats(data, count):
|
||||
"""Unpack a series of binary-encoded big-endian single-precision IBM floats.
|
||||
|
||||
Args:
|
||||
data: A sequence of bytes. (Python 2 - a str object, Python 3 - a bytes object)
|
||||
data: A sequence of bytes. (Python 2 - a str object,
|
||||
Python 3 - a bytes object)
|
||||
|
||||
count: The number of floats to be read.
|
||||
|
||||
@@ -198,38 +205,50 @@ def unpack_values(buf, count, item_size, fmt, endian='>'):
|
||||
"""Unpack a series items from a byte string.
|
||||
|
||||
Args:
|
||||
data: A sequence of bytes. (Python 2 - a str object, Python 3 - a bytes object)
|
||||
data: A sequence of bytes. (Python 2 - a str object,
|
||||
Python 3 - a bytes object)
|
||||
|
||||
count: The number of floats to be read.
|
||||
|
||||
fmt: A format code (one of the values in the datatype.CTYPES dictionary)
|
||||
fmt: A format code (one of the values in the datatype.CTYPES
|
||||
dictionary)
|
||||
|
||||
Returns:
|
||||
A sequence of objects with type corresponding to the format code.
|
||||
"""
|
||||
c_format = '{}{}{}'.format(endian, count, fmt)
|
||||
return struct.unpack(c_format, buf)
|
||||
# We could use array.fromfile() here. On the one hand it's likely to be faster and more compact,
|
||||
# On the other, it only works on "real" files, not arbitrary file-like-objects and it would require us
|
||||
# to handle endian byte swapping ourselves.
|
||||
# We could use array.fromfile() here. On the one hand it's likely
|
||||
# to be faster and more compact,
|
||||
#
|
||||
# On the other, it only works on "real" files, not arbitrary
|
||||
# file-like-objects and it would require us to handle endian byte
|
||||
# swapping ourselves.
|
||||
|
||||
|
||||
_TraceAttributeSpec = namedtuple('Record', ['name', 'pos', 'type'])
|
||||
|
||||
|
||||
def compile_trace_header_format(endian='>'):
|
||||
"""Compile a format string for use with the struct module from the trace header definition.
|
||||
"""Compile a format string for use with the struct module from the
|
||||
trace header definition.
|
||||
|
||||
Args:
|
||||
endian: '>' for big-endian data (the standard and default), '<' for
|
||||
little-endian (non-standard)
|
||||
|
||||
Returns:
|
||||
A string which can be used with the struct module for parsing trace headers.
|
||||
A string which can be used with the struct module for parsing
|
||||
trace headers.
|
||||
|
||||
"""
|
||||
|
||||
record_specs = sorted([_TraceAttributeSpec(name, TRACE_HEADER_DEF[name]['pos'], TRACE_HEADER_DEF[name]['type']) for name in TRACE_HEADER_DEF],
|
||||
key=lambda r : r.pos)
|
||||
record_specs = sorted(
|
||||
[_TraceAttributeSpec(name,
|
||||
TRACE_HEADER_DEF[name]['pos'],
|
||||
TRACE_HEADER_DEF[name]['type'])
|
||||
for name in TRACE_HEADER_DEF],
|
||||
key=lambda r: r.pos)
|
||||
|
||||
fmt = [endian]
|
||||
length = 0
|
||||
@@ -251,11 +270,14 @@ def compile_trace_header_format(endian='>'):
|
||||
|
||||
def _compile_trace_header_record():
|
||||
"""Build a TraceHeader namedtuple from the trace header definition"""
|
||||
record_specs = sorted([_TraceAttributeSpec(name, TRACE_HEADER_DEF[name]['pos'], TRACE_HEADER_DEF[name]['type']) for name in TRACE_HEADER_DEF],
|
||||
key=lambda r : r.pos)
|
||||
return namedtuple('TraceHeader', (record_spec.name for record_spec in record_specs))
|
||||
record_specs = sorted(
|
||||
[_TraceAttributeSpec(name,
|
||||
TRACE_HEADER_DEF[name]['pos'],
|
||||
TRACE_HEADER_DEF[name]['type'])
|
||||
for name in TRACE_HEADER_DEF],
|
||||
key=lambda r: r.pos)
|
||||
return namedtuple('TraceHeader',
|
||||
(record_spec.name for record_spec in record_specs))
|
||||
|
||||
|
||||
TraceHeader = _compile_trace_header_record()
|
||||
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ TRACE_HEADER_DEF["DataUse"]["descr"] = {0: {
|
||||
2: "Test"}}
|
||||
TRACE_HEADER_DEF["DataUse"]["descr"][1] = TRACE_HEADER_DEF["DataUse"]["descr"][0]
|
||||
TRACE_HEADER_DEF["offset"] = {"pos": 36, "type": "int32"}
|
||||
TRACE_HEADER_DEF["ReceiverGroupElevation"] = {"pos": 40, "type": "int32"}
|
||||
TTRACE_HEADER_DEF["ReceiverGroupElevation"] = {"pos": 40, "type": "int32"}
|
||||
TRACE_HEADER_DEF["SourceSurfaceElevation"] = {"pos": 44, "type": "int32"}
|
||||
TRACE_HEADER_DEF["SourceDepth"] = {"pos": 48, "type": "int32"}
|
||||
TRACE_HEADER_DEF["ReceiverDatumElevation"] = {"pos": 52, "type": "int32"}
|
||||
|
||||
@@ -33,7 +33,7 @@ def contains_duplicates(sorted_iterable):
|
||||
|
||||
|
||||
def measure_stride(iterable):
|
||||
"""Determine whether successive numeric items differ by a contant amount.
|
||||
"""Determine whether successive numeric items differ by a constant amount.
|
||||
|
||||
Args:
|
||||
iterable: An iterable series of numeric values.
|
||||
|
||||
Reference in New Issue
Block a user