mirror of
https://github.com/wassname/segpy.git
synced 2026-07-19 11:27:54 +08:00
Merge branch 'ibm_float'
This commit is contained in:
+4
-1
@@ -1,3 +1,6 @@
|
||||
# Data
|
||||
data/
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
@@ -53,7 +56,7 @@ coverage.xml
|
||||
*.pot
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
docs/build/
|
||||
|
||||
# Event stores
|
||||
*.events
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
language: python
|
||||
python:
|
||||
- "2.7"
|
||||
- "3.3"
|
||||
- "3.4"
|
||||
# command to install dependencies
|
||||
install:
|
||||
- "pip install -r test/test-requirements.txt"
|
||||
# command to run tests
|
||||
script: nosetests
|
||||
@@ -0,0 +1,32 @@
|
||||
The SEG Y file format is one of several standards developed by the Society of Exploration Geophysicists for storing
|
||||
geophysical seismic data. It is an open standard, and is controlled by the SEG Technical Standards Committee, a
|
||||
non-profit organization.
|
||||
|
||||
This project aims to implement an open SEG Y module in Python for transporting seismic data between SEG Y files and
|
||||
Python data structures in pure Python.
|
||||
|
||||
|
||||
Status
|
||||
======
|
||||
|
||||
*Segpy 2* is currently in alpha, so expect rough edges. That said, it seems to broadly work and is largely feature
|
||||
complete.
|
||||
|
||||
|
||||
What It Does
|
||||
============
|
||||
|
||||
How To Get It
|
||||
=============
|
||||
|
||||
*Segpy* is available on the Python Package index and can be installed with ``pip``::
|
||||
|
||||
$ pip install segpy
|
||||
|
||||
|
||||
Requirements
|
||||
============
|
||||
|
||||
*Segpy 2* work with Python 3.2 and higher (and 2.7 for now). For the majority of use *Segpy 2* has no external
|
||||
dependencies. Optional modules with further dependencies such as *Numpy* are included in the ``segpy.ext`` package of
|
||||
extras.
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#Licence: GPLv2.0
|
||||
#Copyright: Dave Aitel
|
||||
|
||||
import sys
|
||||
|
||||
try:
|
||||
import pygtk
|
||||
#tell pyGTK, if possible, that we want GTKv2
|
||||
pygtk.require("2.0")
|
||||
except:
|
||||
print "You need to install pyGTK or GTKv2 or set your PYTHONPATH correctly"
|
||||
print "try: export PYTHONPATH=/usr/local/lib/python2.2/site-packages/"
|
||||
sys.exit(1)
|
||||
|
||||
import gtk
|
||||
import gtk.glade
|
||||
import segypy
|
||||
|
||||
#now we have both gtk and gtk.glade imported
|
||||
#Also, we know we are running GTKv2
|
||||
|
||||
def insert_row(model,parent,firstcolumn,secondcolumn):
|
||||
myiter=model.insert_after(parent,None)
|
||||
model.set_value(myiter,0,firstcolumn)
|
||||
model.set_value(myiter,1,secondcolumn)
|
||||
return myiter
|
||||
|
||||
class appgui:
|
||||
def __init__(self):
|
||||
"""
|
||||
In this init we are going to display the main serverinfo window
|
||||
"""
|
||||
gladefile="project1.glade"
|
||||
windowname="serverinfo"
|
||||
self.wTree=gtk.glade.XML (gladefile,windowname)
|
||||
#we only have one callback to register, but you could register
|
||||
#any number, or use a special class that
|
||||
#automatically registers all callbacks
|
||||
dic = { "on_button1_clicked" : self.button1_clicked,
|
||||
"on_serverinfo_destroy" : (gtk.mainquit)}
|
||||
self.wTree.signal_autoconnect (dic)
|
||||
|
||||
self.logwindowview=self.wTree.get_widget("textview1")
|
||||
self.logwindow=gtk.TextBuffer(None)
|
||||
self.logwindowview.set_buffer(self.logwindow)
|
||||
|
||||
import gobject
|
||||
self.treeview=self.wTree.get_widget("treeview1")
|
||||
self.treemodel=gtk.TreeStore(gobject.TYPE_STRING,
|
||||
gobject.TYPE_INT,
|
||||
gobject.TYPE_STRING)
|
||||
self.treeview.set_model(self.treemodel)
|
||||
|
||||
self.treeview.set_headers_visible(True)
|
||||
|
||||
renderer=gtk.CellRendererText()
|
||||
column=gtk.TreeViewColumn("Name",renderer, text=0)
|
||||
column.set_resizable(True)
|
||||
self.treeview.append_column(column)
|
||||
|
||||
renderer=gtk.CellRendererText()
|
||||
column=gtk.TreeViewColumn("Description",renderer, text=1)
|
||||
column.set_resizable(True)
|
||||
self.treeview.append_column(column)
|
||||
|
||||
self.treeview.show()
|
||||
|
||||
model=self.treemodel
|
||||
treeSTH=insert_row(model,None,'Segy Trace Header', '')
|
||||
for key in segypy.TRACE_HEADER_DEF.keys():
|
||||
insert_row(model,treeSTH,key, 'An Empty Row')
|
||||
treeSH=insert_row(model,None,'Segy Header', 'SH')
|
||||
for key in segypy.HEADER_DEF.keys():
|
||||
insert_row(model,treeSH,key, 'An Empty Row')
|
||||
|
||||
|
||||
|
||||
return
|
||||
|
||||
|
||||
#####CALLLBACKS
|
||||
def button1_clicked(self,widget):
|
||||
print "button clicked"
|
||||
host=self.wTree.get_widget("entry1").get_text()
|
||||
port=int(self.wTree.get_widget("spinbutton1").get_value())
|
||||
if host=="":
|
||||
return
|
||||
import urllib
|
||||
page=urllib.urlopen("http://"+host+":"+str(port)+"/")
|
||||
data=page.read()
|
||||
try:
|
||||
#this changed in a revision of pyGTK2
|
||||
self.logwindow.insert_at_cursor(data,len(data))
|
||||
except:
|
||||
self.logwindow.insert_at_cursor(data)
|
||||
#print data
|
||||
return
|
||||
|
||||
|
||||
app=appgui()
|
||||
gtk.main()
|
||||
|
||||
|
||||
|
||||
-240
@@ -1,240 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# Small test to demonstrate glade.XML.signal_autoconnect on an instance
|
||||
#
|
||||
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use('GTK')
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.axes import Subplot
|
||||
from matplotlib.backends.backend_gtk import FigureCanvasGTK, NavigationToolbar
|
||||
from matplotlib.numerix import arange, sin, pi
|
||||
|
||||
import Numeric as numpy
|
||||
|
||||
import gtk, gtk.glade
|
||||
|
||||
import segypy
|
||||
|
||||
|
||||
def insert_row(model,parent,firstcolumn,secondcolumn,thirdcolumn):
|
||||
myiter=model.insert_after(parent,None)
|
||||
model.set_value(myiter,0,firstcolumn)
|
||||
model.set_value(myiter,1,secondcolumn)
|
||||
model.set_value(myiter,2,thirdcolumn)
|
||||
# print secondcolumn
|
||||
# return myiter
|
||||
|
||||
def update_segyplot(self):
|
||||
self.figure = Figure(figsize=(6,4), dpi=72)
|
||||
self.axis = self.figure.add_subplot(111)
|
||||
self.axis.set_xlabel('Trace Header')
|
||||
self.axis.set_ylabel('Time [s]')
|
||||
self.axis.set_title(self.segy[1]['filename'])
|
||||
self.axis.grid(True)
|
||||
self.axis.imshow(self.segy[0])
|
||||
self.canvas = FigureCanvasGTK(self.figure) # a gtk.DrawingArea
|
||||
self.canvas.show()
|
||||
|
||||
self.graphview = self.xml.get_widget("vbox4")
|
||||
self.graphview.pack_start(self.canvas, True, True)
|
||||
|
||||
def init_treeviews(self):
|
||||
import gobject
|
||||
|
||||
###############################################################
|
||||
# SEGYHEADER VIEW
|
||||
self.treeview1 = self.xml.get_widget("treeview1")
|
||||
self.treemodel1 = gtk.TreeStore(gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING)
|
||||
self.treeview1.set_model(self.treemodel1)
|
||||
self.treeview1.set_headers_visible(True)
|
||||
|
||||
# COLUMN 1
|
||||
renderer= gtk.CellRendererText()
|
||||
self.tvcolumn1 = gtk.TreeViewColumn('Name', renderer, text=0)
|
||||
self.tvcolumn1.set_resizable(True)
|
||||
self.treeview1.append_column(self.tvcolumn1)
|
||||
|
||||
# COLUMN 2
|
||||
renderer= gtk.CellRendererText()
|
||||
self.tvcolumn2 = gtk.TreeViewColumn('Meaning', renderer, text=1)
|
||||
self.tvcolumn2.set_resizable(True)
|
||||
self.treeview1.append_column(self.tvcolumn2)
|
||||
|
||||
# COLUMN 3
|
||||
renderer= gtk.CellRendererText()
|
||||
self.tvcolumn3 = gtk.TreeViewColumn('Value', renderer, text=2)
|
||||
self.tvcolumn3.set_resizable(True)
|
||||
self.treeview1.append_column(self.tvcolumn3)
|
||||
|
||||
###############################################################
|
||||
# SEGYHEADER VIEW
|
||||
|
||||
self.treeview2 = self.xml.get_widget("treeview2")
|
||||
self.treemodel2 = gtk.TreeStore(gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING,
|
||||
gobject.TYPE_STRING)
|
||||
self.treeview2.set_model(self.treemodel2)
|
||||
self.treeview2.set_headers_visible(True)
|
||||
|
||||
# COLUMN 1
|
||||
renderer= gtk.CellRendererText()
|
||||
self.tvcolumn1 = gtk.TreeViewColumn('Name', renderer, text=0)
|
||||
self.tvcolumn1.set_resizable(True)
|
||||
self.treeview2.append_column(self.tvcolumn1)
|
||||
|
||||
# COLUMN 2
|
||||
renderer= gtk.CellRendererText()
|
||||
self.tvcolumn2 = gtk.TreeViewColumn('Meaning', renderer, text=1)
|
||||
self.tvcolumn2.set_resizable(True)
|
||||
self.treeview2.append_column(self.tvcolumn2)
|
||||
|
||||
# COLUMN 3
|
||||
renderer= gtk.CellRendererText()
|
||||
self.tvcolumn3 = gtk.TreeViewColumn('Value', renderer, text=2)
|
||||
self.tvcolumn3.set_resizable(True)
|
||||
self.treeview2.append_column(self.tvcolumn3)
|
||||
|
||||
|
||||
def update_segyheader(self):
|
||||
import gobject
|
||||
|
||||
|
||||
# INSERT INTO TREEMODEL
|
||||
self.SHtree= {"init": 1}
|
||||
for key in segypy.HEADER_DEF.keys():
|
||||
SHkey=segypy.HEADER_DEF[key]
|
||||
if (SHkey.has_key('descr')):
|
||||
descr = segypy.HEADER_DEF[key]['descr'][0][self.segy[1][key]]
|
||||
else:
|
||||
descr = ''
|
||||
insert_row(self.treemodel1,None,key,descr,self.segy[1][key])
|
||||
|
||||
self.treeview1.show()
|
||||
|
||||
def update_segytraceheader(self,itrace=1):
|
||||
import gobject
|
||||
|
||||
# INSERT INTO TREEMODEL
|
||||
self.STHtree= {"init": 1}
|
||||
for key in segypy.TRACE_HEADER_DEF.keys():
|
||||
STHkey=segypy.TRACE_HEADER_DEF[key]
|
||||
if (STHkey.has_key('descr')):
|
||||
try:
|
||||
descr = segypy.TRACE_HEADER_DEF[key]['descr'][0][self.segy[2][key][itrace-1]]
|
||||
except:
|
||||
descr='Not Defined'
|
||||
else:
|
||||
descr = ''
|
||||
insert_row(self.treemodel2,None,key,descr,self.segy[2][key][itrace-1])
|
||||
|
||||
self.treeview2.show()
|
||||
|
||||
|
||||
class SimpleTest:
|
||||
def __init__(self):
|
||||
self.xml = gtk.glade.XML('segygui/segygui.glade')
|
||||
self.xml.signal_autoconnect(self)
|
||||
init_treeviews(self)
|
||||
self.window1 = self.xml.get_widget("window1")
|
||||
self.window1.resize(600,400)
|
||||
|
||||
def on_cut1_activate(self, button):
|
||||
self.hpaned1= self.xml.get_widget("hpaned1")
|
||||
self.vbox3= self.xml.get_widget("vbox3")
|
||||
pos = self.hpaned1.get_position()
|
||||
if (pos==1):
|
||||
pos=100
|
||||
self.vbox3.visible = 1
|
||||
else:
|
||||
pos=1
|
||||
self.vbox3.visible = 0
|
||||
pos = self.hpaned1.set_position(pos)
|
||||
print self.vbox3.visible
|
||||
|
||||
#width,height = self.vbox3.get_size()
|
||||
#print width,height
|
||||
# self.window1.resize(400,400)
|
||||
|
||||
|
||||
def on_about1_activate(self, button):
|
||||
dialog = gtk.AboutDialog()
|
||||
dialog.set_name("SegyPY GUI")
|
||||
dialog.set_version(segypy.version.__str__())
|
||||
dialog.set_copyright("\302\251 Copyright 200x Thomas Mejer Hansen")
|
||||
dialog.set_website("http://segymat.sourceforge.net/segypy/")
|
||||
authors=["Thomas Mejer Hansen"]
|
||||
dialog.set_authors(authors)
|
||||
try:
|
||||
f=open('LICENSE')
|
||||
license = f.read(100000)
|
||||
except IOError:
|
||||
license = 'Could not read license file....';
|
||||
dialog.set_license(license)
|
||||
## Close dialog on user response
|
||||
dialog.connect ("response", lambda d, r: d.destroy())
|
||||
dialog.show()
|
||||
|
||||
def on_open1_activate(self, button):
|
||||
dialog = gtk.FileChooserDialog("Open..",
|
||||
None,
|
||||
gtk.FILE_CHOOSER_ACTION_OPEN,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_OPEN, gtk.RESPONSE_OK))
|
||||
dialog.set_default_response(gtk.RESPONSE_OK)
|
||||
|
||||
filter = gtk.FileFilter()
|
||||
filter.set_name("All files")
|
||||
filter.add_pattern("*")
|
||||
|
||||
dialog.add_filter(filter)
|
||||
filter = gtk.FileFilter()
|
||||
filter.set_name("SEG-Y files")
|
||||
filter.add_pattern("*.segy")
|
||||
filter.add_pattern("*.sgy")
|
||||
filter.add_pattern("*.seg-y")
|
||||
dialog.add_filter(filter)
|
||||
filter = gtk.FileFilter()
|
||||
filter.set_name("SU files")
|
||||
filter.add_pattern("*.su")
|
||||
dialog.add_filter(filter)
|
||||
response = dialog.run()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
print dialog.get_filename(), 'selected'
|
||||
filename = dialog.get_filename()
|
||||
dialog.destroy()
|
||||
self.segy = segypy.read_segy(filename)
|
||||
|
||||
update_segyheader(self)
|
||||
update_segytraceheader(self)
|
||||
update_segyplot(self)
|
||||
|
||||
def on_new1_activate(self, button):
|
||||
self.segy = segypy.read_segy('../data_4byteINT.segy')
|
||||
update_segyheader(self)
|
||||
update_segytraceheader(self,1)
|
||||
update_segyplot(self)
|
||||
|
||||
|
||||
test = SimpleTest()
|
||||
|
||||
gtk.main()
|
||||
@@ -1,192 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||
|
||||
<glade-interface>
|
||||
|
||||
<widget class="GtkWindow" id="serverinfo">
|
||||
<property name="visible">True</property>
|
||||
<property name="title" translatable="yes">Server Info</property>
|
||||
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||
<property name="window_position">GTK_WIN_POS_NONE</property>
|
||||
<property name="modal">False</property>
|
||||
<property name="resizable">True</property>
|
||||
<property name="destroy_with_parent">False</property>
|
||||
<property name="decorated">True</property>
|
||||
<property name="skip_taskbar_hint">False</property>
|
||||
<property name="skip_pager_hint">False</property>
|
||||
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
|
||||
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
|
||||
<property name="focus_on_map">True</property>
|
||||
<signal name="destroy" handler="on_serverinfo_destroy" last_modification_time="Wed, 15 Jan 2003 16:24:14 GMT"/>
|
||||
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">8</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Host:</property>
|
||||
<property name="use_underline">False</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||
<property name="width_chars">-1</property>
|
||||
<property name="single_line_mode">False</property>
|
||||
<property name="angle">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkEntry" id="entry1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">True</property>
|
||||
<property name="visibility">True</property>
|
||||
<property name="max_length">0</property>
|
||||
<property name="text" translatable="yes"></property>
|
||||
<property name="has_frame">True</property>
|
||||
<property name="invisible_char">*</property>
|
||||
<property name="activates_default">False</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkSpinButton" id="spinbutton1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="digits">0</property>
|
||||
<property name="numeric">False</property>
|
||||
<property name="update_policy">GTK_UPDATE_ALWAYS</property>
|
||||
<property name="snap_to_ticks">False</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="adjustment">80 1 65535 1 10 10</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkButton" id="button1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="label" translatable="yes">GO!</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||
<property name="focus_on_click">True</property>
|
||||
<signal name="clicked" handler="on_button1_clicked" last_modification_time="Wed, 15 Jan 2003 15:10:01 GMT"/>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
|
||||
<property name="shadow_type">GTK_SHADOW_NONE</property>
|
||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTextView" id="textview1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">True</property>
|
||||
<property name="overwrite">False</property>
|
||||
<property name="accepts_tab">True</property>
|
||||
<property name="justification">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap_mode">GTK_WRAP_NONE</property>
|
||||
<property name="cursor_visible">True</property>
|
||||
<property name="pixels_above_lines">0</property>
|
||||
<property name="pixels_below_lines">0</property>
|
||||
<property name="pixels_inside_wrap">0</property>
|
||||
<property name="left_margin">0</property>
|
||||
<property name="right_margin">0</property>
|
||||
<property name="indent">0</property>
|
||||
<property name="text" translatable="yes"></property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
|
||||
<property name="shadow_type">GTK_SHADOW_NONE</property>
|
||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTreeView" id="treeview1">
|
||||
<property name="width_request">130</property>
|
||||
<property name="height_request">226</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_visible">True</property>
|
||||
<property name="rules_hint">False</property>
|
||||
<property name="reorderable">False</property>
|
||||
<property name="enable_search">True</property>
|
||||
<property name="fixed_height_mode">False</property>
|
||||
<property name="hover_selection">False</property>
|
||||
<property name="hover_expand">False</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
|
||||
</glade-interface>
|
||||
@@ -1,497 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||
|
||||
<glade-interface>
|
||||
|
||||
<widget class="GtkWindow" id="window1">
|
||||
<property name="visible">True</property>
|
||||
<property name="title" translatable="yes">Segy Viewer</property>
|
||||
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||
<property name="window_position">GTK_WIN_POS_CENTER</property>
|
||||
<property name="modal">False</property>
|
||||
<property name="resizable">True</property>
|
||||
<property name="destroy_with_parent">False</property>
|
||||
<property name="icon_name">stock_chart-reorganize</property>
|
||||
<property name="decorated">True</property>
|
||||
<property name="skip_taskbar_hint">False</property>
|
||||
<property name="skip_pager_hint">False</property>
|
||||
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
|
||||
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
|
||||
<property name="focus_on_map">True</property>
|
||||
<property name="urgency_hint">False</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuBar" id="menubar1">
|
||||
<property name="visible">True</property>
|
||||
<property name="pack_direction">GTK_PACK_DIRECTION_LTR</property>
|
||||
<property name="child_pack_direction">GTK_PACK_DIRECTION_LTR</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_File</property>
|
||||
<property name="use_underline">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="menuitem1_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="new1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-new</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_new1_activate" last_modification_time="Sun, 09 Oct 2005 20:43:06 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="open1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-open</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_open1_activate" last_modification_time="Sun, 09 Oct 2005 20:43:06 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="save1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-save</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_save1_activate" last_modification_time="Sun, 09 Oct 2005 20:43:06 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="save_as1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-save-as</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_save_as1_activate" last_modification_time="Sun, 09 Oct 2005 20:43:06 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
|
||||
<property name="visible">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="quit1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-quit</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_quit1_activate" last_modification_time="Sun, 09 Oct 2005 20:43:06 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem2">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Edit</property>
|
||||
<property name="use_underline">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="menuitem2_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="segy_header1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Segy Header</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_segy_header1_activate" last_modification_time="Mon, 10 Oct 2005 07:29:34 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="trace_header1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Trace Header</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_trace_header1_activate" last_modification_time="Mon, 10 Oct 2005 07:29:34 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="cut1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-cut</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_cut1_activate" last_modification_time="Sun, 09 Oct 2005 20:43:06 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="copy1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-copy</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_copy1_activate" last_modification_time="Sun, 09 Oct 2005 20:43:06 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="paste1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-paste</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_paste1_activate" last_modification_time="Sun, 09 Oct 2005 20:43:06 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="delete1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-delete</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_delete1_activate" last_modification_time="Sun, 09 Oct 2005 20:43:06 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem3">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_View</property>
|
||||
<property name="use_underline">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem4">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Help</property>
|
||||
<property name="use_underline">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="menuitem4_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="about1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_About</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_about1_activate" last_modification_time="Sun, 09 Oct 2005 20:43:06 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkHPaned" id="hpaned1">
|
||||
<property name="width_request">385</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox3">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkNotebook" id="notebook1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="show_tabs">True</property>
|
||||
<property name="show_border">True</property>
|
||||
<property name="tab_pos">GTK_POS_TOP</property>
|
||||
<property name="scrollable">False</property>
|
||||
<property name="enable_popup">False</property>
|
||||
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Plot</property>
|
||||
<property name="use_underline">False</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||
<property name="width_chars">-1</property>
|
||||
<property name="single_line_mode">False</property>
|
||||
<property name="angle">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="type">tab</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
|
||||
<property name="shadow_type">GTK_SHADOW_IN</property>
|
||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTreeView" id="treeview1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_visible">True</property>
|
||||
<property name="rules_hint">False</property>
|
||||
<property name="reorderable">False</property>
|
||||
<property name="enable_search">True</property>
|
||||
<property name="fixed_height_mode">False</property>
|
||||
<property name="hover_selection">False</property>
|
||||
<property name="hover_expand">False</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="tab_expand">False</property>
|
||||
<property name="tab_fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Header</property>
|
||||
<property name="use_underline">False</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||
<property name="width_chars">-1</property>
|
||||
<property name="single_line_mode">False</property>
|
||||
<property name="angle">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="type">tab</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox5">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkArrow" id="arrow3">
|
||||
<property name="width_request">15</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="arrow_type">GTK_ARROW_LEFT</property>
|
||||
<property name="shadow_type">GTK_SHADOW_OUT</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
<signal name="button_press_event" handler="on_arrow3_button_press_event" last_modification_time="Mon, 17 Oct 2005 10:28:17 GMT"/>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkHScale" id="hscale1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="draw_value">False</property>
|
||||
<property name="value_pos">GTK_POS_TOP</property>
|
||||
<property name="digits">0</property>
|
||||
<property name="update_policy">GTK_UPDATE_DISCONTINUOUS</property>
|
||||
<property name="inverted">False</property>
|
||||
<property name="adjustment">0 0 100 0 0 0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkArrow" id="arrow2">
|
||||
<property name="width_request">15</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="arrow_type">GTK_ARROW_RIGHT</property>
|
||||
<property name="shadow_type">GTK_SHADOW_OUT</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
<signal name="button_press_event" handler="on_arrow2_button_press_event" last_modification_time="Mon, 17 Oct 2005 10:28:03 GMT"/>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkScrolledWindow" id="scrolledwindow2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
|
||||
<property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
|
||||
<property name="shadow_type">GTK_SHADOW_IN</property>
|
||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTreeView" id="treeview2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_visible">True</property>
|
||||
<property name="rules_hint">False</property>
|
||||
<property name="reorderable">False</property>
|
||||
<property name="enable_search">True</property>
|
||||
<property name="fixed_height_mode">False</property>
|
||||
<property name="hover_selection">False</property>
|
||||
<property name="hover_expand">False</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="tab_expand">False</property>
|
||||
<property name="tab_fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Trace</property>
|
||||
<property name="use_underline">False</property>
|
||||
<property name="use_markup">False</property>
|
||||
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||
<property name="wrap">False</property>
|
||||
<property name="selectable">False</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||
<property name="width_chars">-1</property>
|
||||
<property name="single_line_mode">False</property>
|
||||
<property name="angle">0</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="type">tab</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="shrink">True</property>
|
||||
<property name="resize">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox4">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="shrink">True</property>
|
||||
<property name="resize">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkStatusbar" id="statusbar1">
|
||||
<property name="visible">True</property>
|
||||
<property name="has_resize_grip">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="padding">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
|
||||
</glade-interface>
|
||||
@@ -1,251 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||
|
||||
<glade-interface>
|
||||
|
||||
<widget class="GtkWindow" id="window1">
|
||||
<property name="width_request">800</property>
|
||||
<property name="height_request">600</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="title" translatable="yes">SegyPY GUI</property>
|
||||
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||
<property name="window_position">GTK_WIN_POS_NONE</property>
|
||||
<property name="modal">False</property>
|
||||
<property name="resizable">True</property>
|
||||
<property name="destroy_with_parent">False</property>
|
||||
<property name="decorated">True</property>
|
||||
<property name="skip_taskbar_hint">False</property>
|
||||
<property name="skip_pager_hint">False</property>
|
||||
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
|
||||
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
|
||||
<property name="focus_on_map">True</property>
|
||||
<property name="urgency_hint">False</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTable" id="table1">
|
||||
<property name="width_request">307</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="has_default">True</property>
|
||||
<property name="n_rows">3</property>
|
||||
<property name="n_columns">1</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="row_spacing">0</property>
|
||||
<property name="column_spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuBar" id="menubar1">
|
||||
<property name="visible">True</property>
|
||||
<property name="pack_direction">GTK_PACK_DIRECTION_LTR</property>
|
||||
<property name="child_pack_direction">GTK_PACK_DIRECTION_LTR</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_File</property>
|
||||
<property name="use_underline">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="menuitem1_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="new1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-new</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_new1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="open1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-open</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_open1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="save1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-save</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_save1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="save_as1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-save-as</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_save_as1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
|
||||
<property name="visible">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="quit1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-quit</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_quit1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem2">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Edit</property>
|
||||
<property name="use_underline">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="menuitem2_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="cut1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-cut</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_cut1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="copy1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-copy</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_copy1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="paste1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-paste</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_paste1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="delete1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-delete</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_delete1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem3">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_View</property>
|
||||
<property name="use_underline">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem4">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Help</property>
|
||||
<property name="use_underline">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="menuitem4_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="about1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_About</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_about1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="right_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="bottom_attach">1</property>
|
||||
<property name="x_options">fill</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkToolbar" id="toolbar1">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
<property name="toolbar_style">GTK_TOOLBAR_BOTH</property>
|
||||
<property name="tooltips">True</property>
|
||||
<property name="show_arrow">True</property>
|
||||
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="right_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_options">fill</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="right_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="x_options">fill</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
|
||||
</glade-interface>
|
||||
@@ -1,250 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||
|
||||
<glade-interface>
|
||||
|
||||
<widget class="GtkWindow" id="window1">
|
||||
<property name="width_request">800</property>
|
||||
<property name="height_request">600</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="title" translatable="yes">SegyPY GUI</property>
|
||||
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||
<property name="window_position">GTK_WIN_POS_NONE</property>
|
||||
<property name="modal">False</property>
|
||||
<property name="resizable">True</property>
|
||||
<property name="destroy_with_parent">False</property>
|
||||
<property name="decorated">True</property>
|
||||
<property name="skip_taskbar_hint">False</property>
|
||||
<property name="skip_pager_hint">False</property>
|
||||
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
|
||||
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
|
||||
<property name="focus_on_map">True</property>
|
||||
<property name="urgency_hint">False</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkTable" id="table1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="has_default">True</property>
|
||||
<property name="n_rows">3</property>
|
||||
<property name="n_columns">1</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="row_spacing">0</property>
|
||||
<property name="column_spacing">0</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuBar" id="menubar1">
|
||||
<property name="visible">True</property>
|
||||
<property name="pack_direction">GTK_PACK_DIRECTION_LTR</property>
|
||||
<property name="child_pack_direction">GTK_PACK_DIRECTION_LTR</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_File</property>
|
||||
<property name="use_underline">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="menuitem1_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="new1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-new</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_new1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="open1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-open</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_open1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="save1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-save</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_save1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="save_as1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-save-as</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_save_as1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
|
||||
<property name="visible">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="quit1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-quit</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_quit1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem2">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Edit</property>
|
||||
<property name="use_underline">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="menuitem2_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="cut1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-cut</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_cut1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="copy1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-copy</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_copy1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="paste1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-paste</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_paste1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="delete1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label">gtk-delete</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="activate" handler="on_delete1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem3">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_View</property>
|
||||
<property name="use_underline">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="menuitem4">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Help</property>
|
||||
<property name="use_underline">True</property>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="menuitem4_menu">
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="about1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_About</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_about1_activate" last_modification_time="Sat, 08 Oct 2005 11:26:31 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="right_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="bottom_attach">1</property>
|
||||
<property name="x_options">fill</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkToolbar" id="toolbar1">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||
<property name="toolbar_style">GTK_TOOLBAR_BOTH</property>
|
||||
<property name="tooltips">True</property>
|
||||
<property name="show_arrow">True</property>
|
||||
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="right_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_options">fill</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">False</property>
|
||||
<property name="spacing">0</property>
|
||||
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="right_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="x_options">fill</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
|
||||
</glade-interface>
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||
<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd">
|
||||
|
||||
<glade-project>
|
||||
<name>Test</name>
|
||||
<program_name>test</program_name>
|
||||
<gnome_support>FALSE</gnome_support>
|
||||
</glade-project>
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||
<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd">
|
||||
|
||||
<glade-project>
|
||||
<name>Test</name>
|
||||
<program_name>test</program_name>
|
||||
<gnome_support>FALSE</gnome_support>
|
||||
</glade-project>
|
||||
+20
-26
@@ -1,34 +1,28 @@
|
||||
=====
|
||||
Segpy
|
||||
=====
|
||||
=======
|
||||
Segpy 2
|
||||
=======
|
||||
|
||||
Status
|
||||
======
|
||||
|
||||
Build status: rewrite branch:
|
||||
|
||||
.. image:: https://travis-ci.org/rob-smallshire/segpy.svg?branch=rewrite
|
||||
:target: https://travis-ci.org/rob-smallshire/segpy
|
||||
|
||||
What is Segpy?
|
||||
==============
|
||||
|
||||
The SEG Y file format is one of several standards developed by the Society of Exploration Geophysicists for storing
|
||||
geophysical seismic data. It is an open standard, and is controlled by the SEG Technical Standards Committee, a
|
||||
non-profit organization.
|
||||
|
||||
This project aims to implement an open SEG Y module in Python for transporting seismic data between SEG Y files and
|
||||
Numpy arrays. Segpy is a package for reading, writing and manipulating SEG Y data in pure Python.
|
||||
Python data structures in pure Python.
|
||||
|
||||
History
|
||||
=======
|
||||
Segpy Versions
|
||||
==============
|
||||
|
||||
Segpy is a fork of a seemingly abandoned Python SEG Y reader called `SegyPY <http://segymat.sourceforge.net/segypy/>`_
|
||||
which was last updated in 2005. I couldn't get a response from the original author of SegyPY and since it the code was
|
||||
under an LGPL license I took the decision in 2011 to fork and run a new project on Google Code, under a the name of
|
||||
*Segpy* avoid confusion. In July 2014 the project was again migrated from a now defunct Mercurial repository on Google
|
||||
Code to this Git repository on GitHub.
|
||||
|
||||
The aim of the revived project is to fix serious problems with the code base in the area of correctness and
|
||||
performance, but also to update it to work with contemporary Python environments including Python 2.7 and Python 3.
|
||||
|
||||
**Pull requests, bug reports and suggestions for improvements are most welcome!**
|
||||
|
||||
|
||||
Authors
|
||||
=======
|
||||
|
||||
* Robert Smallshire 2011 to date
|
||||
* Thomas Mejer Hansen 2005
|
||||
|
||||
The Ibm2Ieee conversion routines are developed and made availabe for SegyPY by Secchi Angelo, who thanks Howard
|
||||
Lightstone and Anton Vredegoor for their help.
|
||||
Segpy 2.0 is a complete re-imagining of a SEG Y reader in Python and represents a complete break from Segpy 1.0 in terms
|
||||
of the interface it presents to clients and the implementation behind those interfaces. Segpy 1.0 should be considered
|
||||
unmaintained legacy software. The present and future of Segpy is Segpy 2.
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = build
|
||||
|
||||
# User-friendly check for sphinx-build
|
||||
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
|
||||
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
|
||||
endif
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " xml to make Docutils-native XML files"
|
||||
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Segpy.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Segpy.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/Segpy"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Segpy"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
latexpdfja:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through platex and dvipdfmx..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
xml:
|
||||
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
|
||||
@echo
|
||||
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
|
||||
|
||||
pseudoxml:
|
||||
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
|
||||
@echo
|
||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
||||
@@ -0,0 +1,2 @@
|
||||
sphinx
|
||||
sphinx_rtd_theme
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
@ECHO OFF
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set BUILDDIR=build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
|
||||
set I18NSPHINXOPTS=%SPHINXOPTS% source
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. singlehtml to make a single large HTML file
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. devhelp to make HTML files and a Devhelp project
|
||||
echo. epub to make an epub
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. text to make text files
|
||||
echo. man to make manual pages
|
||||
echo. texinfo to make Texinfo files
|
||||
echo. gettext to make PO message catalogs
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. xml to make Docutils-native XML files
|
||||
echo. pseudoxml to make pseudoxml-XML files for display purposes
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
|
||||
%SPHINXBUILD% 2> nul
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "singlehtml" (
|
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Segpy.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Segpy.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "devhelp" (
|
||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub" (
|
||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latexpdf" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
cd %BUILDDIR%/latex
|
||||
make all-pdf
|
||||
cd %BUILDDIR%/..
|
||||
echo.
|
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latexpdfja" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
cd %BUILDDIR%/latex
|
||||
make all-pdf-ja
|
||||
cd %BUILDDIR%/..
|
||||
echo.
|
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "text" (
|
||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The text files are in %BUILDDIR%/text.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "man" (
|
||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "texinfo" (
|
||||
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "gettext" (
|
||||
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "xml" (
|
||||
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The XML files are in %BUILDDIR%/xml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pseudoxml" (
|
||||
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
||||
@@ -0,0 +1,272 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Segpy documentation build configuration file, created by
|
||||
# sphinx-quickstart on Sat Jan 31 17:56:17 2015.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys
|
||||
|
||||
# on_rtd is whether we are on readthedocs.org
|
||||
import os
|
||||
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.ifconfig',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'Segpy'
|
||||
copyright = '2015, Robert Smallshire'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '2.0.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '2.0.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
#keep_warnings = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
|
||||
if not on_rtd: # only import and set the theme if we're building docs locally
|
||||
import sphinx_rtd_theme
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
|
||||
# otherwise, readthedocs.org uses their theme by default, so no need to specify it
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
#html_extra_path = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Segpydoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'Segpy.tex', 'Segpy Documentation',
|
||||
'Robert Smallshire', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'segpy', 'Segpy Documentation',
|
||||
['Robert Smallshire'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'Segpy', 'Segpy Documentation',
|
||||
'Robert Smallshire', 'Segpy', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
#texinfo_no_detailmenu = False
|
||||
@@ -0,0 +1,49 @@
|
||||
.. Segpy documentation master file, created by
|
||||
sphinx-quickstart on Sat Jan 31 17:56:17 2015.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
=====
|
||||
Segpy
|
||||
=====
|
||||
|
||||
*Segpy* is a Python package for reading and writing SEG Y data. The SEG Y file format is one of several standards
|
||||
developed by the Society of Exploration Geophysicists for storing geophysical seismic data. It is an open standard, and
|
||||
is controlled by the SEG Technical Standards Committee, a non-profit organization.
|
||||
|
||||
This project aims to implement an open SEG Y module in Python for transporting seismic data between SEG Y files and
|
||||
Python data structures.
|
||||
|
||||
Contents
|
||||
========
|
||||
|
||||
Front Matter
|
||||
------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
Narrative Documentation
|
||||
-----------------------
|
||||
|
||||
Read this to learn how to use *Segpy*:
|
||||
|
||||
|
||||
Reference Documentation
|
||||
-----------------------
|
||||
|
||||
Descriptions and examples for every public function, class and method in *Segpy*.
|
||||
|
||||
|
||||
Change History
|
||||
--------------
|
||||
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
"""A simple example which loads a SEG Y file and saves it again.
|
||||
|
||||
Usage:
|
||||
|
||||
loadsave.py <in.segy> <out.segy>
|
||||
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from segpy.reader import create_reader
|
||||
from segpy.writer import write_segy
|
||||
|
||||
|
||||
def load_save(in_filename, out_filename):
|
||||
with open(in_filename, 'rb') as in_file, \
|
||||
open(out_filename, 'wb') as out_file:
|
||||
|
||||
segy_reader = create_reader(in_file)
|
||||
write_segy(out_file, segy_reader)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if argv is None:
|
||||
argv = sys.argv[1:]
|
||||
|
||||
try:
|
||||
in_filename = argv[0]
|
||||
out_filename = argv[1]
|
||||
except IndexError:
|
||||
print(globals()['__doc__'], file=sys.stderr)
|
||||
return os.EX_USAGE
|
||||
|
||||
try:
|
||||
load_save(in_filename, out_filename)
|
||||
except (FileNotFoundError, IsADirectoryError) as e:
|
||||
print(e, file=sys.stderr)
|
||||
return os.EX_NOINPUT
|
||||
except PermissionError as e:
|
||||
print(e, file=sys.stderr)
|
||||
return os.EX_NOPERM
|
||||
except Exception as e:
|
||||
traceback.print_exception(type(e), e, e.__traceback__, file=sys.stderr)
|
||||
return os.EX_SOFTWARE
|
||||
return os.EX_OK
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,81 @@
|
||||
"""Displays a simple report of a SEG Y file.
|
||||
|
||||
Usage:
|
||||
|
||||
report.py <in.segy>
|
||||
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from segpy.reader import create_reader
|
||||
|
||||
|
||||
def report_segy(in_filename):
|
||||
with open(in_filename, 'rb') as in_file:
|
||||
|
||||
segy_reader = create_reader(in_file)
|
||||
print()
|
||||
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("Dimensionality: ", segy_reader.dimensionality)
|
||||
|
||||
try:
|
||||
print("Number of CDPs: ", segy_reader.num_cdps())
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
print("Number of inlines: ", segy_reader.num_inlines())
|
||||
print("Number of crosslines: ", segy_reader.num_xlines())
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
print("=== BEGIN TEXTUAL REEL HEADER ===")
|
||||
for line in segy_reader.textual_reel_header:
|
||||
print(line[3:])
|
||||
print("=== END TEXTUAL REEL HEADER ===")
|
||||
print()
|
||||
print("=== BEGIN EXTENDED TEXTUAL HEADER ===")
|
||||
print(segy_reader.extended_textual_header)
|
||||
print("=== END EXTENDED TEXTUAL_HEADER ===")
|
||||
|
||||
for trace_index in segy_reader.trace_indexes():
|
||||
trace_header = segy_reader.trace_header(trace_index)
|
||||
print("Inline {}, Crossline {}, Shotpoint {}".format(trace_header.Inline3D, trace_header.Crossline3D,
|
||||
trace_header.ShotPoint))
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if argv is None:
|
||||
argv = sys.argv[1:]
|
||||
|
||||
try:
|
||||
in_filename = argv[0]
|
||||
except IndexError:
|
||||
print(globals()['__doc__'], file=sys.stderr)
|
||||
return os.EX_USAGE
|
||||
|
||||
try:
|
||||
report_segy(in_filename)
|
||||
except (FileNotFoundError, IsADirectoryError) as e:
|
||||
print(e, file=sys.stderr)
|
||||
return os.EX_NOINPUT
|
||||
except PermissionError as e:
|
||||
print(e, file=sys.stderr)
|
||||
return os.EX_NOPERM
|
||||
except Exception as e:
|
||||
traceback.print_exception(type(e), e, e.__traceback__, file=sys.stderr)
|
||||
return os.EX_SOFTWARE
|
||||
return os.EX_OK
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,68 @@
|
||||
"""A simple example which times reading of all traces in a SEG Y file.
|
||||
|
||||
Usage:
|
||||
|
||||
timed_reader.py <in.segy>
|
||||
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
import datetime
|
||||
|
||||
import os
|
||||
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from segpy.reader import create_reader
|
||||
|
||||
|
||||
def read_traces(in_filename):
|
||||
with open(in_filename, 'rb') as in_file:
|
||||
|
||||
t0 = datetime.datetime.now()
|
||||
|
||||
segy_reader = create_reader(in_file)
|
||||
|
||||
t1 = datetime.datetime.now()
|
||||
|
||||
for trace_index in segy_reader.trace_indexes():
|
||||
trace = segy_reader.trace_samples(trace_index)
|
||||
|
||||
t2 = datetime.datetime.now()
|
||||
|
||||
time_to_read_header = (t1 - t0).total_seconds()
|
||||
time_to_read_traces = (t2 - t1).total_seconds()
|
||||
time_to_read_both = (t2 - t0).total_seconds()
|
||||
|
||||
print("Time to read headers : {} seconds", time_to_read_header)
|
||||
print("Time to read traces : {} seconds", time_to_read_traces)
|
||||
print("Total time : {} seconds", time_to_read_both)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
if argv is None:
|
||||
argv = sys.argv[1:]
|
||||
|
||||
try:
|
||||
in_filename = argv[0]
|
||||
except IndexError:
|
||||
print(globals()['__doc__'], file=sys.stderr)
|
||||
return os.EX_USAGE
|
||||
|
||||
try:
|
||||
read_traces(in_filename)
|
||||
except (FileNotFoundError, IsADirectoryError) as e:
|
||||
print(e, file=sys.stderr)
|
||||
return os.EX_NOINPUT
|
||||
except PermissionError as e:
|
||||
print(e, file=sys.stderr)
|
||||
return os.EX_NOPERM
|
||||
except Exception as e:
|
||||
traceback.print_exception(type(e), e, e.__traceback__, file=sys.stderr)
|
||||
return os.EX_SOFTWARE
|
||||
return os.EX_OK
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,137 @@
|
||||
"""Extract a timeslice from a 3D seismic volume to a Numpy array.
|
||||
|
||||
Usage: timeslice.py [-h] [--dtype DTYPE] [--null NULL]
|
||||
segy-file npy-file slice-index
|
||||
|
||||
Positional arguments:
|
||||
segy-file Path to an existing SEG Y file of 3D seismic data
|
||||
npy-file Path to the Numpy array file to be created for the timeslice
|
||||
slice-index Zero based index of the time slice to be extracted
|
||||
|
||||
Optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--dtype DTYPE Numpy data type. If not provided a dtype compatible with the
|
||||
SEG Y data will be used.
|
||||
--null NULL Sample value to use for missing or short traces.
|
||||
|
||||
Example:
|
||||
|
||||
timeslice.py stack_final_int8.sgy slice_800.npy 800 --null=42.0 --dtype=f
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import numpy as np
|
||||
|
||||
from segpy.reader import create_reader
|
||||
from segpy_numpy.numpy.dtypes import make_dtype
|
||||
|
||||
|
||||
class DimensionalityError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def extract_timeslice(segy_filename, out_filename, slice_index, dtype=None, null=0):
|
||||
"""Extract a timeslice from a 3D SEG Y file to a Numpy NPY file.
|
||||
|
||||
Args:
|
||||
segy_filename: Filename of a SEG Y file.
|
||||
|
||||
out_filename: Filename of the NPY file.
|
||||
|
||||
slice_index: The zero-based index (increasing with depth) of the slice to be extracted.
|
||||
|
||||
dtype: Optional Numpy dtype for the result array. If not provided a dtype compatible with
|
||||
the SEG Y data will be used.
|
||||
|
||||
null: Optional sample value to use for missing or short traces. Defaults to zero.
|
||||
"""
|
||||
with open(segy_filename, 'rb') as segy_file:
|
||||
|
||||
segy_reader = create_reader(segy_file)
|
||||
|
||||
if dtype is None:
|
||||
dtype = make_dtype(segy_reader.data_sample_format)
|
||||
|
||||
if segy_reader.dimensionality != 3:
|
||||
raise DimensionalityError("Cannot slice {n} dimensional seismic.".format(segy_reader.dimensionality))
|
||||
|
||||
i_line_range = segy_reader.inline_range()
|
||||
x_line_range = segy_reader.xline_range()
|
||||
|
||||
i_size = len(i_line_range)
|
||||
x_size = len(x_line_range)
|
||||
t_size = segy_reader.max_num_trace_samples()
|
||||
|
||||
if not (0 <= slice_index < t_size):
|
||||
raise ValueError("Time slice index {0} out of range {} to {}".format(slice_index, 0, t_size))
|
||||
|
||||
timeslice = np.full((x_size, i_size), null, dtype)
|
||||
|
||||
for inline_num, xline_num in segy_reader.inline_xline_numbers():
|
||||
trace_index = segy_reader.trace_index((inline_num, xline_num))
|
||||
trace = segy_reader.trace_samples(trace_index)
|
||||
|
||||
try:
|
||||
sample = trace[slice_index]
|
||||
except IndexError:
|
||||
sample = null
|
||||
|
||||
i_index = inline_num - i_line_range.start
|
||||
x_index = xline_num - x_line_range.start
|
||||
timeslice[x_index, i_index] = sample
|
||||
|
||||
np.save(out_filename, timeslice)
|
||||
|
||||
|
||||
def nullable_dtype(s):
|
||||
return None if s == "" else np.dtype(s)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("segy_file", metavar="segy-file",
|
||||
help="Path to an existing SEG Y file of 3D seismic data")
|
||||
|
||||
parser.add_argument("npy_file", metavar="npy-file",
|
||||
help="Path to the Numpy array file to be created for the timeslice")
|
||||
|
||||
parser.add_argument("slice_index", metavar="slice-index", type=int,
|
||||
help="Zero based index of the time slice to be extracted", )
|
||||
|
||||
parser.add_argument("--dtype", type=nullable_dtype, default="",
|
||||
help="Numpy data type. If not provided a dtype compatible with the SEG Y data will be used.")
|
||||
|
||||
parser.add_argument("--null", type=float, default=0.0,
|
||||
help="Sample value to use for missing or short traces.")
|
||||
|
||||
if argv is None:
|
||||
argv = sys.argv[1:]
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
try:
|
||||
extract_timeslice(args.segy_file,
|
||||
args.npy_file,
|
||||
args.slice_index,
|
||||
args.dtype,
|
||||
args.null)
|
||||
except (FileNotFoundError, IsADirectoryError) as e:
|
||||
print(e, file=sys.stderr)
|
||||
return os.EX_NOINPUT
|
||||
except PermissionError as e:
|
||||
print(e, file=sys.stderr)
|
||||
return os.EX_NOPERM
|
||||
except Exception as e:
|
||||
traceback.print_exception(type(e), e, e.__traceback__, file=sys.stderr)
|
||||
return os.EX_SOFTWARE
|
||||
return os.EX_OK
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import struct
|
||||
|
||||
|
||||
def ibm2ieee2(ibm_float):
|
||||
"""
|
||||
ibm2ieee2(ibm_float)
|
||||
Used by permission
|
||||
(C) Secchi Angelo
|
||||
with thanks to Howard Lightstone and Anton Vredegoor.
|
||||
"""
|
||||
dividend = float(16 ** 6)
|
||||
|
||||
if ibm_float == 0:
|
||||
return 0.0
|
||||
istic, a, b, c = struct.unpack('>BBBB', ibm_float)
|
||||
if istic >= 128:
|
||||
sign = -1.0
|
||||
istic -= 128
|
||||
else:
|
||||
sign = 1.0
|
||||
mant = float(a << 16) + float(b << 8) + float(c)
|
||||
return sign * 16 ** (istic - 64) * (mant / dividend)
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/bin/sh
|
||||
VERSION=0.3
|
||||
|
||||
DIR=segypy-$VERSION
|
||||
|
||||
mkdir $DIR
|
||||
cp segypy.py $DIR/.
|
||||
cp testsegy.py $DIR/.
|
||||
cp LICENSE $DIR/.
|
||||
cp README $DIR/.
|
||||
|
||||
zip -r $DIR.zip $DIR
|
||||
tar cvfz $DIR.tgz $DIR
|
||||
|
||||
rm -fr $DIR
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
def image_segy(Data):
|
||||
"""
|
||||
imageSegy(Data)
|
||||
Image segy Data
|
||||
"""
|
||||
import pylab
|
||||
pylab.imshow(Data)
|
||||
pylab.title('pymat test')
|
||||
pylab.grid(True)
|
||||
pylab.show()
|
||||
|
||||
|
||||
def wiggle(Data, SH, skipt=1, maxval=8, lwidth=0.1):
|
||||
"""
|
||||
wiggle(Data, SH)
|
||||
"""
|
||||
import pylab
|
||||
|
||||
t = range(SH['ns'])
|
||||
|
||||
for i in range(0, SH['ntraces'], skipt):
|
||||
|
||||
trace = Data[:, i]
|
||||
trace[0] = 0
|
||||
trace[SH['ns'] - 1] = 0
|
||||
pylab.plot(i + trace / maxval, t, color='black', linewidth=lwidth)
|
||||
for a in range(len(trace)):
|
||||
if trace[a] < 0:
|
||||
trace[a] = 0
|
||||
|
||||
pylab.fill(i + Data[:, i] / maxval, t, 'k', linewidth=0)
|
||||
pylab.title(SH['filename'])
|
||||
pylab.grid(True)
|
||||
pylab.show()
|
||||
@@ -0,0 +1,20 @@
|
||||
Segpy-Numpy offers tools for working with both *Segpy* and *Numpy*. See the *Segpy* documentation for further details.
|
||||
|
||||
|
||||
What It Does
|
||||
============
|
||||
|
||||
How To Get It
|
||||
=============
|
||||
|
||||
*Segpy-Numpy* is available on the Python Package index and can be installed with ``pip``::
|
||||
|
||||
$ pip install segpy-numpy
|
||||
|
||||
|
||||
Requirements
|
||||
============
|
||||
|
||||
*Segpy-Numpy* should work with Python 3.2 and higher (and 2.7 for now). For the majority of use *Segpy 2* has no
|
||||
externaldependencies. Optional modules with further dependencies such as *Numpy* are included in the ``segpy.ext``
|
||||
package of extras.
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
print("segpy_numpy.__init__ imported")
|
||||
|
||||
from pkgutil import extend_path
|
||||
|
||||
#__path__ = extend_path(__path__, __name__)
|
||||
__version__ = '2.0.0a1'
|
||||
|
||||
|
||||
def load():
|
||||
pass
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
"""Optional interoperability with Numpy."""
|
||||
|
||||
import numpy
|
||||
|
||||
NUMPY_DTYPES = {'ibm': numpy.dtype('f4'),
|
||||
'int32': numpy.dtype('i4'),
|
||||
'int16': numpy.dtype('i2'),
|
||||
'float32': numpy.dtype('f4'),
|
||||
'int8': numpy.dtype('i1')}
|
||||
|
||||
|
||||
def make_dtype(data_sample_format):
|
||||
"""Convert a SEG Y data sample format to a compatible numpy dtype.
|
||||
|
||||
Note :
|
||||
IBM float data sample formats ('ibm') will correspond to IEEE float data types.
|
||||
|
||||
Args:
|
||||
data_sample_format: A data sample format string.
|
||||
|
||||
Returns:
|
||||
A numpy.dtype instance.
|
||||
|
||||
Raises:
|
||||
ValueError: For unrecognised data sample format strings.
|
||||
"""
|
||||
try:
|
||||
return NUMPY_DTYPES[data_sample_format]
|
||||
except KeyError:
|
||||
raise ValueError("Unknown data sample format string {!r}".format(data_sample_format))
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
[bdist_wheel]
|
||||
# This flag says that the code is written to work on both Python 2 and Python
|
||||
# 3. If at all possible, it is good practice to do this. If you cannot, you
|
||||
# will need to generate wheels for each Python version that you support.
|
||||
universal=1
|
||||
@@ -0,0 +1,127 @@
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
from setuptools import setup, find_packages # Always prefer setuptools over distutils
|
||||
from codecs import open # To use a consistent encoding
|
||||
from os import path
|
||||
|
||||
|
||||
def read(*names, **kwargs):
|
||||
with io.open(
|
||||
os.path.join(os.path.dirname(__file__), *names),
|
||||
encoding=kwargs.get("encoding", "utf8")
|
||||
) as fp:
|
||||
return fp.read()
|
||||
|
||||
|
||||
def find_version(*file_paths):
|
||||
version_file = read(*file_paths)
|
||||
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
|
||||
version_file, re.M)
|
||||
if version_match:
|
||||
return version_match.group(1)
|
||||
raise RuntimeError("Unable to find version string.")
|
||||
|
||||
|
||||
here = path.abspath(path.dirname(__file__))
|
||||
|
||||
|
||||
def local_file(name):
|
||||
return os.path.join(here, name)
|
||||
|
||||
|
||||
|
||||
# Get the long description from the relevant file
|
||||
with open(local_file('DESCRIPTION.rst'), encoding='utf-8') as f:
|
||||
long_description = f.read()
|
||||
|
||||
setup(
|
||||
name='segpy-numpy',
|
||||
|
||||
# Versions should comply with PEP440. For a discussion on single-sourcing
|
||||
# the version across setup.py and the project code, see
|
||||
# https://packaging.python.org/en/latest/single_source_version.html
|
||||
version=find_version("segpy_numpy/__init__.py"),
|
||||
|
||||
description='Interoperability between Numpy with Segpy.',
|
||||
long_description=long_description,
|
||||
|
||||
# The project's main homepage.
|
||||
url='https://github.com/rob-smallshire/segpy',
|
||||
|
||||
# Author details
|
||||
author='Robert Smallshire',
|
||||
author_email='robert@smallshire.org.uk',
|
||||
|
||||
# Choose your license
|
||||
license='GPL',
|
||||
|
||||
# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||
classifiers=[
|
||||
# How mature is this project? Common values are
|
||||
# 3 - Alpha
|
||||
# 4 - Beta
|
||||
# 5 - Production/Stable
|
||||
'Development Status :: 3 - Alpha',
|
||||
|
||||
# Indicate who your project is intended for
|
||||
'Intended Audience :: Developers',
|
||||
'Topic :: Scientific/Engineering',
|
||||
'Topic :: Software Development :: Libraries',
|
||||
|
||||
# Pick your license as you wish (should match "license" above)
|
||||
'License :: OSI Approved :: MIT License',
|
||||
|
||||
# Specify the Python versions you support here. In particular, ensure
|
||||
# that you indicate whether you support Python 2, Python 3 or both.
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
],
|
||||
|
||||
# What does your project relate to?
|
||||
keywords='seismic geocomputing geophysics numpy',
|
||||
|
||||
# You can just specify the packages manually here if your project is
|
||||
# simple. Or you can use find_packages().
|
||||
packages=find_packages(here, exclude=['segpy', 'contrib', 'docs', 'test*']),
|
||||
|
||||
# List run-time dependencies here. These will be installed by pip when your
|
||||
# project is installed. For an analysis of "install_requires" vs pip's
|
||||
# requirements files see:
|
||||
# https://packaging.python.org/en/latest/requirements.html
|
||||
install_requires=['numpy'],
|
||||
|
||||
# List additional groups of dependencies here (e.g. development dependencies).
|
||||
# You can install these using the following syntax, for example:
|
||||
# $ pip install -e .[dev,test]
|
||||
extras_require = {
|
||||
'dev': ['check-manifest', 'wheel'],
|
||||
'doc': ['sphinx', 'cartouche'],
|
||||
'test': ['coverage', 'hypothesis'],
|
||||
},
|
||||
|
||||
# If there are data files included in your packages that need to be
|
||||
# installed, specify them here. If using Python 2.6 or less, then these
|
||||
# have to be included in MANIFEST.in as well.
|
||||
package_data={
|
||||
},
|
||||
|
||||
# Although 'package_data' is the preferred approach, in some case you may
|
||||
# need to place data files outside of your packages.
|
||||
# see http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files
|
||||
# In this case, 'data_file' will be installed into '<sys.prefix>/my_data'
|
||||
data_files=[],
|
||||
|
||||
# To provide executable scripts, use entry points in preference to the
|
||||
# "scripts" keyword. Entry points provide cross-platform support and allow
|
||||
# pip to create the appropriate form of executable for the target platform.
|
||||
|
||||
# Declare entry-points to modules.
|
||||
entry_points={
|
||||
'segpy.ext': 'segpy-numpy = segpy_numpy'
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
__version__ = '2.0.0a1'
|
||||
@@ -2,7 +2,7 @@
|
||||
SEG Y Header Definition
|
||||
"""
|
||||
|
||||
from revisions import SEGY_REVISION_0, SEGY_REVISION_1
|
||||
from segpy.revisions import SEGY_REVISION_0, SEGY_REVISION_1
|
||||
|
||||
HEADER_DEF = {"Job": {"pos": 3200, "type": "int32", "def": 0}}
|
||||
HEADER_DEF["Line"] = {"pos": 3204, "type": "int32", "def": 0}
|
||||
@@ -47,13 +47,13 @@ HEADER_DEF["DataSampleFormat"]["datatype"][SEGY_REVISION_1] = {
|
||||
1: 'ibm',
|
||||
2: 'l',
|
||||
3: 'h',
|
||||
# 5: 'float',
|
||||
5: 'f',
|
||||
8: 'B'}
|
||||
|
||||
HEADER_DEF["EnsembleFold"] = {"pos": 3226, "type": "int16", "def": 0}
|
||||
HEADER_DEF["TraceSorting"] = {"pos": 3228, "type": "int16", "def": 0}
|
||||
HEADER_DEF["VerticalSumCode"] = {"pos": 3230, "type": "int16", "def": 0}
|
||||
HEADER_DEF["SweepFrequencyStart"] = {"pos": 3232, "type": "int16", "def": 0}
|
||||
HEADER_DEF["SweepFrequencyEnd"] = {"pos": 3234, "type": "int16", "def": 0}
|
||||
HEADER_DEF["SweepLength"] = {"pos": 3236, "type": "int16", "def": 0}
|
||||
HEADER_DEF["SweepType"] = {"pos": 3238, "type": "int16", "def": 0}
|
||||
@@ -0,0 +1,48 @@
|
||||
def lfu_cache(maxsize=100):
|
||||
'''Least-frequently-used cache decorator.
|
||||
|
||||
Arguments to the cached function must be hashable.
|
||||
Cache performance statistics stored in f.hits and f.misses.
|
||||
Clear the cache with f.clear().
|
||||
http://en.wikipedia.org/wiki/Least_Frequently_Used
|
||||
|
||||
'''
|
||||
def decorating_function(user_function):
|
||||
cache = {} # mapping of args to results
|
||||
use_count = Counter() # times each key has been accessed
|
||||
kwd_mark = object() # separate positional and keyword args
|
||||
|
||||
@functools.wraps(user_function)
|
||||
def wrapper(*args, **kwds):
|
||||
key = args
|
||||
if kwds:
|
||||
key += (kwd_mark,) + tuple(sorted(kwds.items()))
|
||||
use_count[key] += 1
|
||||
|
||||
# get cache entry or compute if not found
|
||||
try:
|
||||
result = cache[key]
|
||||
wrapper.hits += 1
|
||||
except KeyError:
|
||||
result = user_function(*args, **kwds)
|
||||
cache[key] = result
|
||||
wrapper.misses += 1
|
||||
|
||||
# purge least frequently used cache entry
|
||||
if len(cache) > maxsize:
|
||||
for key, _ in nsmallest(maxsize // 10,
|
||||
use_count.iteritems(),
|
||||
key=itemgetter(1)):
|
||||
del cache[key], use_count[key]
|
||||
|
||||
return result
|
||||
|
||||
def clear():
|
||||
cache.clear()
|
||||
use_count.clear()
|
||||
wrapper.hits = wrapper.misses = 0
|
||||
|
||||
wrapper.hits = wrapper.misses = 0
|
||||
wrapper.clear = clear
|
||||
return wrapper
|
||||
return decorating_function
|
||||
@@ -0,0 +1,588 @@
|
||||
from abc import abstractmethod, ABCMeta
|
||||
from collections import Mapping, Sequence, OrderedDict
|
||||
from fractions import Fraction
|
||||
|
||||
from segpy.portability import reprlib
|
||||
from segpy.util import contains_duplicates, measure_stride, minmax
|
||||
|
||||
|
||||
class CatalogBuilder(object):
|
||||
"""Use a catalog builder to construct optimised, immutable mappings.
|
||||
|
||||
A CatalogBuilder is useful when, depending on the particular keys and
|
||||
values used, a more compact or efficient representation of the mapping
|
||||
is possible than, say, a regular dictionary. The CatalogBuilder
|
||||
accumulates values and then, once all values have been added, analyzes
|
||||
the keys and values to produce a more optimized representation of the
|
||||
mapping.
|
||||
"""
|
||||
|
||||
def __init__(self, mapping=None):
|
||||
"""Initialize a Catalog Builder.
|
||||
|
||||
Args:
|
||||
mapping: An optional mapping (such as a dictionary) of items.
|
||||
"""
|
||||
self._catalog = []
|
||||
if mapping is not None:
|
||||
for key, value in mapping.items():
|
||||
self.add(key, value)
|
||||
|
||||
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.
|
||||
|
||||
"""
|
||||
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.
|
||||
|
||||
Returns:
|
||||
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.
|
||||
|
||||
if len(self._catalog) < 2:
|
||||
return DictionaryCatalog(self._catalog)
|
||||
|
||||
# In-place sort by index
|
||||
self._catalog.sort(key=lambda index_value: index_value[0])
|
||||
|
||||
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):
|
||||
return self._create_catalog_2()
|
||||
|
||||
return self._create_catalog_1()
|
||||
|
||||
def _create_catalog_1(self):
|
||||
"""Create a catalog for one-dimensional integer keys (i.e. scalars)
|
||||
"""
|
||||
index_min = self._catalog[0][0]
|
||||
index_max = self._catalog[-1][0]
|
||||
index_stride = measure_stride(index for index, value in self._catalog)
|
||||
|
||||
if index_stride is None:
|
||||
# Dictionary strategy - arbitrary keys and values
|
||||
return DictionaryCatalog(self._catalog)
|
||||
|
||||
self._catalog.sort(key=lambda index_value: index_value[1])
|
||||
value_min = self._catalog[0][1]
|
||||
value_max = self._catalog[-1][1]
|
||||
value_stride = measure_stride(value for index, value in self._catalog)
|
||||
|
||||
if index_stride is not None and value_stride == 0:
|
||||
assert value_min == value_max
|
||||
return RegularConstantCatalog(index_min,
|
||||
index_max,
|
||||
index_stride,
|
||||
value_min)
|
||||
|
||||
if index_stride is None and value_stride == 0:
|
||||
assert value_min == value_max
|
||||
return ConstantCatalog(
|
||||
(index for index, value in self._catalog),
|
||||
value_min)
|
||||
|
||||
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))
|
||||
|
||||
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)
|
||||
return catalog
|
||||
|
||||
def _create_catalog_2(self):
|
||||
"""Create a catalog for two-dimensional integer keys.
|
||||
|
||||
Each key must be a two-element sequence.
|
||||
"""
|
||||
i_min, i_max = minmax(i for (i, j), value in self._catalog)
|
||||
j_min, j_max = minmax(j for (i, j), value in self._catalog)
|
||||
|
||||
is_rm, diff = self._is_row_major(i_min, j_min, j_max)
|
||||
if is_rm:
|
||||
return RowMajorCatalog(i_min, i_max, j_min, j_max, diff)
|
||||
return DictionaryCatalog(self._catalog)
|
||||
|
||||
def _is_row_major(self, i_min, j_min, j_max):
|
||||
"""Does row major ordering predict values from keys?
|
||||
|
||||
In row-major order the last dimension is contiguous, and so changes
|
||||
quickest, when moving through the elements in storage order. Hence
|
||||
the number of rows is the number of distinct i values and the numbers
|
||||
of elements in each row (i.e. columns) is the number of distinct j.
|
||||
|
||||
Args:
|
||||
i_min: The minimum i value.
|
||||
j_min: The minimum j value.
|
||||
j_max: The maximum j value.
|
||||
|
||||
Returns:
|
||||
A 2-tuple containing, in the first element True if the values can
|
||||
be predicted from the keys by assuming a row-major ordering,
|
||||
otherwise False. If True, the second element will be a constant
|
||||
offset, otherwise it can be ignored.
|
||||
"""
|
||||
diff = None
|
||||
for (i, j), actual_value in self._catalog:
|
||||
proposed_value = (i - i_min) * (j_max + 1 - j_min) + (j - j_min)
|
||||
current_diff = actual_value - proposed_value
|
||||
if diff is None:
|
||||
diff = current_diff
|
||||
if current_diff != diff:
|
||||
return False, None
|
||||
return True, diff
|
||||
|
||||
|
||||
class Catalog(Mapping):
|
||||
"""An abstract base class for Catalogs which provides min and max keys and values."""
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
@abstractmethod
|
||||
def __init__(self, key_min=None, key_max=None, value_min=None, value_max=None):
|
||||
"""Must be overridden and called by subclasses.
|
||||
|
||||
Args:
|
||||
key_min: Optional minimum key.
|
||||
key_max: Optional maximum key.
|
||||
value_min: Optional minimum value.
|
||||
value_max: Optional maximum value.
|
||||
"""
|
||||
self._min_key = key_min
|
||||
self._max_key = key_max
|
||||
self._min_value = value_min
|
||||
self._max_value = value_max
|
||||
|
||||
def key_min(self):
|
||||
"""Minimum key"""
|
||||
if self._min_key is None:
|
||||
self._min_key = min(self.keys())
|
||||
return self._min_key
|
||||
|
||||
def key_max(self):
|
||||
"""Maximum key"""
|
||||
if self._max_key is None:
|
||||
self._max_key = max(self.keys())
|
||||
return self._max_key
|
||||
|
||||
def value_min(self):
|
||||
"""Minimum value"""
|
||||
if self._min_value is None:
|
||||
self._min_value = min(self.values())
|
||||
return self._min_value
|
||||
|
||||
def value_max(self):
|
||||
"""Maximum value"""
|
||||
if self._max_value is None:
|
||||
self._max_value = max(self.values())
|
||||
return self._max_value
|
||||
|
||||
|
||||
class RowMajorCatalog(Catalog):
|
||||
"""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.
|
||||
|
||||
A RowMajorCatalog predicts the value v from the key (i, j) according to the
|
||||
following formula:
|
||||
|
||||
v = (i - i_min) * j_max + (j - j_min) + c
|
||||
|
||||
for
|
||||
i_min <= i <= i_max
|
||||
j_min <= j <= j_max
|
||||
|
||||
and where c is an integer constant to allow zero- or one-based indexing.
|
||||
"""
|
||||
|
||||
def __init__(self, i_min, i_max, j_min, j_max, c):
|
||||
"""Initialize a RowMajorCatalog.
|
||||
|
||||
Args:
|
||||
i_min (int): The minimum i value.
|
||||
i_max (int): The maximum i value.
|
||||
j_min (int): The minimum j value.
|
||||
j_max (int): The maximum j value.
|
||||
c (int): The constant offset
|
||||
"""
|
||||
super(RowMajorCatalog, self).__init__()
|
||||
self._i_min = i_min
|
||||
self._i_max = i_max
|
||||
self._j_min = j_min
|
||||
self._j_max = j_max
|
||||
self._c = c
|
||||
|
||||
@property
|
||||
def i_min(self):
|
||||
"""Minimum i value"""
|
||||
return self._i_min
|
||||
|
||||
@property
|
||||
def i_max(self):
|
||||
"""Maximum i value"""
|
||||
return self._i_max
|
||||
|
||||
@property
|
||||
def j_min(self):
|
||||
"""Minimum j value"""
|
||||
return self._j_min
|
||||
|
||||
@property
|
||||
def j_max(self):
|
||||
"""Maximum j value"""
|
||||
return self._j_max
|
||||
|
||||
def key_min(self):
|
||||
"""Minimum (i, j) key"""
|
||||
return self._i_min, self._j_min
|
||||
|
||||
def key_max(self):
|
||||
"""Maximum (i, j) key"""
|
||||
return self._i_max, self._j_max
|
||||
|
||||
def value_min(self):
|
||||
"""Minimum value at key_min"""
|
||||
return self[self.key_min()]
|
||||
|
||||
def value_max(self):
|
||||
"""Maximum value at key_max"""
|
||||
return self[self.key_max()]
|
||||
|
||||
def __getitem__(self, key):
|
||||
i, j = key
|
||||
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 + 1 - self._j_min) + (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)
|
||||
|
||||
def __len__(self):
|
||||
return (self._i_max - self._i_min) * (self._j_max + 1 - self._j_min)
|
||||
|
||||
def __iter__(self):
|
||||
for i in range(self._i_min, self._i_max + 1):
|
||||
for j in range(self._j_min, self._j_max + 1):
|
||||
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)
|
||||
|
||||
|
||||
class DictionaryCatalog(Catalog):
|
||||
"""An immutable, ordered, dictionary mapping.
|
||||
"""
|
||||
|
||||
def __init__(self, items):
|
||||
super(DictionaryCatalog, self).__init__()
|
||||
self._items = OrderedDict(items)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self._items[key]
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._items)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._items)
|
||||
|
||||
def __contains__(self, item):
|
||||
return item in self._items
|
||||
|
||||
def __repr__(self):
|
||||
return '{}({})'.format(
|
||||
self.__class__.__name__, reprlib.repr(self._items.items()))
|
||||
|
||||
|
||||
class RegularConstantCatalog(Catalog):
|
||||
"""Mapping with keys ordered with regular spacing along the number line.
|
||||
|
||||
The values associated with the keys are constant.
|
||||
"""
|
||||
|
||||
def __init__(self, key_min, key_max, key_stride, value):
|
||||
"""Initialize a RegularConstantCatalog.
|
||||
|
||||
The catalog is initialized by a description of how the keys
|
||||
are distributed along the number line, and a value which
|
||||
corresponds with all keys.
|
||||
|
||||
Args:
|
||||
key_min: The minimum key.
|
||||
key_max: The maximum key.
|
||||
key_stride: The difference between successive keys.
|
||||
value: A value associated with all keys.
|
||||
"""
|
||||
key_range = key_max - key_min
|
||||
if key_range % key_stride != 0:
|
||||
raise ValueError("RegularIndex key range {!r} is not "
|
||||
"a multiple of stride {!r}".format(
|
||||
key_stride, key_range))
|
||||
super(RegularConstantCatalog, self).__init__(
|
||||
key_min=key_min,
|
||||
key_max=key_max,
|
||||
value_min=value,
|
||||
value_max=value)
|
||||
self._key_stride = key_stride
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key not in self:
|
||||
raise KeyError("{!r} does not contain key {!r}".format(self, key))
|
||||
return self.value_min()
|
||||
|
||||
def __len__(self):
|
||||
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)
|
||||
|
||||
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())
|
||||
|
||||
|
||||
class ConstantCatalog(Catalog):
|
||||
"""Mapping with arbitrary keys and a single constant value.
|
||||
"""
|
||||
|
||||
def __init__(self, keys, value):
|
||||
"""Initialize a RegularConstantCatalog.
|
||||
|
||||
The catalog is initialized by a description with an iterable series of
|
||||
keys and a constant value to be associated with all the keys.
|
||||
|
||||
Args:
|
||||
keys: An iterable series of distinct keys.
|
||||
key_max: The maximum key.
|
||||
key_stride: The difference between successive keys.
|
||||
value: A value associated with all keys.
|
||||
"""
|
||||
super(ConstantCatalog, self).__init__(value_min=value, value_max=value)
|
||||
self._items = frozenset(keys)
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key not in self:
|
||||
raise KeyError("{!r} does not contain key {!r}".format(self, key))
|
||||
return self.value_min()
|
||||
|
||||
def __len__(self):
|
||||
return len(self._items)
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self._items
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._items)
|
||||
|
||||
def __repr__(self):
|
||||
return '{}({}, {})'.format(
|
||||
self.__class__.__name__,
|
||||
reprlib.repr(self._items),
|
||||
self.value_min())
|
||||
|
||||
|
||||
class RegularCatalog(Catalog):
|
||||
"""Mapping with keys ordered with regular spacing along the number line.
|
||||
|
||||
The values associated with the keys are arbitrary.
|
||||
"""
|
||||
|
||||
def __init__(self, key_min, key_max, key_stride, values):
|
||||
"""Initialize a RegularCatalog.
|
||||
|
||||
The catalog is initialized by a description of how the keys
|
||||
are distributed along the number line, and an iterable series of
|
||||
corresponding values.
|
||||
|
||||
Args:
|
||||
key_min: The minimum key.
|
||||
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 ValueError("{} key range {!r} is not "
|
||||
"a multiple of stride {!r}".format(self.__class__.__name__,
|
||||
key_stride, key_range))
|
||||
super(RegularCatalog, self).__init__(key_min=key_min, key_max=key_max)
|
||||
self._key_stride = key_stride
|
||||
self._values = list(values)
|
||||
num_keys = key_range // key_stride
|
||||
if num_keys != len(self._values):
|
||||
raise ValueError("{} key range and values inconsistent".format(self.__class__.__name__))
|
||||
|
||||
def __getitem__(self, key):
|
||||
if not (self.key_min() <= key <= self.key_max()):
|
||||
raise KeyError("{!r} key {!r} out of range".format(self, key))
|
||||
offset = key - self.key_min()
|
||||
if offset % self._key_stride != 0:
|
||||
raise KeyError("{!r} does not contain key {!r}".format(self, key))
|
||||
index = offset // self._key_stride
|
||||
return self._values[index]
|
||||
|
||||
def __len__(self):
|
||||
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)
|
||||
|
||||
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,
|
||||
reprlib.repr(self._values))
|
||||
|
||||
|
||||
class LinearRegularCatalog(Catalog):
|
||||
"""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.
|
||||
|
||||
A LinearRegularCatalog predicts the value v from the key according to the
|
||||
following formula:
|
||||
|
||||
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):
|
||||
"""Initialize a LinearRegularCatalog.
|
||||
|
||||
Args:
|
||||
key_min: The minimum key.
|
||||
key_max: The maximum key.
|
||||
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 ValueError("{} key range {!r} is not "
|
||||
"a multiple of key stride {!r}".format(
|
||||
self.__class__.__name__,
|
||||
key_stride,
|
||||
key_range))
|
||||
self._key_stride = key_stride
|
||||
|
||||
value_range = value_max - value_min
|
||||
if value_range % value_stride != 0:
|
||||
raise ValueError("{} value range {!r} is not "
|
||||
"a multiple of value stride {!r}".format(
|
||||
self.__class__.__name__,
|
||||
value_stride,
|
||||
value_range))
|
||||
self._value_stride = value_stride
|
||||
|
||||
super(LinearRegularCatalog, self).__init__(
|
||||
key_min=key_min,
|
||||
key_max=key_max,
|
||||
value_min=value_min,
|
||||
value_max=value_max)
|
||||
|
||||
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 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())
|
||||
|
||||
def __getitem__(self, key):
|
||||
if not (self.key_min() <= key <= self.key_max()):
|
||||
raise KeyError("{!r} key {!r} out of range".format(self, key))
|
||||
offset = key - self.key_min()
|
||||
if offset % self._key_stride != 0:
|
||||
raise KeyError("{!r} does not contain key {!r}".format(self, key))
|
||||
|
||||
v = self._m * (key - self.key_min()) + self.value_min()
|
||||
assert v.denominator == 1
|
||||
return v.numerator
|
||||
|
||||
def __len__(self):
|
||||
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)
|
||||
|
||||
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)
|
||||
@@ -0,0 +1,39 @@
|
||||
DATA_SAMPLE_FORMAT = {1: 'ibm',
|
||||
2: 'int32',
|
||||
3: 'int16',
|
||||
5: 'float32',
|
||||
8: 'int8'}
|
||||
|
||||
# A mapping from SEG Y data types to format characters used by the struct module
|
||||
CTYPES = {'int32': 'i',
|
||||
'uint32': 'I',
|
||||
'int16': 'h',
|
||||
'uint16': 'H',
|
||||
'int8': 'b',
|
||||
'uint8': 'B',
|
||||
'float32': 'f',
|
||||
'ibm': 'ibm'}
|
||||
|
||||
|
||||
CTYPE_DESCRIPTION = {'ibm': 'IBM float',
|
||||
'int32': '32 bit signed integer',
|
||||
'uint32': '32 bit unsigned integer',
|
||||
'int16': '16 bit signed integer',
|
||||
'uint16': '16 bit unsigned integer',
|
||||
'float32': 'IEEE float32',
|
||||
'int8': '8 bit signed integer (byte)',
|
||||
'uint8': '8 bit unsigned integer (byte)'}
|
||||
|
||||
|
||||
SIZES = dict(i=4,
|
||||
I=4,
|
||||
h=2,
|
||||
H=2,
|
||||
b=1,
|
||||
B=1,
|
||||
f=4,
|
||||
ibm=4)
|
||||
|
||||
|
||||
def size_in_bytes(ctype):
|
||||
return SIZES[ctype]
|
||||
@@ -0,0 +1,78 @@
|
||||
ASCII = 'ascii'
|
||||
EBCDIC = 'cp037'
|
||||
|
||||
SUPPORTED_ENCODINGS = (ASCII, EBCDIC)
|
||||
|
||||
|
||||
class UnsupportedEncodingError(Exception):
|
||||
|
||||
def __init__(self, text, encoding):
|
||||
self._encoding = encoding
|
||||
super(UnsupportedEncodingError, self).__init__(text)
|
||||
|
||||
@property
|
||||
def encoding(self):
|
||||
return self._encoding
|
||||
|
||||
def __str__(self):
|
||||
return "{} not supported for encoding {}".format(self.args[0], self._encoding)
|
||||
|
||||
def __repr__(self):
|
||||
return "{}({!r}, {!r}".format(self.__class__.__name__, self.args[0], self._encoding)
|
||||
|
||||
|
||||
def is_supported_encoding(encoding):
|
||||
return encoding in SUPPORTED_ENCODINGS
|
||||
|
||||
|
||||
COMMON_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:_- '
|
||||
COMMON_EBCDIC_CHARS = set(COMMON_CHARS.encode(EBCDIC))
|
||||
COMMON_ASCII_CHARS = set(COMMON_CHARS.encode(ASCII))
|
||||
|
||||
def guess_encoding(bs, threshold=0.5):
|
||||
"""Try to determine whether the encoding of byte stream b is an ASCII string or an EBCDIC string.
|
||||
|
||||
Args:
|
||||
bs: A byte string (Python 2 - str; Python 3 - bytes)
|
||||
|
||||
Returns:
|
||||
A string which can be used with the Python encoding functions: 'cp037' for EBCDIC, 'ascii' for ASCII or None
|
||||
if neither.
|
||||
"""
|
||||
|
||||
ebcdic_count = 0
|
||||
ascii_count = 0
|
||||
null_count = 0
|
||||
|
||||
count = 0
|
||||
for b in bs:
|
||||
if b in COMMON_EBCDIC_CHARS:
|
||||
ebcdic_count +=1
|
||||
if b in COMMON_ASCII_CHARS:
|
||||
ascii_count +=1
|
||||
if b == 0:
|
||||
null_count += 1
|
||||
count += 1
|
||||
|
||||
if count == 0:
|
||||
return None
|
||||
|
||||
ebcdic_freq = ebcdic_count / count
|
||||
ascii_freq = ascii_count / count
|
||||
null_freq = null_count / count
|
||||
|
||||
if null_freq == 1.0:
|
||||
return ASCII # Doesn't matter
|
||||
|
||||
if ebcdic_freq < threshold and ascii_freq >= threshold:
|
||||
return ASCII
|
||||
|
||||
if ebcdic_freq >= threshold and ascii_freq < threshold:
|
||||
return EBCDIC
|
||||
|
||||
if ebcdic_freq < threshold and ascii_freq < threshold:
|
||||
return None
|
||||
|
||||
|
||||
|
||||
return None
|
||||
@@ -0,0 +1,20 @@
|
||||
import pkg_resources
|
||||
|
||||
loaded = set()
|
||||
|
||||
|
||||
def load_entry_points(name=None):
|
||||
"""Load extension packages into the segpy.ext namespace.
|
||||
|
||||
Any packages registered against the 'segpy.ext' entry-point group will be
|
||||
installed dynamically into the segpy.ext namespace.
|
||||
"""
|
||||
for entry_point in pkg_resources.iter_entry_points(group='segpy.ext', name=name):
|
||||
package = entry_point.load()
|
||||
if package not in loaded:
|
||||
loaded.add(package)
|
||||
__path__.extend(package.__path__)
|
||||
package.load()
|
||||
|
||||
|
||||
load_entry_points()
|
||||
@@ -0,0 +1,491 @@
|
||||
from math import frexp, isnan, isinf, ceil, floor, trunc
|
||||
from numbers import Real
|
||||
|
||||
from segpy.portability import long_int, byte_string, four_bytes
|
||||
|
||||
|
||||
|
||||
IBM_ZERO_BYTES = b'\x00\x00\x00\x00'
|
||||
IBM_NEGATIVE_ONE_BYTES = b'\xc1\x10\x00\x00'
|
||||
IBM_POSITIVE_ONE_BYTES = b'A\x10\x00\x00'
|
||||
|
||||
MIN_IBM_FLOAT = -7.2370051459731155e+75
|
||||
LARGEST_NEGATIVE_NORMAL_IBM_FLOAT = -5.397605346934028e-79
|
||||
SMALLEST_POSITIVE_NORMAL_IBM_FLOAT = 5.397605346934028e-79
|
||||
MAX_IBM_FLOAT = 7.2370051459731155e+75
|
||||
|
||||
MAX_BITS_PRECISION_IBM_FLOAT = 24
|
||||
MIN_BITS_PRECISION_IBM_FLOAT = 21 # The first 3 bits of the mantissa may be zero
|
||||
EPSILON_IBM_FLOAT = pow(2.0, -(MIN_BITS_PRECISION_IBM_FLOAT - 1))
|
||||
_L24 = long_int(2) ** MAX_BITS_PRECISION_IBM_FLOAT
|
||||
_F24 = float(pow(2, MAX_BITS_PRECISION_IBM_FLOAT))
|
||||
|
||||
_L21 = long_int(2) ** MIN_BITS_PRECISION_IBM_FLOAT
|
||||
|
||||
EXPONENT_BIAS = 64
|
||||
|
||||
MIN_EXACT_INTEGER_IBM_FLOAT = -2**MAX_BITS_PRECISION_IBM_FLOAT
|
||||
MAX_EXACT_INTEGER_IBM_FLOAT = 2**MIN_BITS_PRECISION_IBM_FLOAT
|
||||
|
||||
|
||||
def ibm2ieee(big_endian_bytes):
|
||||
"""Interpret a byte string as a big-endian IBM float.
|
||||
|
||||
Args:
|
||||
big_endian_bytes (str): A string containing at least four bytes.
|
||||
|
||||
Returns:
|
||||
The floating point value.
|
||||
"""
|
||||
a, b, c, d = four_bytes(big_endian_bytes)
|
||||
|
||||
if a == b == c == d == 0:
|
||||
return 0.0
|
||||
|
||||
sign = -1 if (a & 0x80) else 1
|
||||
exponent_16_biased = a & 0x7f
|
||||
mantissa = ((b << 16) | (c << 8) | d) / _F24
|
||||
|
||||
value = sign * mantissa * pow(16, exponent_16_biased - EXPONENT_BIAS)
|
||||
return value
|
||||
|
||||
BITS_PER_NYBBLE = 4
|
||||
|
||||
|
||||
def truncate(big_endian_bytes):
|
||||
a, b, c, d = four_bytes(big_endian_bytes)
|
||||
|
||||
sign = -1 if (a & 0x80) else 1
|
||||
exponent_16_biased = a & 0x7f
|
||||
exponent_16 = exponent_16_biased - EXPONENT_BIAS
|
||||
mantissa = ((b << 16) | (c << 8) | d)
|
||||
|
||||
print("sign =", sign)
|
||||
print("exponent_16", exponent_16, hex(exponent_16), bin(exponent_16))
|
||||
print("mantissa", mantissa, hex(mantissa), bin(mantissa))
|
||||
|
||||
num_nybbles_to_preserve = min(exponent_16, MAX_BITS_PRECISION_IBM_FLOAT // BITS_PER_NYBBLE)
|
||||
num_bits_to_clear = MAX_BITS_PRECISION_IBM_FLOAT - num_nybbles_to_preserve * BITS_PER_NYBBLE
|
||||
clear_mask = 2**num_bits_to_clear - 1
|
||||
preserve_mask = (2**MAX_BITS_PRECISION_IBM_FLOAT - 1) & ~clear_mask
|
||||
|
||||
print("num_nybbles_to_preserve", num_nybbles_to_preserve)
|
||||
print("num_bits_to_clear", num_bits_to_clear)
|
||||
print("clear_mask ", bin(clear_mask))
|
||||
print("preserve_mask", bin(preserve_mask))
|
||||
|
||||
truncated_mantissa = mantissa & preserve_mask
|
||||
|
||||
value = truncated_mantissa * pow(16, exponent_16)
|
||||
|
||||
scaled_value = value >> MAX_BITS_PRECISION_IBM_FLOAT
|
||||
|
||||
return scaled_value
|
||||
|
||||
#
|
||||
# tb = (preserve_mask >> 16) & b
|
||||
# tc = (preserve_mask >> 8) & c
|
||||
# td = preserve_mask & d
|
||||
#
|
||||
# return byte_string((a, tb, tc, td))
|
||||
|
||||
|
||||
def ieee2ibm(f):
|
||||
"""Convert a float to four big-endian bytes representing an IBM float.
|
||||
|
||||
Args:
|
||||
f (float): The value to be converted.
|
||||
|
||||
Returns:
|
||||
A bytes object (Python 3) or a string (Python 2) containing four
|
||||
bytes representing a big-endian IBM float.
|
||||
|
||||
Raises:
|
||||
OverflowError: If f is outside the representable range.
|
||||
ValueError: If f is NaN or infinite.
|
||||
FloatingPointError: If f cannot be represented without total loss of precision.
|
||||
"""
|
||||
if f == 0:
|
||||
# There are many potential representations of zero - this is the standard one
|
||||
return b'\x00\x00\x00\x00'
|
||||
|
||||
if isnan(f):
|
||||
raise ValueError("NaN cannot be represented in IBM floating point")
|
||||
|
||||
if isinf(f):
|
||||
raise ValueError("Infinities cannot be represented in IBM floating point")
|
||||
|
||||
if f < MIN_IBM_FLOAT:
|
||||
raise OverflowError("IEEE Floating point value {} is less than the "
|
||||
"representable minimum for IBM floats.".format(f))
|
||||
|
||||
if f > MAX_IBM_FLOAT:
|
||||
raise OverflowError("IEEE Floating point value {} is greater than the "
|
||||
"representable maximum for IBM floats".format(f))
|
||||
|
||||
# Now compute m and e to satisfy:
|
||||
#
|
||||
# f = m * 2^e
|
||||
#
|
||||
# where 0.5 <= abs(m) < 1
|
||||
# except when f == 0 in which case m == 0 and e == 0, which we've already
|
||||
# dealt with.
|
||||
m, e = frexp(f)
|
||||
|
||||
# Convert the fraction (m) into an integer representation. IEEE float32
|
||||
# numbers have 23 explicit (24 implicit) bits of precision.
|
||||
|
||||
mantissa = abs(long_int(m * _L24))
|
||||
exponent = e
|
||||
sign = 0x80 if f < 0 else 0x00
|
||||
|
||||
# IBM single precision floats are of the form
|
||||
# (-1)^sign * 0.significand * 16^(exponent-64)
|
||||
|
||||
# Adjust the exponent, and the mantissa in sympathy so it is
|
||||
# a multiple of four, so it can be expressed in base 16
|
||||
remainder = exponent % 4
|
||||
if remainder != 0:
|
||||
shift = 4 - remainder
|
||||
mantissa >>= shift
|
||||
exponent += shift
|
||||
|
||||
exponent_16 = exponent >> 2 # Divide by four to convert to base 16
|
||||
exponent_16_biased = exponent_16 + 64 # Add the exponent bias of 64
|
||||
|
||||
# If the biased exponent is negative, we try to use a subnormal representation
|
||||
if exponent_16_biased < 0:
|
||||
shift_16 = 0 - exponent_16_biased
|
||||
exponent_16_biased += shift_16 # An increment of the base-16 exponent must be balanced by
|
||||
mantissa >>= 4 * shift_16 # A division by 16 (four binary places) in the mantissa
|
||||
if mantissa == 0:
|
||||
raise FloatingPointError("IEEE Floating point value {} is smaller than the "
|
||||
"smallest subnormal number for IBM floats.".format(f))
|
||||
|
||||
a = sign | exponent_16_biased
|
||||
b = (mantissa >> 16) & 0xff
|
||||
c = (mantissa >> 8) & 0xff
|
||||
d = mantissa & 0xff
|
||||
|
||||
return byte_string((a, b, c, d))
|
||||
|
||||
|
||||
class IBMFloat(Real):
|
||||
|
||||
__slots__ = ['_data']
|
||||
|
||||
_INTERNED = {IBM_ZERO_BYTES: None,
|
||||
IBM_NEGATIVE_ONE_BYTES: None,
|
||||
IBM_POSITIVE_ONE_BYTES: None}
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
def __new__(cls, b):
|
||||
obj = object.__new__(cls)
|
||||
|
||||
data = bytes(b)
|
||||
num_bytes = len(data)
|
||||
if num_bytes != 4:
|
||||
raise ValueError("{} cannot be constructed from {} values".format(cls.__name__, num_bytes))
|
||||
obj._data = data
|
||||
|
||||
# Intern common values
|
||||
if data in cls._INTERNED:
|
||||
if cls._INTERNED[data] is None:
|
||||
cls._INTERNED[data] = obj
|
||||
return cls._INTERNED[data]
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_float(cls, f):
|
||||
"""Construct an IBMFloat from an IEEE float.
|
||||
|
||||
Args:
|
||||
f (float): The value to be converted.
|
||||
|
||||
Returns:
|
||||
An IBMFloat.
|
||||
|
||||
Raises:
|
||||
OverflowError: If f is outside the representable range.
|
||||
ValueError: If f is NaN or infinite.
|
||||
FloatingPointError: If f cannot be represented without total loss of precision.
|
||||
"""
|
||||
return cls(ieee2ibm(f))
|
||||
|
||||
@classmethod
|
||||
def from_real(cls, f):
|
||||
if isinstance(f, IBMFloat):
|
||||
return f
|
||||
return cls.from_float(f)
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, b):
|
||||
return cls(b)
|
||||
|
||||
@classmethod
|
||||
def ldexp(cls, fraction, exponent):
|
||||
"""Make an IBMFloat from fraction and exponent.
|
||||
|
||||
The is the inverse function of IBMFloat.frexp()
|
||||
|
||||
Args:
|
||||
fraction: A Real in the range -1.0 to 1.0.
|
||||
exponent: An integer in the range -256 to 255 inclusive.
|
||||
"""
|
||||
if not (-1.0 <= fraction <= 1.0):
|
||||
raise ValueError("ldexp fraction {!r} out of range -1.0 to +1.0")
|
||||
|
||||
if not (-256 <= exponent < 256):
|
||||
raise ValueError("ldexp exponent {!r} out of range -256 to 256")
|
||||
|
||||
ieee = fraction * 2**exponent
|
||||
return IBMFloat.from_float(ieee)
|
||||
|
||||
@property
|
||||
def signbit(self):
|
||||
"""True if the value is negative, otherwise False."""
|
||||
return bool(self._data[0] & 0x80)
|
||||
|
||||
def __float__(self):
|
||||
return ibm2ieee(self._data)
|
||||
|
||||
def __bytes__(self):
|
||||
return self._data
|
||||
|
||||
def __repr__(self):
|
||||
return "{}.from_float({!r}) ~{!r}".format(self.__class__.__name__, self._data, float(self))
|
||||
|
||||
def __str__(self):
|
||||
return str(float(self))
|
||||
|
||||
def __bool__(self):
|
||||
return not self.is_zero()
|
||||
|
||||
def is_zero(self):
|
||||
return self.int_mantissa == 0
|
||||
|
||||
def __nonzero__(self):
|
||||
return not self.is_zero()
|
||||
|
||||
def is_subnormal(self):
|
||||
if self.is_zero():
|
||||
# Only one of the many possible representations of zero is considered 'normal' - all the zeros
|
||||
return not all(b == 0 for b in self._data)
|
||||
|
||||
return self._data[1] < 16 # TODO: Replace magic number with constant
|
||||
|
||||
def zero_subnormal(self):
|
||||
return IBM_FLOAT_ZERO if self.is_subnormal() else self
|
||||
|
||||
def frexp(self):
|
||||
"""Obtain the fraction and exponent.
|
||||
|
||||
Returns:
|
||||
A pair where the first item is the fraction in the range -1.0 and +1.0 and the
|
||||
exponent is an integer such that f = fraction * 2**exponent
|
||||
"""
|
||||
sign = -1 if self.signbit else 1
|
||||
mantissa = sign * self.int_mantissa / _F24
|
||||
exp_2 = self.exp16 * 4
|
||||
return mantissa, exp_2
|
||||
|
||||
def __pos__(self):
|
||||
return self
|
||||
|
||||
def __neg__(self):
|
||||
if self.is_zero():
|
||||
return IBM_FLOAT_ZERO
|
||||
|
||||
data = self._data
|
||||
return IBMFloat((data[0] ^ 0b10000000,
|
||||
data[1],
|
||||
data[2],
|
||||
data[3]))
|
||||
|
||||
def __abs__(self):
|
||||
if self.is_zero():
|
||||
return IBM_FLOAT_ZERO
|
||||
|
||||
data = self._data
|
||||
return IBMFloat((data[0] & 0b01111111,
|
||||
data[1],
|
||||
data[2],
|
||||
data[3]))
|
||||
|
||||
def __eq__(self, rhs):
|
||||
lhs = self
|
||||
|
||||
if not isinstance(rhs, IBMFloat):
|
||||
return float(lhs) == float(rhs)
|
||||
|
||||
lhs_sign = lhs.signbit
|
||||
rhs_sign = rhs.signbit
|
||||
|
||||
if lhs_sign != rhs_sign:
|
||||
return False
|
||||
|
||||
nlhs = lhs.normalize()
|
||||
nrhs = rhs.normalize()
|
||||
|
||||
if not (nlhs.is_subnormal() or nrhs.is_subnormal()):
|
||||
# Both of the numbers are normalised
|
||||
return nlhs._data == nrhs._data
|
||||
|
||||
# Either or both of the numbers are subnormal
|
||||
lhs_exp16 = nlhs.exp16
|
||||
rhs_exp16 = nrhs.exp16
|
||||
|
||||
lhs_mantissa = nlhs.int_mantissa
|
||||
rhs_mantissa = nrhs.int_mantissa
|
||||
|
||||
if lhs_exp16 < rhs_exp16:
|
||||
delta_exp16 = rhs_exp16 - lhs_exp16
|
||||
lhs_mantissa >>= 4 * delta_exp16
|
||||
lhs_exp16 += delta_exp16
|
||||
|
||||
if lhs_exp16 > rhs_exp16:
|
||||
delta_exp16 = lhs_exp16 - rhs_exp16
|
||||
rhs_mantissa >>= 4 * delta_exp16
|
||||
rhs_exp16 += delta_exp16
|
||||
|
||||
assert lhs_exp16 == rhs_exp16
|
||||
return lhs_mantissa == rhs_mantissa
|
||||
|
||||
def __floordiv__(self, rhs):
|
||||
return float(self) // float(rhs)
|
||||
|
||||
def __rfloordiv__(self, lhs):
|
||||
return float(lhs) // float(self)
|
||||
|
||||
def __rtruediv__(self, lhs):
|
||||
q = float(lhs) / float(self)
|
||||
return IBMFloat.from_float(q) if isinstance(lhs, float) else q
|
||||
|
||||
def __pow__(self, exponent):
|
||||
p = pow(float(self), float(exponent))
|
||||
return IBMFloat.from_float(p) if isinstance(exponent, IBMFloat) else p
|
||||
|
||||
def __rpow__(self, base):
|
||||
return IBMFloat.from_float(pow(float(base), float(self)))
|
||||
|
||||
def __mod__(self, rhs):
|
||||
m = float(self) % float(rhs)
|
||||
return IBMFloat.from_float(m) if isinstance(rhs, IBMFloat) else m
|
||||
|
||||
def __rmod__(self, lhs):
|
||||
m = float(lhs) % float(self)
|
||||
return IBMFloat.from_float(m) if isinstance(lhs, IBMFloat) else m
|
||||
|
||||
def __rmul__(self, lhs):
|
||||
p = float(lhs) * float(self)
|
||||
return IBMFloat.from_float(p) if isinstance(lhs, IBMFloat) else p
|
||||
|
||||
def __radd__(self, lhs):
|
||||
s = float(lhs) + float(self)
|
||||
return IBMFloat.from_float(s) if isinstance(lhs, IBMFloat) else s
|
||||
|
||||
def __lt__(self, rhs):
|
||||
return float(self) < float(rhs)
|
||||
|
||||
def __le__(self, rhs):
|
||||
return float(self) <= float(rhs)
|
||||
|
||||
def __gt__(self, rhs):
|
||||
return float(self) > float(rhs)
|
||||
|
||||
def __ge__(self, rhs):
|
||||
return float(self) >= float(rhs)
|
||||
|
||||
def __ceil__(self):
|
||||
t = trunc(self)
|
||||
return t if self.signbit else t + 1
|
||||
|
||||
def __floor__(self):
|
||||
t = trunc(self)
|
||||
return t - 1 if self.signbit else t
|
||||
|
||||
@property
|
||||
def exp16(self):
|
||||
"""The base 16 exponent."""
|
||||
exponent_16_biased = self._data[0] & 0x7f
|
||||
exponent_16 = exponent_16_biased - EXPONENT_BIAS
|
||||
return exponent_16
|
||||
|
||||
@property
|
||||
def int_mantissa(self):
|
||||
data = self._data
|
||||
return (data[1] << 16) | (data[2] << 8) | data[3]
|
||||
|
||||
def __trunc__(self):
|
||||
sign = -1 if self.signbit else 1
|
||||
exponent_16 = self.exp16
|
||||
mantissa = self.int_mantissa
|
||||
|
||||
num_nybbles_to_preserve = min(exponent_16, MAX_BITS_PRECISION_IBM_FLOAT // BITS_PER_NYBBLE)
|
||||
num_bits_to_clear = MAX_BITS_PRECISION_IBM_FLOAT - num_nybbles_to_preserve * BITS_PER_NYBBLE
|
||||
clear_mask = 2**num_bits_to_clear - 1
|
||||
preserve_mask = (2**MAX_BITS_PRECISION_IBM_FLOAT - 1) & ~clear_mask
|
||||
|
||||
truncated_mantissa = mantissa & preserve_mask
|
||||
magnitude = truncated_mantissa * pow(16, exponent_16) >> MAX_BITS_PRECISION_IBM_FLOAT
|
||||
return sign * magnitude
|
||||
|
||||
def normalize(self):
|
||||
"""Normalize the floating point value.
|
||||
|
||||
Returns:
|
||||
A normalized IBMFloat equal in value to this object.
|
||||
|
||||
Raises:
|
||||
FloatingPointError: If the number could not be normalized.
|
||||
"""
|
||||
if self.is_zero():
|
||||
return IBM_FLOAT_ZERO
|
||||
|
||||
exponent_16 = self.exp16
|
||||
mantissa = self.int_mantissa
|
||||
|
||||
while mantissa < (1 << 20):
|
||||
new_exponent_16 = exponent_16 - 1
|
||||
if not (-64 <= new_exponent_16 < 64):
|
||||
raise FloatingPointError("Could not normalize {!r} without causing exponent overflow.".format(self))
|
||||
|
||||
mantissa <<= 4
|
||||
exponent_16 = new_exponent_16
|
||||
|
||||
exponent_16_biased = exponent_16 + EXPONENT_BIAS
|
||||
|
||||
sign = int(self.signbit) << 7
|
||||
|
||||
a = sign | exponent_16_biased
|
||||
b = (mantissa >> 16) & 0xff
|
||||
c = (mantissa >> 8) & 0xff
|
||||
d = mantissa & 0xff
|
||||
|
||||
return IBMFloat.from_bytes((a, b, c, d))
|
||||
|
||||
def __round__(self, ndigits=None):
|
||||
return IBMFloat.from_float(round(float(self), ndigits))
|
||||
|
||||
def __truediv__(self, rhs):
|
||||
q = float(self) / float(rhs)
|
||||
return IBMFloat.from_float(q) if isinstance(rhs, IBMFloat) else q
|
||||
|
||||
def __mul__(self, rhs):
|
||||
p = float(self) * float(rhs)
|
||||
return IBMFloat.from_float(p) if isinstance(rhs, IBMFloat) else p
|
||||
|
||||
def __add__(self, rhs):
|
||||
p = float(self) + float(rhs)
|
||||
return IBMFloat.from_float(p) if isinstance(rhs, IBMFloat) else p
|
||||
|
||||
def __int__(self):
|
||||
return trunc(self)
|
||||
|
||||
|
||||
IBM_FLOAT_ZERO = IBMFloat.from_bytes(IBM_ZERO_BYTES)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
EMPTY_BYTE_STRING = b'' if sys.version_info >= (3, 0) else ''
|
||||
|
||||
|
||||
def seekable(fh):
|
||||
"""Determine whether a file-like object supports seeking.
|
||||
|
||||
Args:
|
||||
fh: The file-like-object to be tested.
|
||||
|
||||
Returns:
|
||||
True if the file supports seeking, otherwise False.
|
||||
"""
|
||||
try:
|
||||
return fh.seekable()
|
||||
except AttributeError:
|
||||
try:
|
||||
pos = fh.tell()
|
||||
try:
|
||||
fh.seek(0, os.SEEK_END)
|
||||
finally:
|
||||
fh.seek(pos)
|
||||
except AttributeError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
if sys.version_info >= (3, 0):
|
||||
long_int = int
|
||||
else:
|
||||
long_int = long
|
||||
|
||||
|
||||
if sys.version_info >= (3, 0):
|
||||
def byte_string(integers):
|
||||
return bytes(integers)
|
||||
else:
|
||||
def byte_string(integers):
|
||||
return EMPTY_BYTE_STRING.join(chr(i) for i in integers)
|
||||
|
||||
|
||||
if sys.version_info >= (3, 0):
|
||||
import reprlib
|
||||
reprlib = reprlib # Keep the static analyzer happy
|
||||
else:
|
||||
import repr as reprlib
|
||||
|
||||
if sys.version_info >= (3, 0):
|
||||
izip = zip
|
||||
from itertools import zip_longest as izip_longest
|
||||
else:
|
||||
from itertools import (izip, izip_longest)
|
||||
izip = izip # Keep the static analyzer happy
|
||||
izip_longest = izip_longest # Keep the static analyzer happy
|
||||
|
||||
|
||||
if sys.version_info >= (3, 0):
|
||||
def four_bytes(byte_str):
|
||||
a, b, c, d = byte_str[:4]
|
||||
return a, b, c, d
|
||||
else:
|
||||
def four_bytes(byte_str):
|
||||
a = ord(byte_str[0])
|
||||
b = ord(byte_str[1])
|
||||
c = ord(byte_str[2])
|
||||
d = ord(byte_str[3])
|
||||
return a, b, c, d
|
||||
|
||||
if sys.version_info >= (3, 0):
|
||||
unicode = str
|
||||
else:
|
||||
unicode = unicode
|
||||
|
||||
|
||||
|
||||
|
||||
+661
@@ -0,0 +1,661 @@
|
||||
from __future__ import print_function
|
||||
from segpy.encoding import ASCII
|
||||
|
||||
from segpy.portability import seekable
|
||||
from segpy.util import file_length, filename_from_handle
|
||||
from segpy.datatypes import DATA_SAMPLE_FORMAT, CTYPE_DESCRIPTION, CTYPES, size_in_bytes
|
||||
from segpy.toolkit import (extract_revision,
|
||||
bytes_per_sample,
|
||||
read_binary_reel_header,
|
||||
read_trace_header,
|
||||
catalog_traces,
|
||||
read_binary_values,
|
||||
compile_trace_header_format,
|
||||
REEL_HEADER_NUM_BYTES,
|
||||
TRACE_HEADER_NUM_BYTES,
|
||||
read_textual_reel_header,
|
||||
read_extended_textual_headers,
|
||||
guess_textual_header_encoding)
|
||||
|
||||
|
||||
def create_reader(fh, encoding=None, endian='>', progress=None):
|
||||
"""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.
|
||||
|
||||
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.
|
||||
|
||||
encoding: An optional text encoding for the textual headers. If
|
||||
None (the default) a heuristic will be used to guess the
|
||||
header encoding.
|
||||
|
||||
endian: '>' for big-endian data (the standard and default), '<'
|
||||
for little-endian (non-standard)
|
||||
|
||||
progress: A unary callable which will be passed a number
|
||||
between zero and one indicating the progress made. If
|
||||
provided, this callback will be invoked at least once with
|
||||
an argument equal to one.
|
||||
|
||||
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.
|
||||
|
||||
Example:
|
||||
|
||||
with open('my_seismic_data.sgy', 'rb') as fh:
|
||||
reader = create_reader(fh)
|
||||
print(reader.num_traces())
|
||||
|
||||
"""
|
||||
if hasattr(fh, 'encoding') and fh.encoding is not None:
|
||||
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")
|
||||
|
||||
if fh.closed:
|
||||
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))
|
||||
|
||||
if endian not in ('<', '>'):
|
||||
raise ValueError("Unrecognised endian value {!r}".format(endian))
|
||||
|
||||
if encoding is None:
|
||||
encoding = guess_textual_header_encoding(fh)
|
||||
|
||||
if encoding is None:
|
||||
encoding = ASCII
|
||||
|
||||
textual_reel_header = read_textual_reel_header(fh, encoding)
|
||||
binary_reel_header = read_binary_reel_header(fh, endian)
|
||||
extended_textual_header = read_extended_textual_headers(fh, binary_reel_header, encoding)
|
||||
revision = extract_revision(binary_reel_header)
|
||||
bps = bytes_per_sample(binary_reel_header, revision)
|
||||
|
||||
trace_offset_catalog, trace_length_catalog, cdp_catalog, line_catalog = catalog_traces(fh, bps, endian, progress)
|
||||
|
||||
if cdp_catalog is not None and line_catalog is None:
|
||||
return SegYReader2D(fh, textual_reel_header, binary_reel_header, extended_textual_header, trace_offset_catalog,
|
||||
trace_length_catalog, cdp_catalog, encoding, endian)
|
||||
|
||||
if cdp_catalog is None and line_catalog is not None:
|
||||
return SegYReader3D(fh, textual_reel_header, binary_reel_header, extended_textual_header, trace_offset_catalog,
|
||||
trace_length_catalog, line_catalog, encoding, endian)
|
||||
|
||||
return SegYReader(fh, textual_reel_header, binary_reel_header, extended_textual_header, trace_offset_catalog,
|
||||
trace_length_catalog, encoding, endian)
|
||||
|
||||
|
||||
class SegYReader(object):
|
||||
"""A basic SEG Y reader.
|
||||
|
||||
Use to obtain read the reel header, the trace_samples headers or trace_samples
|
||||
values. Traces can be accessed only by trace_samples index.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
fh,
|
||||
textual_reel_header,
|
||||
binary_reel_header,
|
||||
extended_textual_headers,
|
||||
trace_offset_catalog,
|
||||
trace_length_catalog,
|
||||
encoding,
|
||||
endian='>'):
|
||||
"""Initialize a SegYReader around a file-like-object.
|
||||
|
||||
Note:
|
||||
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.
|
||||
|
||||
textual_reel_header: A sequence of forty 80-character Unicode strings
|
||||
containing header data.
|
||||
|
||||
binary_reel_header: A dictionary containing reel header data.
|
||||
|
||||
extended_textual_headers: A sequence of sequences of Unicode strings.
|
||||
|
||||
trace_offset_catalog: A mapping from zero-based trace_samples index to
|
||||
the byte-offset to individual traces within the file.
|
||||
|
||||
trace_length_catalog: A mapping from zero-based trace_samples index to the
|
||||
number of samples in that trace_samples.
|
||||
|
||||
encoding: Either ASCII or EBCDIC.
|
||||
|
||||
endian: '>' for big-endian data (the standard and default), '<' for
|
||||
little-endian (non-standard)
|
||||
|
||||
"""
|
||||
self._fh = fh
|
||||
self._endian = endian
|
||||
self._encoding = encoding
|
||||
self._trace_header_format = compile_trace_header_format(self._endian)
|
||||
|
||||
self._textual_reel_header = textual_reel_header
|
||||
self._binary_reel_header = binary_reel_header
|
||||
self._extended_textual_headers = extended_textual_headers
|
||||
|
||||
self._trace_offset_catalog = trace_offset_catalog
|
||||
self._trace_length_catalog = trace_length_catalog
|
||||
|
||||
self._revision = extract_revision(self._binary_reel_header)
|
||||
self._bytes_per_sample = bytes_per_sample(
|
||||
self._binary_reel_header, self.revision)
|
||||
|
||||
def trace_indexes(self):
|
||||
"""An iterator over zero-based trace_samples indexes.
|
||||
|
||||
Returns:
|
||||
An iterator which yields integers in the range zero to
|
||||
num_traces() - 1
|
||||
"""
|
||||
return iter(self._trace_offset_catalog)
|
||||
|
||||
def num_traces(self):
|
||||
"""The number of traces"""
|
||||
return len(self._trace_offset_catalog)
|
||||
|
||||
def max_num_trace_samples(self):
|
||||
"""The number of samples in the trace_samples with the most samples."""
|
||||
return self._trace_length_catalog.value_max()
|
||||
|
||||
def num_trace_samples(self, trace_index):
|
||||
"""The number of samples in the specified trace_samples."""
|
||||
return self._trace_length_catalog[trace_index]
|
||||
|
||||
def trace_samples(self, trace_index, start=None, stop=None):
|
||||
"""Read a specific trace_samples.
|
||||
|
||||
Args:
|
||||
trace_index: An integer in the range zero to num_traces() - 1
|
||||
|
||||
start: Optional zero-based start sample index. The default
|
||||
is to read from the first (i.e. zeroth) sample.
|
||||
|
||||
stop: Optional zero-based stop sample index. Following Python
|
||||
slice convention this is one beyond the end.
|
||||
|
||||
Returns:
|
||||
A sequence of numeric trace_samples samples.
|
||||
|
||||
Example:
|
||||
|
||||
first_trace_samples = segy_reader.trace_samples(0)
|
||||
part_of_second_trace_samples = segy_reader.trace_samples(1, 1000, 2000)
|
||||
"""
|
||||
if not (0 <= trace_index < self.num_traces()):
|
||||
raise ValueError("Trace index out of range.")
|
||||
|
||||
num_samples_in_trace = self.num_trace_samples(trace_index)
|
||||
|
||||
start_sample = start if start is not None else 0
|
||||
stop_sample = stop if stop is not None else num_samples_in_trace
|
||||
|
||||
if not (0 <= stop_sample <= num_samples_in_trace):
|
||||
raise ValueError("trace_samples(): stop value {} out of range 0 to {}"
|
||||
.format(stop, num_samples_in_trace))
|
||||
|
||||
if not (0 <= start_sample <= stop_sample):
|
||||
raise ValueError("trace_samples(): start value {} out of range 0 to {}"
|
||||
.format(start, stop_sample))
|
||||
|
||||
dsf = self._binary_reel_header['DataSampleFormat']
|
||||
ctype = DATA_SAMPLE_FORMAT[dsf]
|
||||
start_pos = (self._trace_offset_catalog[trace_index]
|
||||
+ TRACE_HEADER_NUM_BYTES
|
||||
+ start_sample * size_in_bytes(CTYPES[ctype]))
|
||||
num_samples_to_read = stop_sample - start_sample
|
||||
|
||||
trace_values = read_binary_values(
|
||||
self._fh, start_pos, ctype, num_samples_to_read, self._endian)
|
||||
return trace_values
|
||||
|
||||
def trace_header(self, trace_index):
|
||||
"""Read a specific trace_samples.
|
||||
|
||||
Args:
|
||||
trace_index: An integer in the range zero to num_traces() - 1
|
||||
|
||||
Returns:
|
||||
A TraceHeader corresponding to the requested trace_samples.
|
||||
|
||||
Example:
|
||||
|
||||
first_trace_header, first_trace_samples = segy_reader.trace_samples(0)
|
||||
"""
|
||||
if not (0 <= trace_index < self.num_traces()):
|
||||
raise ValueError("Trace index {} out of range".format(trace_index))
|
||||
pos = self._trace_offset_catalog[trace_index]
|
||||
trace_header = read_trace_header(self._fh, self._trace_header_format, pos)
|
||||
return trace_header
|
||||
|
||||
@property
|
||||
def dimensionality(self):
|
||||
"""The spatial dimensionality of the data.
|
||||
|
||||
Returns:
|
||||
3 for 3D seismic volumes, 2 for 2D seismic lines, 1 for a
|
||||
single trace_samples, otherwise 0.
|
||||
|
||||
"""
|
||||
return self._dimensionality()
|
||||
|
||||
def _dimensionality(self):
|
||||
return 1 if self.num_traces() == 1 else 0
|
||||
|
||||
@property
|
||||
def textual_reel_header(self):
|
||||
"""The textual real header.
|
||||
|
||||
An immutable sequence of forty Unicode strings each 80 characters long.
|
||||
"""
|
||||
return self._textual_reel_header
|
||||
|
||||
@property
|
||||
def binary_reel_header(self):
|
||||
"""The binary reel header.
|
||||
|
||||
A dictionary containing data from the reel header.
|
||||
"""
|
||||
return self._binary_reel_header
|
||||
|
||||
@property
|
||||
def extended_textual_header(self):
|
||||
"""A sequence of sequences of Unicode strings.
|
||||
|
||||
If there were no headers, the sequence will be empty.
|
||||
"""
|
||||
return self._extended_textual_headers
|
||||
|
||||
|
||||
@property
|
||||
def filename(self):
|
||||
"""The filename.
|
||||
|
||||
Returns:
|
||||
The filename if it could be determined, otherwise '<unknown>'
|
||||
"""
|
||||
return filename_from_handle(self._fh)
|
||||
|
||||
@property
|
||||
def revision(self):
|
||||
"""The SEG Y revision.
|
||||
|
||||
Returns:
|
||||
Either datatypes.SEGY_REVISION_0 or datatypes.SEGY_REVISION_1
|
||||
"""
|
||||
return self._revision
|
||||
|
||||
@property
|
||||
def bytes_per_sample(self):
|
||||
"""The number of bytes per trace_samples sample.
|
||||
"""
|
||||
return self._bytes_per_sample
|
||||
|
||||
@property
|
||||
def data_sample_format(self):
|
||||
"""The data type of the samples in machine-readable form.
|
||||
|
||||
Returns:
|
||||
One of the values from datatypes.DATA_SAMPLE_FORMAT
|
||||
"""
|
||||
return DATA_SAMPLE_FORMAT[self._binary_reel_header['DataSampleFormat']]
|
||||
|
||||
@property
|
||||
def data_sample_format_description(self):
|
||||
"""A descriptive human-readable description of the data sample format
|
||||
"""
|
||||
return CTYPE_DESCRIPTION[self.data_sample_format]
|
||||
|
||||
@property
|
||||
def encoding(self):
|
||||
"""The encoding, of the data in the underlying file. Either ASCII ('ascii'),
|
||||
EBCDIC ('cp037') or None."""
|
||||
return self._encoding
|
||||
|
||||
@property
|
||||
def endian(self):
|
||||
"""The endianness of the data in the underlying file. Either '>' for big-endian or '<' for
|
||||
little endian or None."""
|
||||
return self._endian
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
fh,
|
||||
textual_reel_header,
|
||||
binary_reel_header,
|
||||
extended_textual_headers,
|
||||
trace_offset_catalog,
|
||||
trace_length_catalog,
|
||||
line_catalog,
|
||||
encoding,
|
||||
endian='>'):
|
||||
"""Initialize a SegYReader3D around a file-like-object.
|
||||
|
||||
Note:
|
||||
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.
|
||||
|
||||
binary_reel_header: A dictionary containing reel header data.
|
||||
|
||||
trace_offset_catalog: A mapping from zero-based trace_samples indexes to
|
||||
the byte-offset to individual traces within the file.
|
||||
|
||||
trace_length_catalog: A mapping from zero-based trace_samples indexes to
|
||||
the number of samples in that trace_samples.
|
||||
|
||||
line_catalog: A mapping from (xline, inline) tuples to
|
||||
trace_indexes.
|
||||
|
||||
encoding: Either ASCII or EBCDIC.
|
||||
|
||||
endian: '>' for big-endian data (the standard and default), '<' for
|
||||
little-endian (non-standard)
|
||||
"""
|
||||
super(SegYReader3D, self).__init__(fh, textual_reel_header, binary_reel_header, extended_textual_headers,
|
||||
trace_offset_catalog, trace_length_catalog, encoding, endian)
|
||||
self._line_catalog = line_catalog
|
||||
self._num_inlines = None
|
||||
self._num_xlines = None
|
||||
|
||||
def _dimensionality(self):
|
||||
return 3
|
||||
|
||||
def inline_range(self):
|
||||
"""A range encompassing inline numbers.
|
||||
|
||||
The number of inlines within this range can be found with len(reader.inline_range()).
|
||||
|
||||
Returns:
|
||||
A range() object with start set to the first inline number and stop set to
|
||||
one beyond the last inline number. The range always has a step of one, although
|
||||
this should not be taken as meaning that any intermediate inline number generated
|
||||
by the range is valid.
|
||||
"""
|
||||
start = self._line_catalog.key_min()[0]
|
||||
stop = self._line_catalog.key_max()[0] + 1
|
||||
return range(start, stop)
|
||||
|
||||
def num_inlines(self):
|
||||
"""The number of distinct inlines in the survey.
|
||||
|
||||
This number is not necessarily the same as the value returned by
|
||||
len(reader.inline_range()) as there may be missing inlines within the range.
|
||||
"""
|
||||
if self._num_inlines is None:
|
||||
try:
|
||||
self._num_inlines = self._line_catalog.i_max - self._line_catalog.i_min + 1
|
||||
except AttributeError:
|
||||
self._num_inlines = len(set(i for i, j in self._line_catalog))
|
||||
return self._num_inlines
|
||||
|
||||
def xline_range(self):
|
||||
"""A range encompassing crossline numbers.
|
||||
|
||||
The number of crosslines within this range can be found with len(reader.crossline_range()).
|
||||
|
||||
Returns:
|
||||
A range() object with start set to the first crossline number and stop set to
|
||||
one beyond the last crossline number. The range always has a step of one, although
|
||||
this should not be taken as meaning that any intermediate crossline number generated
|
||||
by the range is valid.
|
||||
"""
|
||||
start = self._line_catalog.key_min()[1]
|
||||
stop = self._line_catalog.key_max()[1] + 1
|
||||
return range(start, stop)
|
||||
|
||||
def num_xlines(self):
|
||||
"""The number of distinct crosslines in the survey.
|
||||
|
||||
This number is not necessarily the same as the value returned by
|
||||
len(reader.xline_range()) as there may be missing crosslines within the range.
|
||||
"""
|
||||
if self._num_xlines is None:
|
||||
try:
|
||||
self._num_xlines = self._line_catalog.j_max - self._line_catalog.j_min + 1
|
||||
except AttributeError:
|
||||
self._num_xlines = len(set(j for i, j in self._line_catalog))
|
||||
return self._num_xlines
|
||||
|
||||
def inline_xline_numbers(self):
|
||||
"""An iterator over all (inline_number, xline_number) tuples
|
||||
corresponding to traces.
|
||||
"""
|
||||
return iter(self._line_catalog)
|
||||
|
||||
def has_trace_index(self, inline_xline):
|
||||
"""Determine whether a specific trace_samples exists.
|
||||
|
||||
Args:
|
||||
inline_xline: A 2-tuple of inline number, crossline number.
|
||||
|
||||
Returns:
|
||||
True if the specified trace_samples exists, otherwise False.
|
||||
"""
|
||||
return inline_xline in self._line_catalog
|
||||
|
||||
def trace_index(self, inline_xline):
|
||||
"""Obtain the trace_samples 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
|
||||
inline_xline_numbers() iterator.
|
||||
|
||||
Furthermore, inline and crossline numbers should not be
|
||||
relied upon to be zero- or one-based indexes (although
|
||||
they may be).
|
||||
|
||||
Args:
|
||||
inline_xline: A 2-tuple of inline number, crossline number.
|
||||
|
||||
Returns:
|
||||
A trace_samples index which can be used with trace_samples().
|
||||
"""
|
||||
return self._line_catalog[inline_xline]
|
||||
|
||||
|
||||
class SegYReader2D(SegYReader):
|
||||
def __init__(self,
|
||||
fh,
|
||||
textual_reel_header,
|
||||
binary_reel_header,
|
||||
extended_textual_headers,
|
||||
trace_offset_catalog,
|
||||
trace_length_catalog,
|
||||
cdp_catalog,
|
||||
encoding,
|
||||
endian='>'):
|
||||
"""Initialize a SegYReader2D around a file-like-object.
|
||||
|
||||
Note:
|
||||
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.
|
||||
|
||||
binary_reel_header: A dictionary containing reel header data.
|
||||
|
||||
trace_catalog_offset: A mapping from zero-based trace_samples index to
|
||||
the byte-offset to individual traces within the file.
|
||||
|
||||
trace_length_catalog: A mapping from zero-based trace_samples indexes to
|
||||
the number of samples in that trace_samples.
|
||||
|
||||
cdp_catalog: A mapping from CDP numbers to trace_indexes.
|
||||
|
||||
encoding: Either ASCII or EBCDIC.
|
||||
|
||||
endian: '>' for big-endian data (the standard and default), '<' for
|
||||
little-endian (non-standard)
|
||||
"""
|
||||
super(SegYReader2D, self).__init__(fh, textual_reel_header, binary_reel_header, extended_textual_headers,
|
||||
trace_offset_catalog, trace_length_catalog, encoding, endian)
|
||||
self._cdp_catalog = cdp_catalog
|
||||
|
||||
def _dimensionality(self):
|
||||
return 2
|
||||
|
||||
def cdp_numbers(self):
|
||||
"""An iterator over all cdp numbers corresponding to traces.
|
||||
"""
|
||||
return iter(self._cdp_catalog)
|
||||
|
||||
def cdp_range(self):
|
||||
"""A range encompassing CDP numbers.
|
||||
|
||||
The number of CDPs within this range can be found with len(reader.cdp_range()).
|
||||
|
||||
Returns:
|
||||
A range() object with start set to the first CDP number and stop set to
|
||||
one beyond the last CDP number. The range always has a step of one, although
|
||||
this should not be taken as meaning that any intermediate CDP number generated
|
||||
by the range is valid.
|
||||
"""
|
||||
start = self._cdp_catalog.value_min()
|
||||
stop = self._cdp_catalog.value_max() + 1
|
||||
return range(start, stop)
|
||||
|
||||
def num_cdps(self):
|
||||
"""The number of distinct CDPs.
|
||||
|
||||
This number is not necessarily the same as the value returned by
|
||||
len(reader.cdp_range()) as there may be missing CDPs.
|
||||
"""
|
||||
return len(self._cdp_catalog)
|
||||
|
||||
def has_trace_index(self, cdp_number):
|
||||
"""Determine whether a specified trace_samples exists.
|
||||
|
||||
Args:
|
||||
cdp_number: A CDP number.
|
||||
|
||||
Returns:
|
||||
True if the trace_samples exists, otherwise False.
|
||||
"""
|
||||
return self._cdp_catalog[cdp_number]
|
||||
|
||||
def trace_index(self, cdp_number):
|
||||
"""Obtain the trace_samples index given an xline and a inline.
|
||||
|
||||
Args:
|
||||
cdp_number: A CDP number.
|
||||
|
||||
Returns:
|
||||
A trace_samples index which can be used with trace_samples().
|
||||
"""
|
||||
return self._cdp_catalog[cdp_number]
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
import sys
|
||||
|
||||
if argv is None:
|
||||
argv = sys.argv[1:]
|
||||
|
||||
class ProgressBar(object):
|
||||
|
||||
def __init__(self, num_chars, character='.'):
|
||||
self._num_chars = num_chars
|
||||
self._character = character
|
||||
self._ratchet = 0
|
||||
|
||||
def __call__(self, proportion):
|
||||
existing = self._num_marks(self._ratchet)
|
||||
required = self._num_marks(proportion)
|
||||
print(self._character * (required - existing), end='')
|
||||
self._ratchet = proportion
|
||||
|
||||
def _num_marks(self, p):
|
||||
return int(round(p * self._num_chars))
|
||||
|
||||
filename = argv[0]
|
||||
|
||||
with open(filename, 'rb') as segy_file:
|
||||
segy_reader = create_reader(segy_file, progress=ProgressBar(30))
|
||||
print()
|
||||
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("Dimensionality: ", segy_reader.dimensionality)
|
||||
|
||||
try:
|
||||
print("Number of CDPs: ", segy_reader.num_cdps())
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
print("Number of inlines: ", segy_reader.num_inlines())
|
||||
print("Number of crosslines: ", segy_reader.num_xlines())
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
print("=== BEGIN TEXTUAL REEL HEADER ===")
|
||||
for line in segy_reader.textual_reel_header:
|
||||
print(line[3:])
|
||||
print("=== END TEXTUAL REEL HEADER ===")
|
||||
print()
|
||||
print("=== BEGIN EXTENDED TEXTUAL HEADER ===")
|
||||
print(segy_reader.extended_textual_header)
|
||||
print("=== END EXTENDED TEXTUAL_HEADER ===")
|
||||
|
||||
for trace_index in segy_reader.trace_indexes():
|
||||
trace_header = segy_reader.trace_header(trace_index)
|
||||
print("Inline {}, Crossline {}, Shotpoint {}".format(trace_header.Inline3D, trace_header.Crossline3D,
|
||||
trace_header.ShotPoint))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,15 +1,18 @@
|
||||
|
||||
SEGY_REVISION_0 = 0x0000
|
||||
SEGY_REVISION_1 = 0x0100
|
||||
SEGY_REVISION_0 = 0
|
||||
SEGY_REVISION_1 = 1
|
||||
|
||||
VARIANTS = {SEGY_REVISION_0: SEGY_REVISION_0,
|
||||
SEGY_REVISION_1: SEGY_REVISION_1,
|
||||
0: SEGY_REVISION_0,
|
||||
1: SEGY_REVISION_1,
|
||||
100: SEGY_REVISION_1,
|
||||
256: SEGY_REVISION_1}
|
||||
|
||||
|
||||
class SegYRevisionError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def canonicalize_revision(revision):
|
||||
"""Canonicalize a SEG Y revision.
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
from segpy.revisions import SEGY_REVISION_0, SEGY_REVISION_1
|
||||
|
||||
TEMPLATE = """
|
||||
C 1 CLIENT { client } COMPANY { company } CREW NO {crew }
|
||||
C 2 LINE { line } AREA { area } MAP ID { map_id }
|
||||
C 3 REEL NO {reelnum} DAY-START OF REEL {d} YEAR {yr} OBSERVER {observer }
|
||||
C 4 INSTRUMENT: MFG { mfg } MODEL { model } SERIAL NO { serial }
|
||||
C 5 DATA TRACES/RECORD {dtpr} AUXILIARY TRACES/RECORD {atpr } CDP FOLD {cdpfold}
|
||||
C 6 SAMPLE INTERVAL {intvl} SAMPLES/TRACE {spt} BITS/IN {bi} BYTES/SAMPLE {bps }
|
||||
C 7 RECORDING FORMAT {rfmt} FORMAT THIS REEL {ftr } MEASUREMENT SYSTEM {measmnt}
|
||||
C 8 SAMPLE CODE: FLOATING PT {f} FIXED PT {x} FIXED PT-GAIN {g} CORRELATED {cor}
|
||||
C 9 GAIN TYPE: FIXED {i} BINARY {b} FLOATING POINT {c} OTHER {other }
|
||||
C10 FILTERS: ALIAS {a }HZ NOTCH {n }HZ BAND {b1}-{b2 }HZ SLOPE {s}-{s2}DB/OCT
|
||||
C11 SOURCE: TYPE {type } NUMBER/POINT {npt } POINT INTERVAL {point_interval }
|
||||
C12 PATTERN: {source_pattern } LENGTH {lent} WIDTH {width }
|
||||
C13 SWEEP: START {ss}HZ END {se}HZ LENGTH {lms}MS CHANNEL NO {q} TYPE {cd }
|
||||
C14 TAPER: START LENGTH {tsl }MS END LENGTH {tel }MS TYPE {taper_type }
|
||||
C15 SPREAD: OFFSET {off } MAX DISTANCE {md } GROUP INTERVAL {group_interval }
|
||||
C16 GEOPHONES: PER GROUP {p} SPACING {y} FREQUENCY {r} MFG {gmfg } MODEL {gmod}
|
||||
C17 PATTERN: {geophone_pattern } LENGTH {glen} WIDTH {geophone_width }
|
||||
C18 TRACES SORTED BY: RECORD {u} CDP {v} OTHER { sort_other }
|
||||
C19 AMPLITUDE RECOVERY: NONE {ar} SPHERICAL DIV {sd } AGC {} OTHER {ar_other }
|
||||
C20 MAP PROJECTION {map_projection } ZONE ID {zid} COORDINATE UNITS {co_units }
|
||||
C21 PROCESSING: { processing1 }
|
||||
C22 PROCESSING: { processing2 }
|
||||
C23 { unassigned1 }
|
||||
C24 { unassigned2 }
|
||||
C25 { unassigned3 }
|
||||
C26 { unassigned4 }
|
||||
C27 { unassigned5 }
|
||||
C28 { unassigned6 }
|
||||
C29 { unassigned7 }
|
||||
C30 { unassigned8 }
|
||||
C31 { unassigned9 }
|
||||
C32 { unassigned10 }
|
||||
C33 { unassigned11 }
|
||||
C34 { unassigned12 }
|
||||
C35 { unassigned13 }
|
||||
C36 { unassigned14 }
|
||||
C37 { unassigned15 }
|
||||
C38 { unassigned16 }
|
||||
C39 { unassigned17 }
|
||||
C40 { end_marker }
|
||||
"""
|
||||
|
||||
|
||||
END_TEXTUAL_HEADER = 'END TEXTUAL HEADER'
|
||||
END_EBCDIC = 'END EBCDIC'
|
||||
|
||||
END_MARKERS = {SEGY_REVISION_0: END_EBCDIC,
|
||||
SEGY_REVISION_1: END_TEXTUAL_HEADER}
|
||||
|
||||
TEMPLATE_FIELD_NAMES = {'client': 'client',
|
||||
'company': 'company',
|
||||
'crew': 'crew_number',
|
||||
'line': 'line',
|
||||
'area': 'area',
|
||||
'map_id': 'map_id',
|
||||
'reelnum': 'reel_number',
|
||||
'd': 'day_start_of_reel',
|
||||
'yr': 'year',
|
||||
'observer': 'observer',
|
||||
'mfg': 'instrument_manufacturer',
|
||||
'model': 'instrument_model',
|
||||
'serial': 'instrument_serial',
|
||||
'dtpr': 'data_traces_per_record',
|
||||
'atpr': 'auxiliary_traces_per_record',
|
||||
'cdpfold': 'cdp_fold',
|
||||
'intvl': 'sample_interval',
|
||||
'spt': 'samples_per_trace',
|
||||
'bi': 'bits_per_inch',
|
||||
'bps': 'bytes_per_sample',
|
||||
'rfmt': 'recording_format',
|
||||
'ftr': 'format_this_reel',
|
||||
'measmnt': 'measurement_system',
|
||||
'f': 'sample_code_floating_point',
|
||||
'x': 'sample_code_fixed_point',
|
||||
'g': 'sample_code_fixed_point_gain',
|
||||
'cor': 'sample_code_correlated',
|
||||
'i': 'gain_type_fixed',
|
||||
'b': 'gain_type_binary',
|
||||
'c': 'gain_type_fixed_point_gain',
|
||||
'other': 'gain_type_other',
|
||||
'a': 'filters_alias_hz',
|
||||
'n': 'filters_notch_hz',
|
||||
'b1': 'filters_band_lower_hz',
|
||||
'b2': 'filters_band_upper_hz',
|
||||
's': 'filters_slope_lower_db_per_oct',
|
||||
's2': 'filters_slope_upper_db_per_oct',
|
||||
'type': 'source_type',
|
||||
'npt': 'source_number_per_point',
|
||||
'point_interval': 'source_point_interval',
|
||||
'source_pattern': 'source_pattern',
|
||||
'lent': 'source_length',
|
||||
'width': 'source_width',
|
||||
'ss': 'sweep_start_hz',
|
||||
'se': 'sweep_end_hz',
|
||||
'lms': 'sweep_length_ms',
|
||||
'q': 'sweep_channel_number',
|
||||
'cd': 'sweep_type',
|
||||
'tsl': 'taper_start_length_ms',
|
||||
'tel': 'taper_end_length_ms',
|
||||
'taper_type': 'taper_type',
|
||||
'off': 'spread_offset',
|
||||
'md': 'spread_max_distance',
|
||||
'group_interval': 'spread_group_interval',
|
||||
'p': 'geophones_per_group',
|
||||
'y': 'geophone_spacing',
|
||||
'r': 'geophone_frequency',
|
||||
'gmfg': 'geophone_manufacturer',
|
||||
'gmod': 'geophone_model',
|
||||
'geophone_pattern': 'geophone_pattern',
|
||||
'glen': 'geophone_length',
|
||||
'geophone_width': 'geophone_width',
|
||||
'u': 'traces_sorted_by_record',
|
||||
'v': 'traces_sorted_by_cdp',
|
||||
'sort_other': 'traces_sorted_by_other',
|
||||
'ar': 'amplitude_recovery_none',
|
||||
'sd': 'amplitude_recovery_spherical_div',
|
||||
'': 'amplitude_recovery_agc',
|
||||
'ar_other': 'amplitude_recovery_other',
|
||||
'map_projection': 'map_projection',
|
||||
'zid': 'zone_id',
|
||||
'co_units': 'coordinate_units',
|
||||
'processing1': 'processing1',
|
||||
'processing2': 'processing2',
|
||||
'unassigned1': 'unassigned1',
|
||||
'unassigned2': 'unassigned2',
|
||||
'unassigned3': 'unassigned3',
|
||||
'unassigned4': 'unassigned4',
|
||||
'unassigned5': 'unassigned5',
|
||||
'unassigned6': 'unassigned6',
|
||||
'unassigned7': 'unassigned7',
|
||||
'unassigned8': 'unassigned8',
|
||||
'unassigned9': 'unassigned9',
|
||||
'unassigned10': 'unassigned10',
|
||||
'unassigned11': 'unassigned11',
|
||||
'unassigned12': 'unassigned12',
|
||||
'unassigned13': 'unassigned13',
|
||||
'unassigned14': 'unassigned14',
|
||||
'unassigned15': 'unassigned15',
|
||||
'unassigned16': 'unassigned16',
|
||||
'unassigned17': 'unassigned17',
|
||||
'end_marker': 'end_marker'
|
||||
}
|
||||
|
||||
INV_TEMPLATE_FIELD_NAMES = dict((v, k) for k, v in TEMPLATE_FIELD_NAMES.items())
|
||||
@@ -0,0 +1,899 @@
|
||||
from __future__ import print_function
|
||||
|
||||
from array import array
|
||||
from collections import namedtuple, OrderedDict
|
||||
import itertools
|
||||
import os
|
||||
import struct
|
||||
import re
|
||||
import logging
|
||||
|
||||
from segpy import textual_reel_header_definition
|
||||
|
||||
|
||||
from segpy.catalog import CatalogBuilder
|
||||
from segpy.datatypes import CTYPES, size_in_bytes
|
||||
from segpy.encoding import guess_encoding, is_supported_encoding, UnsupportedEncodingError
|
||||
from segpy.binary_reel_header_definition import HEADER_DEF
|
||||
from segpy.ibm_float import IBMFloat
|
||||
from segpy.revisions import canonicalize_revision
|
||||
from segpy.trace_header_definition import TRACE_HEADER_DEF
|
||||
from segpy.util import file_length, batched, pad, complementary_intervals, NATIVE_ENDIANNESS
|
||||
from segpy.portability import EMPTY_BYTE_STRING, izip_longest
|
||||
|
||||
HEADER_NEWLINE = '\r\n'
|
||||
|
||||
CARD_LENGTH = 80
|
||||
CARDS_PER_HEADER = 40
|
||||
|
||||
TEXTUAL_HEADER_NUM_BYTES = CARD_LENGTH * CARDS_PER_HEADER
|
||||
BINARY_HEADER_NUM_BYTES = 400
|
||||
REEL_HEADER_NUM_BYTES = TEXTUAL_HEADER_NUM_BYTES + BINARY_HEADER_NUM_BYTES
|
||||
TRACE_HEADER_NUM_BYTES = 240
|
||||
|
||||
END_TEXT_STANZA = "((SEG: EndText))"
|
||||
|
||||
|
||||
def logger():
|
||||
# Defer logger creation until the module is *used* rather than imported.
|
||||
return logging.getLogger(__name__)
|
||||
|
||||
|
||||
def extract_revision(binary_reel_header):
|
||||
"""Obtain the SEG Y revision from the reel header.
|
||||
|
||||
Args:
|
||||
binary_reel_header: A dictionary containing a reel header, such as obtained
|
||||
from read_binary_reel_header()
|
||||
|
||||
Returns:
|
||||
One of the constants revisions.SEGY_REVISION_0 or
|
||||
revisions.SEGY_REVISION_1
|
||||
"""
|
||||
raw_revision = binary_reel_header['SegyFormatRevisionNumber']
|
||||
return canonicalize_revision(raw_revision)
|
||||
|
||||
|
||||
def num_extended_textual_headers(binary_reel_header):
|
||||
"""Obtain the number of 3200 byte extended textual file headers.
|
||||
|
||||
A value of zero indicates there are no Extended Textual File Header records
|
||||
(i.e. this file has no Extended Textual File Header(s)). A value of -1 indicates
|
||||
that there are a variable number of Extended Textual File Header records and the
|
||||
end of the Extended Textual File Header is denoted by an ((SEG: EndText)) stanza
|
||||
in the final record. A positive value indicates that there are exactly that many
|
||||
Extended Textual File Header records.
|
||||
"""
|
||||
num_ext_headers = binary_reel_header['NumberOfExtTextualHeaders']
|
||||
return num_ext_headers
|
||||
|
||||
|
||||
def bytes_per_sample(binary_reel_header, revision):
|
||||
"""Determine the number of bytes per sample from the reel header.
|
||||
|
||||
Args:
|
||||
binary_reel_header: A dictionary containing a reel header, such as obtained
|
||||
from read_binary_reel_header()
|
||||
|
||||
revision: One of the constants revisions.SEGY_REVISION_0 or
|
||||
revisions.SEGY_REVISION_1
|
||||
|
||||
Returns:
|
||||
An integer number of bytes per sample.
|
||||
"""
|
||||
dsf = binary_reel_header['DataSampleFormat']
|
||||
bps = HEADER_DEF["DataSampleFormat"]["bps"][revision][dsf]
|
||||
return bps
|
||||
|
||||
|
||||
def samples_per_trace(binary_reel_header):
|
||||
"""Determine the number of samples per trace_samples from the reel header.
|
||||
|
||||
Note: There is no requirement for all traces to be of the same length,
|
||||
so this value should be considered indicative only, and as such is
|
||||
mostly useful in the absence of other information. The actual number
|
||||
of samples for a specific trace_samples should be retrieved from individual
|
||||
trace_samples headers.
|
||||
|
||||
Args:
|
||||
binary_reel_header: A dictionary containing a reel header, such as obtained
|
||||
from read_binary_reel_header()
|
||||
|
||||
Returns:
|
||||
An integer number of samples per trace_samples
|
||||
"""
|
||||
return binary_reel_header['ns']
|
||||
|
||||
|
||||
def trace_length_bytes(binary_reel_header, bps):
|
||||
"""Determine the trace_samples length in bytes from the reel header.
|
||||
|
||||
Note: There is no requirement for all traces to be of the same length,
|
||||
so this value should be considered indicative only, and as such is
|
||||
mostly useful in the absence of other information. The actual number
|
||||
of samples for a specific trace_samples should be retrieved from individual
|
||||
trace_samples headers.
|
||||
|
||||
Args:
|
||||
binary_reel_header: A dictionary containing a reel header, such as obtained
|
||||
from read_binary_reel_header()
|
||||
|
||||
bps: The number of bytes per sample, such as obtained from a call to
|
||||
bytes_per_sample()
|
||||
|
||||
"""
|
||||
return samples_per_trace(binary_reel_header) * bps + TRACE_HEADER_NUM_BYTES
|
||||
|
||||
|
||||
def guess_textual_header_encoding(fh):
|
||||
fh.seek(0)
|
||||
raw_header = fh.read(TEXTUAL_HEADER_NUM_BYTES)
|
||||
encoding = guess_encoding(raw_header)
|
||||
return encoding
|
||||
|
||||
|
||||
def read_textual_reel_header(fh, encoding):
|
||||
"""Read the SEG Y card image header, also known as the textual header
|
||||
|
||||
Args:
|
||||
fh: A file-like object open in binary mode positioned such that the
|
||||
beginning of the textual header will be the next byte to read.
|
||||
|
||||
encoding: Either 'cp037' for EBCDIC or 'ascii' for ASCII.
|
||||
|
||||
Returns:
|
||||
A tuple of forty Unicode strings (Python 2: unicode, Python 3: str)
|
||||
containing the transcoded header data.
|
||||
"""
|
||||
fh.seek(0)
|
||||
raw_header = fh.read(TEXTUAL_HEADER_NUM_BYTES)
|
||||
|
||||
num_bytes_read = len(raw_header)
|
||||
if num_bytes_read < TEXTUAL_HEADER_NUM_BYTES:
|
||||
raise EOFError("Only {} bytes of {} byte textual reel header could be read"
|
||||
.format(num_bytes_read, TEXTUAL_HEADER_NUM_BYTES))
|
||||
|
||||
lines = tuple(bytes(raw_line).decode(encoding) for raw_line in batched(raw_header, CARD_LENGTH))
|
||||
return lines
|
||||
|
||||
|
||||
def read_binary_reel_header(fh, endian='>'):
|
||||
"""Read the SEG Y binary reel header.
|
||||
|
||||
Args:
|
||||
fh: A file-like object open in binary mode. Binary header is assumed to
|
||||
be at an offset of 3200 bytes from the beginning of the file.
|
||||
|
||||
endian: '>' for big-endian data (the standard and default), '<' for
|
||||
little-endian (non-standard)
|
||||
"""
|
||||
fh.seek(TEXTUAL_HEADER_NUM_BYTES)
|
||||
reel_header = {}
|
||||
for key in HEADER_DEF:
|
||||
pos = HEADER_DEF[key]['pos']
|
||||
ctype = HEADER_DEF[key]['type']
|
||||
values = tuple(read_binary_values(fh, pos, ctype, 1, endian))
|
||||
reel_header[key] = values[0]
|
||||
return reel_header
|
||||
|
||||
|
||||
def has_end_text_stanza(ext_header):
|
||||
"""Determine whether the header is the end text stanza.
|
||||
|
||||
Args:
|
||||
ext_header: A sequence of forty CARD_LENGTH character Unicode strings.
|
||||
|
||||
Returns:
|
||||
True if the header is the SEG Y Revision 1 end text header,
|
||||
otherwise False.
|
||||
"""
|
||||
return END_TEXT_STANZA in ext_header[0]
|
||||
|
||||
|
||||
def read_extended_headers_until_end(fh, encoding):
|
||||
"""Read an unspecified number of extended textual headers, until the end-text header is found.
|
||||
|
||||
Args:
|
||||
fh: A file-like object open in binary mode. The first of any extended textual headers
|
||||
is assumed to be at an offset of 3600 bytes from the beginning of the file
|
||||
(immediately following the binary reel header).
|
||||
|
||||
encoding: Optional encoding of the header in the file. If None (the
|
||||
default) a reliable heuristic will be used to guess the encoding.
|
||||
Typically 'cp037' for EBCDIC or 'ascii' for ASCII.
|
||||
|
||||
Returns:
|
||||
A list of tuples each containing forty CARD_LENGTH character Unicode strings. If present, the end_text
|
||||
stanza is excluded.
|
||||
"""
|
||||
extended_headers = []
|
||||
while True:
|
||||
ext_header = read_textual_reel_header(fh, encoding)
|
||||
extended_headers.append(ext_header)
|
||||
if has_end_text_stanza(ext_header):
|
||||
break
|
||||
return extended_headers
|
||||
|
||||
|
||||
def read_extended_headers_counted(fh, num_expected, encoding):
|
||||
"""Read a specified number of extended textual headers.
|
||||
|
||||
If an end-text stanza is located prematurely (in anything other than the last expected header)
|
||||
reading will be terminated and a warning logged.
|
||||
|
||||
Args:
|
||||
fh: A file-like object open in binary mode. The first of any extended textual headers
|
||||
is assumed to be at an offset of 3600 bytes from the beginning of the file
|
||||
(immediately following the binary reel header).
|
||||
|
||||
num_expected: A non-negative integer of headers.
|
||||
|
||||
encoding: Optional encoding of the header in the file. If None (the
|
||||
default) a reliable heuristic will be used to guess the encoding.
|
||||
Typically 'cp037' for EBCDIC or 'ascii' for ASCII.
|
||||
|
||||
Returns:
|
||||
A list of tuples each containing forty CARD_LENGTH -character Unicode strings.
|
||||
"""
|
||||
assert num_expected >= 0
|
||||
extended_headers = []
|
||||
for i in range(num_expected):
|
||||
ext_header = read_textual_reel_header(fh, encoding)
|
||||
if has_end_text_stanza(ext_header):
|
||||
if i != num_expected - 1:
|
||||
logger().warning("Unexpected end-text extended header.")
|
||||
break
|
||||
extended_headers.append(ext_header)
|
||||
|
||||
return extended_headers
|
||||
|
||||
|
||||
def read_extended_textual_headers(fh, binary_reel_header, encoding):
|
||||
"""Read any extended textual reel headers.
|
||||
|
||||
Args:
|
||||
fh: A file-like object open in binary mode. The first of any extended textual headers
|
||||
is assumed to be at an offset of 3600 bytes from the beginning of the file
|
||||
(immediately following the binary reel header).
|
||||
|
||||
binary_reel_header: A dictionary containing data read from the binary
|
||||
reel header by the read_binary_reel_header() function.
|
||||
|
||||
encoding: Optional encoding of the header in the file. If None (the
|
||||
default) a reliable heuristic will be used to guess the encoding.
|
||||
Typically 'cp037' for EBCDIC or 'ascii' for ASCII.
|
||||
|
||||
Returns:
|
||||
A sequence of sequences of Unicode strings representing headers of lines of characters. The length of the
|
||||
outer sequence will be equal to the number of extended headers read. Each item in the outer sequence will be
|
||||
a sequence of exactly forty Unicode strings. To combine the headers into a single string, consider using
|
||||
concatenate_extended_textual_headers().
|
||||
|
||||
Post-condition:
|
||||
As a post-condition to this function, the file-pointer of fh will be
|
||||
positioned immediately after the last extended textual header, which
|
||||
should be the start of the first trace_samples header.
|
||||
"""
|
||||
fh.seek(REEL_HEADER_NUM_BYTES)
|
||||
declared_num_ext_headers = num_extended_textual_headers(binary_reel_header)
|
||||
|
||||
if declared_num_ext_headers < 0:
|
||||
return read_extended_headers_until_end(fh, encoding)
|
||||
|
||||
return read_extended_headers_counted(fh, declared_num_ext_headers, encoding)
|
||||
|
||||
|
||||
_READ_PROPORTION = 0.75 # The proportion of time spent in catalog_traces
|
||||
# reading the file. Determined empirically.
|
||||
|
||||
|
||||
def catalog_traces(fh, bps, endian='>', progress=None):
|
||||
"""Build catalogs to facilitate random access to trace_samples data.
|
||||
|
||||
Note:
|
||||
This function can take significant time to run, proportional
|
||||
to the number of traces in the SEG Y file.
|
||||
|
||||
Four catalogs will be build:
|
||||
|
||||
1. A catalog mapping trace_samples index (0-based) to the position of that
|
||||
trace_samples header in the file.
|
||||
|
||||
2. A catalog mapping trace_samples index (0-based) to the number of
|
||||
samples in that trace_samples.
|
||||
|
||||
3. A catalog mapping CDP number to the trace_samples index.
|
||||
|
||||
4. A catalog mapping an (inline, crossline) number 2-tuple to
|
||||
trace_samples index.
|
||||
|
||||
Args:
|
||||
fh: A file-like-object open in binary mode, positioned at the
|
||||
start of the first trace_samples header.
|
||||
|
||||
bps: The number of bytes per sample, such as obtained by a call
|
||||
to bytes_per_sample()
|
||||
|
||||
endian: '>' for big-endian data (the standard and default), '<'
|
||||
for little-endian (non-standard)
|
||||
|
||||
progress: A unary callable which will be passed a number
|
||||
between zero and one indicating the progress made. If
|
||||
provided, this callback will be invoked at least once with
|
||||
an argument equal to 1
|
||||
|
||||
Returns:
|
||||
A 4-tuple of the form (trace_samples-offset-catalog,
|
||||
trace_samples-length-catalog,
|
||||
cdp-catalog,
|
||||
line-catalog)` where
|
||||
each catalog is an instance of ``collections.Mapping`` or None
|
||||
if no catalog could be built.
|
||||
"""
|
||||
progress_callback = progress if progress is not None else lambda p: None
|
||||
|
||||
if not callable(progress_callback):
|
||||
raise TypeError("catalog_traces(): progress callback must be callable")
|
||||
|
||||
trace_header_format = compile_trace_header_format(endian)
|
||||
|
||||
length = file_length(fh)
|
||||
|
||||
pos_begin = fh.tell()
|
||||
|
||||
trace_offset_catalog_builder = CatalogBuilder()
|
||||
trace_length_catalog_builder = CatalogBuilder()
|
||||
line_catalog_builder = CatalogBuilder()
|
||||
alt_line_catalog_builder = CatalogBuilder()
|
||||
cdp_catalog_builder = CatalogBuilder()
|
||||
|
||||
for trace_number in itertools.count():
|
||||
progress_callback(_READ_PROPORTION * pos_begin / length)
|
||||
fh.seek(pos_begin)
|
||||
data = fh.read(TRACE_HEADER_NUM_BYTES)
|
||||
if len(data) < TRACE_HEADER_NUM_BYTES:
|
||||
break
|
||||
trace_header = TraceHeader._make(trace_header_format.unpack(data))
|
||||
num_samples = trace_header.ns
|
||||
trace_length_catalog_builder.add(trace_number, num_samples)
|
||||
samples_bytes = num_samples * bps
|
||||
trace_offset_catalog_builder.add(trace_number, pos_begin)
|
||||
# Should we check the data actually exists?
|
||||
line_catalog_builder.add((trace_header.Inline3D,
|
||||
trace_header.Crossline3D),
|
||||
trace_number)
|
||||
alt_line_catalog_builder.add((trace_header.TraceSequenceFile,
|
||||
trace_header.cdp),
|
||||
trace_number)
|
||||
cdp_catalog_builder.add(trace_header.cdp, trace_number)
|
||||
pos_end = pos_begin + TRACE_HEADER_NUM_BYTES + samples_bytes
|
||||
pos_begin = pos_end
|
||||
|
||||
progress_callback(_READ_PROPORTION)
|
||||
|
||||
trace_offset_catalog = trace_offset_catalog_builder.create()
|
||||
progress_callback(_READ_PROPORTION + (_READ_PROPORTION / 4))
|
||||
|
||||
trace_length_catalog = trace_length_catalog_builder.create()
|
||||
progress_callback(_READ_PROPORTION + (_READ_PROPORTION / 2))
|
||||
|
||||
cdp_catalog = cdp_catalog_builder.create()
|
||||
progress_callback(_READ_PROPORTION + (_READ_PROPORTION * 3 / 4))
|
||||
|
||||
line_catalog = line_catalog_builder.create()
|
||||
|
||||
if line_catalog is None:
|
||||
# Some 3D files put Inline and Crossline numbers in (TraceSequenceFile, cdp) pair
|
||||
line_catalog = alt_line_catalog_builder.create()
|
||||
|
||||
progress_callback(1)
|
||||
|
||||
return (trace_offset_catalog,
|
||||
trace_length_catalog,
|
||||
cdp_catalog,
|
||||
line_catalog)
|
||||
|
||||
|
||||
def read_trace_header(fh, trace_header_format, pos=None):
|
||||
"""Read a trace_samples header.
|
||||
|
||||
Args:
|
||||
fh: A file-like-object open in binary mode.
|
||||
|
||||
trace_header_format: A Struct object, such as obtained from a
|
||||
call to compile_trace_header_format()
|
||||
|
||||
pos: The file offset in bytes from the beginning from which the data
|
||||
is to be read.
|
||||
|
||||
Returns:
|
||||
A TraceHeader object.
|
||||
"""
|
||||
if pos is not None:
|
||||
fh.seek(pos)
|
||||
data = fh.read(TRACE_HEADER_NUM_BYTES)
|
||||
trace_header = TraceHeader._make(
|
||||
trace_header_format.unpack(data))
|
||||
return trace_header
|
||||
|
||||
|
||||
def read_binary_values(fh, pos=None, ctype='int32', count=1, endian='>'):
|
||||
"""Read a series of values from a binary file.
|
||||
|
||||
Args:
|
||||
fh: A file-like-object open in binary mode.
|
||||
|
||||
c
|
||||
|
||||
ctype: The SEG Y data type.
|
||||
|
||||
number: The number of items to be read.
|
||||
Returns:
|
||||
A sequence containing count items.
|
||||
"""
|
||||
fmt = CTYPES[ctype]
|
||||
item_size = size_in_bytes(fmt)
|
||||
block_size = item_size * count
|
||||
|
||||
fh.seek(pos, os.SEEK_SET)
|
||||
buf = fh.read(block_size)
|
||||
|
||||
if len(buf) < block_size:
|
||||
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, fmt, endian))
|
||||
assert len(values) == count
|
||||
return values
|
||||
|
||||
|
||||
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)
|
||||
|
||||
count: The number of floats to be read.
|
||||
|
||||
Returns:
|
||||
A sequence of floats.
|
||||
"""
|
||||
return [IBMFloat.from_bytes(data[i: i+4]) for i in range(0, count * 4, 4)]
|
||||
|
||||
|
||||
def unpack_values(buf, count, 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)
|
||||
|
||||
count: The number of floats to be read.
|
||||
|
||||
fmt: A format code (one of the values in the datatype.CTYPES
|
||||
dictionary)
|
||||
|
||||
endian: '>' for big-endian data (the standard and default), '<'
|
||||
for little-endian (non-standard)
|
||||
|
||||
Returns:
|
||||
A sequence of objects with type corresponding to the format code.
|
||||
"""
|
||||
a = array(fmt, buf)
|
||||
if endian != NATIVE_ENDIANNESS:
|
||||
a.byteswap()
|
||||
return a
|
||||
|
||||
|
||||
def format_standard_textual_header(revision, **kwargs):
|
||||
"""Produce a standard SEG Y textual header.
|
||||
|
||||
Args:
|
||||
revision: The SEG Y revision.
|
||||
|
||||
**kwargs: Named arguments corresponding to the values in the
|
||||
textual_reel_header_definition.TEMPLATE_FIELD_NAMES dictionary,
|
||||
which in turn correspond to the placeholders in the
|
||||
textual_reel_header_definition.TEMPLATE string. Any omitted
|
||||
arguments will result in placeholders being replaced by spaces.
|
||||
If the end_marker argument is not supplied, an appropriate end
|
||||
marker will be selected based on the SEG Y revision. For standard
|
||||
end markers consider using textual_reel_header_definition.END_TEXTUAL_HEADER
|
||||
or textual_reel_header_definition.END_EBCDIC.
|
||||
|
||||
Returns:
|
||||
A list of forty Unicode strings.
|
||||
|
||||
Usage:
|
||||
header = format_standard_textual_header(1,
|
||||
client="Lundin",
|
||||
company="Western Geco",
|
||||
crew_number=123,
|
||||
processing1="Sixty North AS",
|
||||
sweep_start_hz=10,
|
||||
sweep_end_hz=1000,
|
||||
sweep_length_ms=10000,
|
||||
sweep_channel_number=3,
|
||||
sweep_type='spread')
|
||||
|
||||
"""
|
||||
|
||||
kwargs.setdefault('end_marker', textual_reel_header_definition.END_MARKERS[revision])
|
||||
|
||||
template = textual_reel_header_definition.TEMPLATE
|
||||
|
||||
placeholder_slices = parse_template(template)
|
||||
background_slices = complementary_intervals(placeholder_slices.values(), 0, len(template))
|
||||
|
||||
chunks = []
|
||||
for bg_slice, placeholder in izip_longest(background_slices, placeholder_slices.items()):
|
||||
|
||||
if bg_slice is not None:
|
||||
chunks.append(template[bg_slice])
|
||||
|
||||
if placeholder is not None:
|
||||
ph_name, ph_slice = placeholder
|
||||
ph_arg_name = textual_reel_header_definition.TEMPLATE_FIELD_NAMES[ph_name]
|
||||
ph_value = kwargs.pop(ph_arg_name, '')
|
||||
ph_len = ph_slice.stop - ph_slice.start
|
||||
substitute = str(ph_value)[:ph_len].ljust(ph_len, ' ')
|
||||
chunks.append(substitute)
|
||||
|
||||
if len(kwargs) > 0:
|
||||
raise TypeError("The following keyword arguments did not correspond to template placeholders: {!r}"
|
||||
.format(list(kwargs.keys())))
|
||||
|
||||
concatenation = ''.join(chunks)
|
||||
lines = concatenation.splitlines(keepends=False)
|
||||
|
||||
return lines[1:] # Omit the first and last lines, which are artifacts of the multi-line string template
|
||||
|
||||
|
||||
_TEMPLATE_PATTERN = r'\{\s*(\w*)\s*\}'
|
||||
_TEMPLATE_REGEX = re.compile(_TEMPLATE_PATTERN)
|
||||
|
||||
|
||||
def parse_template(template):
|
||||
"""Parse a template to produce a dictionary of placeholders.
|
||||
|
||||
Args:
|
||||
template: The template string containing { field-name } style fixed-width fields.
|
||||
|
||||
Returns:
|
||||
A OrderedDict mapping field names to slices objects which can be used to index
|
||||
into the template string. The order of the entries is the same as the order within
|
||||
which they occur in the template.
|
||||
"""
|
||||
matches = _TEMPLATE_REGEX.finditer(template)
|
||||
|
||||
fields = OrderedDict()
|
||||
for match in matches:
|
||||
name = match.group(1)
|
||||
start = match.start()
|
||||
end = match.end()
|
||||
fields[name] = slice(start, end)
|
||||
|
||||
return fields
|
||||
|
||||
|
||||
def write_textual_reel_header(fh, lines, encoding):
|
||||
"""Write the SEG Y card image header, also known as the textual header
|
||||
|
||||
Args:
|
||||
fh: A file-like object open in binary mode positioned such that the
|
||||
beginning of the textual header will be the next byte to read.
|
||||
|
||||
lines: An iterable series of forty lines, each of which must be a
|
||||
Unicode string of CARD_LENGTH characters. The first three characters
|
||||
of each line are often "C 1" to "C40" (as required by the SEG Y
|
||||
standard) although this is not enforced by this function, since
|
||||
many widespread SEG Y readers and writers do not adhere to this
|
||||
constraint. To produce a SEG Y compliant series of header lines
|
||||
consider using the format_standard_textual_header() function.
|
||||
|
||||
Any lines longer than CARD_LENGTH characters will be truncated without
|
||||
warning. Any excess lines over CARDS_PER_HEADER will be discarded. Short
|
||||
or omitted lines will be padded with spaces.
|
||||
|
||||
encoding: Typically 'cp037' for EBCDIC or 'ascii' for ASCII.
|
||||
|
||||
Post-condition:
|
||||
The file pointer in fh will be positioned at the first byte following the textual
|
||||
header.
|
||||
|
||||
Raises:
|
||||
UnsupportedEncodingError: If encoding is neither EBCDIC nor ASCII.
|
||||
UnicodeError: If the data provided in lines cannot be encoded with the encoding.
|
||||
"""
|
||||
if not is_supported_encoding(encoding):
|
||||
raise UnsupportedEncodingError("Writing textual reel header", encoding)
|
||||
|
||||
fh.seek(0)
|
||||
|
||||
padded_lines = [line.encode(encoding).ljust(CARD_LENGTH, ' '.encode(encoding))[:CARD_LENGTH]
|
||||
for line in pad(lines, padding='', size=CARDS_PER_HEADER)]
|
||||
joined_header = EMPTY_BYTE_STRING.join(padded_lines)
|
||||
assert len(joined_header) == 3200
|
||||
fh.write(joined_header)
|
||||
|
||||
fh.seek(TEXTUAL_HEADER_NUM_BYTES)
|
||||
|
||||
|
||||
def write_binary_reel_header(fh, binary_reel_header, endian='>'):
|
||||
"""Write the binary_reel_header to the given file-like object.
|
||||
|
||||
Args:
|
||||
fh: A file-like object open in binary mode for writing.
|
||||
|
||||
binary_reel_header: A dictionary of values using a subset of the keys
|
||||
in binary_reel_header_definition.HEADER_DEF associated with
|
||||
compatible values.
|
||||
|
||||
Post-condition:
|
||||
The file pointer for fh will be positioned at the first byte following
|
||||
the binary reel header.
|
||||
"""
|
||||
|
||||
for key in HEADER_DEF:
|
||||
pos = HEADER_DEF[key]['pos']
|
||||
ctype = HEADER_DEF[key]['type']
|
||||
value = binary_reel_header[key] if key in binary_reel_header else HEADER_DEF[key]['def']
|
||||
write_binary_values(fh, [value], ctype, pos, endian)
|
||||
|
||||
fh.seek(REEL_HEADER_NUM_BYTES)
|
||||
|
||||
|
||||
def format_extended_textual_header(text, encoding, include_text_stop=False):
|
||||
"""Format a string into pages and line suitable for an extended textual header.
|
||||
|
||||
Args
|
||||
text: An arbitrary text string. Any universal newlines will be preserved.
|
||||
|
||||
encoding: Either ASCII ('ascii') or EBCDIC ('cp037')
|
||||
|
||||
include_text_stop: If True, a text stop stanza header will be appended, otherwise not.
|
||||
"""
|
||||
|
||||
if not is_supported_encoding(encoding):
|
||||
raise UnsupportedEncodingError("Extended textual header", encoding)
|
||||
|
||||
# According to the standard: "The Extended Textual File Header consists of one or more 3200-byte records, each
|
||||
# record containing 40 lines of textual card-image text." It goes on "... Each line in an Extended Textual File
|
||||
# Header ends in carriage return and linefeed (EBCDIC 0D25 or ASCII 0D0A)." Given that we're dealing with fixed-
|
||||
# length (80 byte) lines, this implies that we have 78 bytes of space into which we can encode the content of each
|
||||
# line, which must be left-justified and padded with spaces.
|
||||
|
||||
width = CARD_LENGTH - len(HEADER_NEWLINE)
|
||||
original_lines = text.splitlines()
|
||||
|
||||
# Split overly long lines (i.e. > 78) and pad too-short lines with spaces
|
||||
lines = []
|
||||
for original_line in original_lines:
|
||||
padded_lines = (pad_and_terminate_header_line(original_line[i:i+width], width)
|
||||
for i in range(0, len(original_line), width))
|
||||
lines.extend(padded_lines)
|
||||
|
||||
pages = list(batched(lines, 40, pad_and_terminate_header_line('', width)))
|
||||
|
||||
if include_text_stop:
|
||||
stop_page = format_extended_textual_header(END_TEXT_STANZA, encoding)[0]
|
||||
pages.append(stop_page)
|
||||
|
||||
return pages
|
||||
|
||||
|
||||
def pad_and_terminate_header_line(line, width):
|
||||
return line.ljust(width, ' ') + HEADER_NEWLINE
|
||||
|
||||
|
||||
def write_extended_textual_headers(fh, pages, encoding):
|
||||
"""Write extended textual headers.
|
||||
|
||||
Args:
|
||||
fh: A file-like object open in binary mode for writing.
|
||||
|
||||
pages: An iterables series of sequences of Unicode strings, where the outer iterable
|
||||
represents 3200 byte pages, each comprised of a sequence of exactly 40 strings of nominally 80 characters
|
||||
each. Although Unicode strings are accepted, and when encoded they should result in exact 80 bytes
|
||||
sequences. To produce a valid data structure for pages, consider using format_extended_textual_header()
|
||||
|
||||
encoding: Either 'cp037' for EBCDIC or 'ascii' for ASCII.
|
||||
|
||||
Post-condition:
|
||||
The file pointer in fh will be position at the first byte after the extended textual headers, which is
|
||||
also the first byte of the first trace-header.
|
||||
|
||||
Raises:
|
||||
ValueError: If the provided header data has the wrong shape.
|
||||
UnicodeError: If the textual data could not be encoded into the specified encoding.
|
||||
|
||||
"""
|
||||
|
||||
if not is_supported_encoding(encoding):
|
||||
raise UnsupportedEncodingError("Writing extended textual header", encoding)
|
||||
|
||||
fh.seek(REEL_HEADER_NUM_BYTES)
|
||||
|
||||
encoded_pages = []
|
||||
for page_index, page in enumerate(pages):
|
||||
encoded_page = []
|
||||
# TODO: Share some of this code with writing the textual reel header.
|
||||
for line_index, line in enumerate(page):
|
||||
encoded_line = line.encode(encoding)
|
||||
num_encoded_bytes = len(encoded_line)
|
||||
if num_encoded_bytes != CARD_LENGTH:
|
||||
raise ValueError("Extended textual header line {} of page {} at {} bytes is not "
|
||||
"{} bytes".format(line_index, page_index, num_encoded_bytes, CARD_LENGTH))
|
||||
encoded_page.append(encoded_line)
|
||||
num_encoded_lines = len(encoded_page)
|
||||
if num_encoded_lines != CARDS_PER_HEADER:
|
||||
raise ValueError("Extended textual header page {} number of "
|
||||
"lines {} is not {}".format(num_encoded_lines, CARDS_PER_HEADER))
|
||||
encoded_pages.append(encoded_page)
|
||||
|
||||
for encoded_page in encoded_pages:
|
||||
concatenated_page = EMPTY_BYTE_STRING.join(encoded_page)
|
||||
assert(len(concatenated_page) == TEXTUAL_HEADER_NUM_BYTES)
|
||||
fh.write(concatenated_page)
|
||||
|
||||
|
||||
def write_trace_header(fh, trace_header, trace_header_format, pos=None):
|
||||
"""Write a TraceHeader to file.
|
||||
|
||||
Args:
|
||||
fh: A file-like object open in binary mode for writing.
|
||||
|
||||
trace_header: A TraceHeader object.
|
||||
|
||||
trace_header_format: A Struct object, such as obtained from a
|
||||
call to compile_trace_header_format()
|
||||
|
||||
pos: An optional file offset in bytes from the beginning of the
|
||||
file. Defaults to the current file position.
|
||||
"""
|
||||
if pos is not None:
|
||||
fh.seek(pos, os.SEEK_SET)
|
||||
buf = trace_header_format.pack(*trace_header)
|
||||
fh.write(buf)
|
||||
|
||||
|
||||
def write_trace_samples(fh, samples, ctype='l', pos=None, endian='>'):
|
||||
"""Write a trace samples to a file
|
||||
|
||||
Args:
|
||||
fh: A file-like-object open for writing in binary mode.
|
||||
|
||||
values: An iterable series of values.
|
||||
|
||||
ctype: The SEG Y data type.
|
||||
|
||||
pos: An optional offset from the beginning of the file. If omitted,
|
||||
any writing is done at the current file position.
|
||||
|
||||
endian: '>' for big-endian data (the standard and default), '<'
|
||||
for little-endian (non-standard)
|
||||
"""
|
||||
write_binary_values(fh, samples, ctype, pos, endian)
|
||||
|
||||
|
||||
def write_binary_values(fh, values, ctype, pos=None, endian='>'):
|
||||
"""Write a series of values to a file.
|
||||
|
||||
Args:
|
||||
fh: A file-like-object open for writing in binary mode.
|
||||
|
||||
values: An iterable series of values.
|
||||
|
||||
ctype: The SEG Y data type.
|
||||
|
||||
pos: An optional offset from the beginning of the file. If omitted,
|
||||
any writing is done at the current file position.
|
||||
|
||||
endian: '>' for big-endian data (the standard and default), '<'
|
||||
for little-endian (non-standard)
|
||||
"""
|
||||
fmt = CTYPES[ctype]
|
||||
|
||||
if pos is not None:
|
||||
fh.seek(pos, os.SEEK_SET)
|
||||
|
||||
buf = (pack_ibm_floats(values)
|
||||
if fmt == 'ibm'
|
||||
else pack_values(values, fmt, endian))
|
||||
|
||||
fh.write(buf)
|
||||
|
||||
|
||||
def pack_ibm_floats(values):
|
||||
"""Pack floats into binary-encoded big-endian single-precision IBM floats.
|
||||
|
||||
Args:
|
||||
values: An iterable series of numeric values.
|
||||
|
||||
Returns:
|
||||
A sequence of bytes. (Python 2 - a str object, Python 3 - a bytes
|
||||
object)
|
||||
"""
|
||||
return EMPTY_BYTE_STRING.join(bytes(IBMFloat.from_real(value)) for value in values)
|
||||
|
||||
|
||||
def pack_values(values, fmt, endian='>'):
|
||||
"""Pack values into binary encoded big-endian byte strings.
|
||||
|
||||
Args:
|
||||
values: An iterable series of values.
|
||||
|
||||
fmt: A format code (one of the values in the datatype.CTYPES
|
||||
dictionary)
|
||||
|
||||
endian: '>' for big-endian data (the standard and default), '<'
|
||||
for little-endian (non-standard)
|
||||
"""
|
||||
c_format = '{}{}{}'.format(endian, len(values), fmt)
|
||||
return struct.pack(c_format, *values)
|
||||
|
||||
|
||||
# TODO: Consider generalising the below to also produce a ReelHeader record. Then modify
|
||||
# read_binary_reel_header() to return such a record, and write_binary_reel_header() to accept such
|
||||
# a record.
|
||||
|
||||
|
||||
_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_samples 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_samples 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)
|
||||
|
||||
fmt = [endian]
|
||||
length = 0
|
||||
for record_spec in record_specs:
|
||||
|
||||
shortfall = length - record_spec.pos
|
||||
if shortfall:
|
||||
fmt.append(str(shortfall) + 'x') # Ignore bytes
|
||||
length += shortfall
|
||||
|
||||
ctype = CTYPES[record_spec.type]
|
||||
fmt.append(ctype)
|
||||
length += size_in_bytes(ctype)
|
||||
|
||||
assert length == TRACE_HEADER_NUM_BYTES
|
||||
|
||||
return struct.Struct(''.join(fmt))
|
||||
|
||||
|
||||
def _compile_trace_header_record():
|
||||
"""Build a TraceHeader namedtuple from the trace_samples 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))
|
||||
|
||||
|
||||
TraceHeader = _compile_trace_header_record()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from revisions import SEGY_REVISION_0, SEGY_REVISION_1
|
||||
from segpy.revisions import SEGY_REVISION_0, SEGY_REVISION_1
|
||||
|
||||
TRACE_HEADER_DEF = {"TraceSequenceLine": {"pos": 0, "type": "int32"}}
|
||||
TRACE_HEADER_DEF["TraceSequenceFile"] = {"pos": 4, "type": "int32"}
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
import itertools
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
|
||||
from segpy.portability import izip
|
||||
|
||||
NATIVE_ENDIANNESS = '<' if sys.byteorder == 'little' else '>'
|
||||
|
||||
UNSET = object()
|
||||
|
||||
def pairwise(iterable):
|
||||
"""Pairwise iteration.
|
||||
|
||||
Args:
|
||||
iterable: An iterable series.
|
||||
|
||||
Returns:
|
||||
An iterator over 2-tuples.
|
||||
"""
|
||||
a, b = itertools.tee(iterable)
|
||||
next(b, None)
|
||||
return izip(a, b)
|
||||
|
||||
|
||||
def batched(iterable, batch_size, padding=UNSET):
|
||||
"""Batch an iterable series into equal sized batches.
|
||||
|
||||
Args:
|
||||
iterable: The series to be batched.
|
||||
|
||||
batch_size: The size of the batch. Must be at least one.
|
||||
|
||||
padding: Optional value used to pad the final batch to batch_size. If
|
||||
omitted, the final batch may be smaller than batch_size.
|
||||
|
||||
Yields:
|
||||
A series of lists, each containing batch_size items from iterable.
|
||||
|
||||
Raises:
|
||||
ValueError: If batch_size is less than one.
|
||||
"""
|
||||
if batch_size < 1:
|
||||
raise ValueError("Batch size {} is not at least one.".format(batch_size))
|
||||
|
||||
pending = []
|
||||
|
||||
for item in iterable:
|
||||
pending.append(item)
|
||||
if len(pending) == batch_size:
|
||||
batch = pending
|
||||
pending = []
|
||||
yield batch
|
||||
|
||||
num_left_over = len(pending)
|
||||
if num_left_over > 0:
|
||||
if padding is not UNSET:
|
||||
pending.extend([padding] * (batch_size - num_left_over))
|
||||
yield pending
|
||||
|
||||
|
||||
def pad(iterable, padding=None, size=None):
|
||||
if size is None:
|
||||
return itertools.chain(iterable, itertools.repeat(padding))
|
||||
return itertools.islice(pad(iterable, padding), size)
|
||||
|
||||
|
||||
def complementary_intervals(intervals, start=None, stop=None):
|
||||
"""Compute a complementary set of intervals which alternate with given intervals to form a contiguous range.
|
||||
|
||||
Given,
|
||||
|
||||
Start Stop
|
||||
[-----) [-----) [----)
|
||||
|
||||
produces,
|
||||
|
||||
[--) [----) [-) [---)
|
||||
|
||||
Args:
|
||||
intervals: An sequence of at least one existing slices or ranges. The type of the first interval (slice or
|
||||
range) is used as the result type.
|
||||
start: An optional start index, defaults to the start of the first slice.
|
||||
stop: An optional one-beyond-the-end index, defaults to the stop attribute of the last slice.
|
||||
|
||||
Yields:
|
||||
A complementary series of slices which alternate with the supplied slices. The number of returned
|
||||
slices will always be len(slices) + 1 since both leading and trailing slices will always be returned.
|
||||
Note the some of the returned slices may be 'empty' (having zero length).
|
||||
"""
|
||||
if len(intervals) < 1:
|
||||
raise ValueError("intervals must contain at least one interval (slice or range) object")
|
||||
|
||||
interval_type = type(intervals[0])
|
||||
|
||||
if start is None:
|
||||
start = intervals[0].start
|
||||
|
||||
if stop is None:
|
||||
stop = intervals[-1].stop
|
||||
|
||||
index = start
|
||||
for s in intervals:
|
||||
yield interval_type(index, s.start)
|
||||
index = s.stop
|
||||
|
||||
yield interval_type(index, stop)
|
||||
|
||||
|
||||
def roundrobin(*iterables):
|
||||
"""Take items from each iterable in turn until all iterables are exhausted.
|
||||
|
||||
roundrobin('ABC', 'D', 'EF') --> A D E B F C
|
||||
"""
|
||||
# Recipe credited to George Sakkis
|
||||
pending = len(iterables)
|
||||
nexts = itertools.cycle(iter(it).__next__ for it in iterables)
|
||||
while pending:
|
||||
try:
|
||||
for n in nexts:
|
||||
yield n()
|
||||
except StopIteration:
|
||||
pending -= 1
|
||||
nexts = itertools.cycle(itertools.islice(nexts, pending))
|
||||
|
||||
|
||||
def contains_duplicates(sorted_iterable):
|
||||
"""Determine in an iterable series contains duplicates.
|
||||
|
||||
Args:
|
||||
sorted_iterable: Any iterable series which must be sorted in either
|
||||
ascending or descending order.
|
||||
|
||||
Returns:
|
||||
True if sorted_iterable contains duplicates, otherwise False.
|
||||
"""
|
||||
for a, b in pairwise(sorted_iterable):
|
||||
if a == b:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def measure_stride(iterable):
|
||||
"""Determine whether successive numeric items differ by a constant amount.
|
||||
|
||||
Args:
|
||||
iterable: An iterable series of numeric values.
|
||||
|
||||
Returns:
|
||||
The difference between successive values (e.g. item[1] - item[0]) if
|
||||
that difference is the same between all successive pairs, otherwise
|
||||
None.
|
||||
"""
|
||||
stride = None
|
||||
for a, b in pairwise(iterable):
|
||||
new_stride = b - a
|
||||
if stride is None:
|
||||
stride = new_stride
|
||||
elif stride != new_stride:
|
||||
return None
|
||||
return stride
|
||||
|
||||
def minmax(iterable):
|
||||
"""Return the minimum and maximum of an iterable series.
|
||||
|
||||
This function requires only a single pass over the data.
|
||||
|
||||
Args:
|
||||
iterable: An iterable series for which to determine the minimum and
|
||||
maximum values.
|
||||
|
||||
Returns:
|
||||
A 2-tuple containing the minimum and maximum values.
|
||||
"""
|
||||
iterator = iter(iterable)
|
||||
try:
|
||||
first = next(iterator)
|
||||
except StopIteration:
|
||||
raise ValueError("minmax() arg is an empty iterable series")
|
||||
minimum = first
|
||||
maximum = first
|
||||
for item in iterator:
|
||||
minimum = min(minimum, item)
|
||||
maximum = max(maximum, item)
|
||||
return minimum, maximum
|
||||
|
||||
|
||||
def file_length(fh):
|
||||
"""Determine the length of a file-like object in bytes.
|
||||
|
||||
Args:
|
||||
fh: A seekable file-like-object.
|
||||
|
||||
Returns:
|
||||
An integer length in bytes.
|
||||
"""
|
||||
pos = fh.tell()
|
||||
try:
|
||||
fh.seek(0, os.SEEK_END)
|
||||
return fh.tell()
|
||||
finally:
|
||||
fh.seek(pos, os.SEEK_SET)
|
||||
|
||||
|
||||
def filename_from_handle(fh):
|
||||
"""Determine the name of the file underlying a file-like object.
|
||||
|
||||
Args:
|
||||
fh: A file-like object.
|
||||
|
||||
Returns:
|
||||
A string containing the file name, or '<unknown>' if it could not
|
||||
be determined.
|
||||
"""
|
||||
try:
|
||||
return fh.name
|
||||
except AttributeError:
|
||||
return '<unknown>'
|
||||
|
||||
|
||||
def now_millis():
|
||||
millis = int(round(time.time() * 1000))
|
||||
return millis
|
||||
|
||||
|
||||
def round_up(integer, multiple):
|
||||
"""Round up to the nearest multiple"""
|
||||
return integer if integer % multiple == 0 else integer + multiple - integer % multiple
|
||||
|
||||
|
||||
def underscores_to_camelcase(s):
|
||||
"""Convert text_in_this_style to TextInThisStyle."""
|
||||
return ''.join(w.capitalize() for w in s.split('_'))
|
||||
|
||||
|
||||
def almost_equal(x, y, epsilon):
|
||||
max_xy_one = max(1.0, abs(x), abs(y))
|
||||
e = epsilon * max_xy_one
|
||||
delta = abs(x - y)
|
||||
return delta <= e
|
||||
@@ -0,0 +1,58 @@
|
||||
from segpy.encoding import ASCII, is_supported_encoding, UnsupportedEncodingError
|
||||
from segpy.toolkit import (write_textual_reel_header, write_binary_reel_header, compile_trace_header_format,
|
||||
write_trace_header, write_trace_samples, format_extended_textual_header,
|
||||
write_extended_textual_headers)
|
||||
|
||||
|
||||
def write_segy(fh,
|
||||
seg_y_data,
|
||||
encoding=None,
|
||||
endian='>',
|
||||
progress=None):
|
||||
"""
|
||||
Args:
|
||||
fh: A file-like object open for binary write.
|
||||
|
||||
seg_y_data: An object from which the headers and trace_samples data can be retrieved. Requires the following
|
||||
properties and methods:
|
||||
seg_y_data.textual_reel_header
|
||||
seg_y_data.binary_reel_header
|
||||
seg_y_data.extended_textual_header
|
||||
seg_y_data.trace_indexes
|
||||
seg_y_data.trace_header(trace_index)
|
||||
seg_y_data.trace_samples(trace_index)
|
||||
|
||||
seg_y_data.encoding
|
||||
seg_y_data.endian
|
||||
|
||||
One such legitimate object would be a SegYReader instance.
|
||||
|
||||
encoding: Optional encoding for text data. Typically 'cp037' for EBCDIC or 'ascii' for ASCII. If omitted, the
|
||||
seg_y_data object will be queries for an encoding property.
|
||||
|
||||
endian: Big endian by default. If omitted, the seg_y_data object will be queried for an encoding property.
|
||||
|
||||
progress: An optional progress bar object.
|
||||
|
||||
Raises:
|
||||
UnsupportedEncodingError: If the specified encoding is neither ASCII nor EBCDIC
|
||||
UnicodeError: If textual data provided cannot be encoded into the required encoding.
|
||||
"""
|
||||
|
||||
encoding = encoding or (hasattr(seg_y_data, 'encoding') and seg_y_data.encoding) or ASCII
|
||||
|
||||
if not is_supported_encoding(encoding):
|
||||
raise UnsupportedEncodingError("Writing SEG Y", encoding)
|
||||
|
||||
write_textual_reel_header(fh, seg_y_data.textual_reel_header, encoding)
|
||||
write_binary_reel_header(fh, seg_y_data.binary_reel_header, endian)
|
||||
write_extended_textual_headers(fh, seg_y_data.extended_textual_header, encoding)
|
||||
|
||||
trace_header_format = compile_trace_header_format(endian)
|
||||
|
||||
for trace_index in seg_y_data.trace_indexes():
|
||||
write_trace_header(fh, seg_y_data.trace_header(trace_index), trace_header_format)
|
||||
write_trace_samples(fh, seg_y_data.trace_samples(trace_index), seg_y_data.data_sample_format, endian=endian)
|
||||
|
||||
|
||||
|
||||
@@ -1,511 +0,0 @@
|
||||
"""
|
||||
A Python module for reading/writing/manipulating
|
||||
SEG-Y formatted files
|
||||
|
||||
segy.readSegy : Read SEGY file
|
||||
segy.read_reel_header : Get SEGY header
|
||||
segy.read_trace_header : Get SEGY Trace header
|
||||
segy.read_all_trace_headers : Get all SEGY Trace headers
|
||||
segy.getSegyTrace : Get SEGY Trace header and trace data for one trace
|
||||
|
||||
segy.writeSegy : Write a data to a SEGY file
|
||||
segy.writeSegyStructure : Writes a segpy data structure to a SEGY file
|
||||
|
||||
segy.read_binary_value : Get a value from a binary string
|
||||
segy.ibm2ieee : Convert IBM floats to IEEE
|
||||
|
||||
segy.version : The version of SegyPY
|
||||
segy.verbose : Amount of verbose information to the screen
|
||||
"""
|
||||
#
|
||||
# segpy : A Python module for reading and writing SEG-Y formatted data
|
||||
#
|
||||
# Forked by Robert Smallshire from the original segypy by
|
||||
#
|
||||
# (C) Thomas Mejer Hansen, 2005-2006
|
||||
#
|
||||
# with contributions from Pete Forman and Andrew Squelch 2007
|
||||
import os
|
||||
|
||||
import sys
|
||||
import struct
|
||||
import logging
|
||||
|
||||
from numpy import (transpose, reshape, zeros, arange)
|
||||
|
||||
from revisions import canonicalize_revision
|
||||
from header_definition import HEADER_DEF
|
||||
from trace_header_definition import TRACE_HEADER_DEF
|
||||
from ibm_float import ibm2ieee2
|
||||
|
||||
FORMAT = '%(asctime)-15s %(levelname)s %(message)s'
|
||||
logging.basicConfig(level=logging.INFO, format=FORMAT)
|
||||
logger = logging.getLogger('segpy.segypy')
|
||||
|
||||
version = '0.3.1'
|
||||
|
||||
REEL_HEADER_NUM_BYTES = 3600
|
||||
TRACE_HEADER_NUM_BYTES = 240
|
||||
|
||||
l_char = struct.calcsize('c')
|
||||
l_uchar = struct.calcsize('B')
|
||||
l_float = struct.calcsize('f')
|
||||
|
||||
|
||||
DATA_SAMPLE_FORMAT = {1: 'ibm',
|
||||
2: 'l',
|
||||
3: 'h',
|
||||
5: 'f',
|
||||
8: 'B'}
|
||||
|
||||
CTYPES = {'l': 'l', 'long': 'l', 'int32': 'l',
|
||||
'L': 'L', 'ulong': 'L', 'uint32': 'L',
|
||||
'h': 'h', 'short': 'h', 'int16': 'h',
|
||||
'H': 'H', 'ushort': 'H', 'uint16': 'H',
|
||||
'c': 'c', 'char': 'c',
|
||||
'B': 'B', 'uchar': 'B',
|
||||
'f': 'f', 'float': 'f',
|
||||
'ibm': 'ibm'}
|
||||
|
||||
# TODO This is redundant with data in the SH_def below
|
||||
CTYPE_DESCRIPTION = {'ibm': 'IBM float',
|
||||
'l': '32 bit integer',
|
||||
'h': '16 bit integer',
|
||||
'f': 'IEEE float',
|
||||
'B': '8 bit char'}
|
||||
|
||||
|
||||
def size_in_bytes(ctype):
|
||||
if ctype == 'l' and struct.calcsize(ctype) == 8:
|
||||
return 4 # 64-bit issue?
|
||||
return struct.calcsize(ctype) if ctype != 'ibm' else struct.calcsize('f')
|
||||
|
||||
|
||||
def get_default_segy_header(ntraces=100, ns=100):
|
||||
"""
|
||||
header = getDefaultSegyHeader()
|
||||
"""
|
||||
# TraceSequenceLine
|
||||
header = {'Job': {'pos': 3200, 'type': 'int32', 'def': 0}}
|
||||
|
||||
for key in HEADER_DEF:
|
||||
header[key] = HEADER_DEF[key].get('def', 0)
|
||||
|
||||
header['ntraces'] = ntraces
|
||||
header['ns'] = ns
|
||||
|
||||
return header
|
||||
|
||||
|
||||
def get_default_segy_trace_headers(ntraces=100, ns=100, dt=1000):
|
||||
"""
|
||||
SH = getDefaultSegyTraceHeader()
|
||||
"""
|
||||
# INITIALIZE DICTIONARY
|
||||
trace_header = {'TraceSequenceLine': {'pos': 0, 'type': 'int32'}}
|
||||
|
||||
for key in TRACE_HEADER_DEF:
|
||||
trace_header[key] = zeros(ntraces)
|
||||
|
||||
for a in range(ntraces):
|
||||
trace_header['TraceSequenceLine'][a] = a + 1
|
||||
trace_header['TraceSequenceFile'][a] = a + 1
|
||||
trace_header['FieldRecord'][a] = 1000
|
||||
trace_header['TraceNumber'][a] = a + 1
|
||||
trace_header['ns'][a] = ns
|
||||
trace_header['dt'][a] = dt
|
||||
return trace_header
|
||||
|
||||
|
||||
def read_trace_header(f, reel_header, trace_header_name='cdp', endian='>'):
|
||||
"""
|
||||
read_trace_header(reel_header, TraceHeaderName)
|
||||
"""
|
||||
|
||||
bps = get_byte_per_sample(reel_header)
|
||||
|
||||
# MAKE SOME LOOKUP TABLE THAT HOLDS THE LOCATION OF HEADERS
|
||||
trace_header_pos = TRACE_HEADER_DEF[trace_header_name]['pos']
|
||||
|
||||
# TODO: Be consistent between 'type' and 'format' here.
|
||||
trace_header_format = TRACE_HEADER_DEF[trace_header_name]['type']
|
||||
ntraces = reel_header['ntraces']
|
||||
trace_header_values = zeros(ntraces)
|
||||
binary_reader = create_binary_reader(f, trace_header_format, endian)
|
||||
start_pos = trace_header_pos + REEL_HEADER_NUM_BYTES
|
||||
stride = reel_header['ns'] * bps + TRACE_HEADER_NUM_BYTES
|
||||
end_pos = start_pos + (ntraces - 1) * stride + 1
|
||||
for i, pos in enumerate(xrange(start_pos, end_pos, stride)):
|
||||
trace_header_values[i] = binary_reader(pos)
|
||||
return trace_header_values
|
||||
|
||||
|
||||
# TODO: Get the parameter ordering of reel_header and f to be consistent
|
||||
def read_all_trace_headers(f, reel_header):
|
||||
trace_headers = {'filename': reel_header['filename']}
|
||||
|
||||
logger.debug('read_all_trace_headers : '
|
||||
'trying to get all segy trace headers')
|
||||
|
||||
for key in TRACE_HEADER_DEF.keys():
|
||||
trace_header = read_trace_header(f, reel_header, key)
|
||||
trace_headers[key] = trace_header
|
||||
logger.info("read_all_trace_headers : " + key)
|
||||
|
||||
return trace_headers
|
||||
|
||||
|
||||
def file_length(f):
|
||||
pos = f.tell()
|
||||
f.seek(0, os.SEEK_END)
|
||||
file_size = f.tell()
|
||||
f.seek(pos, os.SEEK_SET)
|
||||
return file_size
|
||||
|
||||
|
||||
def _filename(f):
|
||||
return f.name if hasattr(f, 'name') else '<unknown>'
|
||||
|
||||
|
||||
def read_segy(f, endian='>'):
|
||||
"""
|
||||
data, header, trace_headers = read_reel_header(f)
|
||||
"""
|
||||
|
||||
file_size = file_length(f)
|
||||
|
||||
logger.debug("read_segy : Length of data : {0}".format(file_size))
|
||||
|
||||
reel_header = read_reel_header(f, endian) # modified by A Squelch
|
||||
|
||||
# GET TRACE
|
||||
index = REEL_HEADER_NUM_BYTES
|
||||
bytes_per_sample = get_byte_per_sample(reel_header)
|
||||
num_data = (file_size - REEL_HEADER_NUM_BYTES) / bytes_per_sample
|
||||
|
||||
data, reel_header, trace_headers = read_traces(f,
|
||||
reel_header,
|
||||
num_data,
|
||||
bytes_per_sample,
|
||||
index,
|
||||
endian)
|
||||
|
||||
logger.debug("read_segy : Read segy data") # modified by A Squelch
|
||||
|
||||
return data, reel_header, trace_headers
|
||||
|
||||
|
||||
def read_traces(f,
|
||||
reel_header,
|
||||
num_data,
|
||||
bytes_per_sample,
|
||||
index,
|
||||
endian='>'): # added by A Squelch
|
||||
"""Read the trace data.
|
||||
|
||||
values, SegyHeader, SegyTraceHeaders = read_traces(data,
|
||||
reel_header,
|
||||
num_data,
|
||||
bytes_per_sample,
|
||||
index)
|
||||
"""
|
||||
|
||||
# Calculate number of dummy samples needed to account for Trace Headers
|
||||
num_dummy_samples = TRACE_HEADER_NUM_BYTES / bytes_per_sample
|
||||
logger.debug("read_traces : num_dummy_samples = " + str(num_dummy_samples))
|
||||
|
||||
# READ ALL SEGY TRACE HEADERS
|
||||
trace_headers = read_all_trace_headers(f, reel_header)
|
||||
|
||||
logger.info("read_traces : Reading segy data")
|
||||
|
||||
dsf = reel_header['DataSampleFormat']
|
||||
ctype = DATA_SAMPLE_FORMAT[dsf]
|
||||
description = CTYPE_DESCRIPTION[ctype]
|
||||
logger.debug("read_traces : Assuming DSF = {0}, {1}".format(
|
||||
dsf, description))
|
||||
values, _ = read_binary_value(f, index, ctype, endian, num_data)
|
||||
|
||||
logger.debug("read_traces : - reshaping")
|
||||
values = reshape(values,
|
||||
(reel_header['ntraces'],
|
||||
reel_header['ns'] + num_dummy_samples))
|
||||
logger.debug("read_traces : - stripping header dummy data")
|
||||
values = values[:, num_dummy_samples:
|
||||
(reel_header['ns'] + num_dummy_samples)]
|
||||
logger.debug("read_traces : - transposing")
|
||||
values = transpose(values)
|
||||
|
||||
# SOMEONE NEEDS TO IMPLEMENT A NICER WAY DO DEAL WITH DSF = 8
|
||||
if reel_header['DataSampleFormat'] == 8:
|
||||
for i in arange(reel_header['ntraces']):
|
||||
for j in arange(reel_header['ns']):
|
||||
if values[i][j] > 128:
|
||||
values[i][j] = values[i][j] - 256
|
||||
|
||||
logger.debug("read_traces : Finished reading segy data")
|
||||
|
||||
return values, reel_header, trace_headers
|
||||
|
||||
|
||||
def read_reel_header(f, endian='>'):
|
||||
"""
|
||||
reel_header = read_reel_header(file_handle)
|
||||
"""
|
||||
filename = _filename(f)
|
||||
reel_header = {'filename': filename}
|
||||
for key in HEADER_DEF.keys():
|
||||
pos = HEADER_DEF[key]['pos']
|
||||
format = HEADER_DEF[key]['type']
|
||||
|
||||
reel_header[key], index = read_binary_value(f, pos, format, endian)
|
||||
|
||||
logger.debug(str(pos) + " " +
|
||||
str(format) +
|
||||
" Reading " + key +
|
||||
"=" + str(reel_header[key]))
|
||||
|
||||
# SET NUMBER OF BYTES PER DATA SAMPLE
|
||||
bps = get_byte_per_sample(reel_header)
|
||||
|
||||
file_size = file_length(f)
|
||||
ntraces = (file_size - REEL_HEADER_NUM_BYTES) / \
|
||||
(reel_header['ns'] * bps + TRACE_HEADER_NUM_BYTES)
|
||||
reel_header['ntraces'] = ntraces
|
||||
|
||||
logger.debug('read_reel_header : successfully read ' + filename)
|
||||
|
||||
return reel_header
|
||||
|
||||
|
||||
def write_segy(filename, data, dt=1000, trace_header_in=None, header_in=None):
|
||||
"""
|
||||
write_segy(filename, data, dt)
|
||||
|
||||
Write SEGY
|
||||
|
||||
See also read_segy
|
||||
|
||||
(c) 2005, Thomas Mejer Hansen
|
||||
|
||||
MAKE OPTIONAL INPUT FOR ALL SEGYHTRACEHEADER VALUES
|
||||
|
||||
"""
|
||||
if header_in is None:
|
||||
header_in = {}
|
||||
if trace_header_in is None:
|
||||
trace_header_in = {}
|
||||
|
||||
logger.debug("write_segy : Trying to write " + filename)
|
||||
|
||||
shape = data.shape
|
||||
ns = shape[0]
|
||||
ntraces = shape[1]
|
||||
print ntraces, ns
|
||||
|
||||
header = get_default_segy_header(ntraces, ns)
|
||||
trace_header = get_default_segy_trace_headers(ntraces, ns, dt)
|
||||
|
||||
# Add trace_header_in, if exists...
|
||||
for key in trace_header_in.keys():
|
||||
print key
|
||||
for a in range(ntraces):
|
||||
trace_header[key] = trace_header_in[key][a]
|
||||
|
||||
# Add header_in, if exists...
|
||||
for key in header_in.keys():
|
||||
print key
|
||||
header[key] = header_in[key]
|
||||
|
||||
write_segy_structure(filename, data, header, trace_header)
|
||||
|
||||
|
||||
def write_segy_structure(filename,
|
||||
data,
|
||||
header,
|
||||
trace_header,
|
||||
endian='>'): # modified by A Squelch
|
||||
"""
|
||||
writeSegyStructure(filename, data, header, trace_header)
|
||||
|
||||
Write SEGY file using SegyPy data structures
|
||||
|
||||
See also readSegy
|
||||
|
||||
(c) 2005, Thomas Mejer Hansen
|
||||
|
||||
"""
|
||||
|
||||
logger.debug("writeSegyStructure : Trying to write " + filename)
|
||||
|
||||
f = open(filename, 'wb')
|
||||
|
||||
# VERBOSE INF
|
||||
revision = canonicalize_revision(header['SegyFormatRevisionNumber'])
|
||||
dsf = header['DataSampleFormat']
|
||||
|
||||
try: # block added by A Squelch
|
||||
data_descriptor = HEADER_DEF['DataSampleFormat']['descr'][revision][dsf]
|
||||
except KeyError:
|
||||
logging.critical(" An error has occurred interpreting a SEGY binary"
|
||||
"header key")
|
||||
logging.critical(" Please check the Endian setting for this "
|
||||
"file: {0}".format(header["filename"]))
|
||||
sys.exit()
|
||||
|
||||
logger.debug("writeSegyStructure : SEG-Y revision = " + str(revision))
|
||||
logger.debug("writeSegyStructure : DataSampleFormat = " +
|
||||
str(dsf) +
|
||||
"(" + data_descriptor + ")")
|
||||
|
||||
# WRITE SEGY HEADER
|
||||
|
||||
for key in HEADER_DEF.keys():
|
||||
pos = HEADER_DEF[key]["pos"]
|
||||
format = HEADER_DEF[key]["type"]
|
||||
value = header[key]
|
||||
put_value(value, f, pos, format, endian)
|
||||
|
||||
# SEGY TRACES
|
||||
ctype = HEADER_DEF['DataSampleFormat']['datatype'][revision][dsf]
|
||||
bps = HEADER_DEF['DataSampleFormat']['bps'][revision][dsf]
|
||||
|
||||
sizeT = TRACE_HEADER_NUM_BYTES + header['ns'] * bps
|
||||
|
||||
for itrace in range(header['ntraces']):
|
||||
index = REEL_HEADER_NUM_BYTES + itrace * sizeT
|
||||
logger.debug('Writing Trace #' +
|
||||
str(itrace + 1) +
|
||||
'/' + str(header['ntraces']))
|
||||
# WRITE SEGY TRACE HEADER
|
||||
for key in TRACE_HEADER_DEF.keys():
|
||||
pos = index + TRACE_HEADER_DEF[key]['pos']
|
||||
format = TRACE_HEADER_DEF[key]['type']
|
||||
value = trace_header[key][itrace]
|
||||
logger.debug(str(pos) + " " +
|
||||
str(format) +
|
||||
" Writing " + key +
|
||||
"=" + str(value))
|
||||
put_value(value, f, pos, format, endian)
|
||||
|
||||
# Write Data
|
||||
cformat = endian + ctype
|
||||
for s in range(header['ns']):
|
||||
strVal = struct.pack(cformat, data[s, itrace])
|
||||
f.seek(index +
|
||||
TRACE_HEADER_NUM_BYTES +
|
||||
s * struct.calcsize(cformat))
|
||||
f.write(strVal)
|
||||
|
||||
f.close()
|
||||
|
||||
|
||||
def put_value(value, fileid, index, ctype='l', endian='>', number=1):
|
||||
"""
|
||||
putValue(data, index, ctype, endian, number)
|
||||
"""
|
||||
ctype = CTYPES[ctype]
|
||||
|
||||
cformat = endian + ctype*number
|
||||
|
||||
logger.debug('putValue : cformat : ' + cformat + ' ctype = ' + ctype)
|
||||
|
||||
str_val = struct.pack(cformat, value)
|
||||
fileid.seek(index)
|
||||
fileid.write(str_val)
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
def create_binary_reader(f, ctype='l', endian='>'):
|
||||
"""Create a unary callable which reads a given binary data type from a file.
|
||||
"""
|
||||
ctype = CTYPES[ctype]
|
||||
size = size_in_bytes(ctype)
|
||||
|
||||
cformat = endian + ctype
|
||||
|
||||
def reader(index):
|
||||
f.seek(index, os.SEEK_SET)
|
||||
data = f.read(size)
|
||||
# TODO: Check the content of data before proceeding
|
||||
value = struct.unpack(cformat, data)
|
||||
return value[0]
|
||||
|
||||
return reader
|
||||
|
||||
|
||||
def read_binary_value(f, index, ctype='l', endian='>', number=1):
|
||||
"""
|
||||
read_binary_value(data, index, ctype, endian, number)
|
||||
"""
|
||||
|
||||
ctype = CTYPES[ctype]
|
||||
|
||||
size = size_in_bytes(ctype)
|
||||
|
||||
cformat = endian + ctype * number
|
||||
|
||||
logger.debug('read_binary_value : cformat : ' + cformat)
|
||||
|
||||
index_end = index + size * number
|
||||
|
||||
f.seek(index, os.SEEK_SET)
|
||||
data = f.read(size * number)
|
||||
if ctype == 'ibm':
|
||||
# ASSUME IBM FLOAT DATA
|
||||
value = range(number)
|
||||
for i in arange(number):
|
||||
index_ibm = i * 4
|
||||
value[i] = ibm2ieee2(data[index_ibm: index_ibm + 4])
|
||||
# this returns an array as opposed to a tuple
|
||||
else:
|
||||
# TODO: Check the content of data before proceeding
|
||||
value = struct.unpack(cformat, data)
|
||||
|
||||
if ctype == 'B':
|
||||
logger.warning('read_binary_value : '
|
||||
'Inefficient use of 1 byte Integer...', 1)
|
||||
|
||||
logger.debug('read_binary_value : ' +
|
||||
'start = ' + str(index) +
|
||||
' size = ' + str(size) +
|
||||
' number = ' + str(number) +
|
||||
' value = ' + str(value) +
|
||||
' cformat = ' + str(cformat))
|
||||
|
||||
if number == 1:
|
||||
return value[0], index_end
|
||||
else:
|
||||
return value, index_end
|
||||
|
||||
|
||||
def get_byte_per_sample(header):
|
||||
revision = canonicalize_revision(header["SegyFormatRevisionNumber"])
|
||||
dsf = header["DataSampleFormat"]
|
||||
|
||||
try: # block added by A Squelch
|
||||
bps = HEADER_DEF["DataSampleFormat"]["bps"][revision][dsf]
|
||||
except KeyError:
|
||||
# TODO: This should not be a critical failure - should just convert
|
||||
# exception
|
||||
logging.critical(" An error has occurred interpreting a SEGY "
|
||||
"binary header key")
|
||||
logging.critical("Please check the Endian setting for "
|
||||
"this file: {0}".format(header["filename"]))
|
||||
sys.exit()
|
||||
|
||||
logger.debug("getBytePerSample : bps = " + str(bps))
|
||||
|
||||
return bps
|
||||
|
||||
|
||||
def main():
|
||||
filename = r'C:\Users\rjs\opendtectroot\Blake_Ridge_Hydrates_3D' \
|
||||
r'\stack_final_scaled50_int8.sgy'
|
||||
with open(filename, 'rb') as segy:
|
||||
data, header, trace_header = read_segy(segy)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,5 @@
|
||||
[bdist_wheel]
|
||||
# This flag says that the code is written to work on both Python 2 and Python
|
||||
# 3. If at all possible, it is good practice to do this. If you cannot, you
|
||||
# will need to generate wheels for each Python version that you support.
|
||||
universal=1
|
||||
@@ -0,0 +1,119 @@
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
from setuptools import setup, find_packages # Always prefer setuptools over distutils
|
||||
from codecs import open # To use a consistent encoding
|
||||
from os import path
|
||||
|
||||
|
||||
def read(*names, **kwargs):
|
||||
with io.open(
|
||||
os.path.join(os.path.dirname(__file__), *names),
|
||||
encoding=kwargs.get("encoding", "utf8")
|
||||
) as fp:
|
||||
return fp.read()
|
||||
|
||||
|
||||
def find_version(*file_paths):
|
||||
version_file = read(*file_paths)
|
||||
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
|
||||
version_file, re.M)
|
||||
if version_match:
|
||||
return version_match.group(1)
|
||||
raise RuntimeError("Unable to find version string.")
|
||||
|
||||
|
||||
here = path.abspath(path.dirname(__file__))
|
||||
|
||||
# Get the long description from the relevant file
|
||||
with open(path.join(here, 'DESCRIPTION.rst'), encoding='utf-8') as f:
|
||||
long_description = f.read()
|
||||
|
||||
setup(
|
||||
name='segpy',
|
||||
|
||||
# Versions should comply with PEP440. For a discussion on single-sourcing
|
||||
# the version across setup.py and the project code, see
|
||||
# https://packaging.python.org/en/latest/single_source_version.html
|
||||
version=find_version("segpy/__init__.py"),
|
||||
|
||||
description='Transfer of seismic data to and from SEG Y files',
|
||||
long_description=long_description,
|
||||
|
||||
# The project's main homepage.
|
||||
url='https://github.com/rob-smallshire/segpy',
|
||||
|
||||
# Author details
|
||||
author='Robert Smallshire',
|
||||
author_email='robert@smallshire.org.uk',
|
||||
|
||||
# Choose your license
|
||||
license='GPL',
|
||||
|
||||
# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||
classifiers=[
|
||||
# How mature is this project? Common values are
|
||||
# 3 - Alpha
|
||||
# 4 - Beta
|
||||
# 5 - Production/Stable
|
||||
'Development Status :: 3 - Alpha',
|
||||
|
||||
# Indicate who your project is intended for
|
||||
'Intended Audience :: Developers',
|
||||
'Topic :: Scientific/Engineering',
|
||||
'Topic :: Software Development :: Libraries',
|
||||
|
||||
# Pick your license as you wish (should match "license" above)
|
||||
'License :: OSI Approved :: MIT License',
|
||||
|
||||
# Specify the Python versions you support here. In particular, ensure
|
||||
# that you indicate whether you support Python 2, Python 3 or both.
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
],
|
||||
|
||||
# What does your project relate to?
|
||||
keywords='seismic geocomputing geophysics',
|
||||
|
||||
# You can just specify the packages manually here if your project is
|
||||
# simple. Or you can use find_packages().
|
||||
packages=find_packages(exclude=['contrib', 'docs', 'test*']),
|
||||
|
||||
# List run-time dependencies here. These will be installed by pip when your
|
||||
# project is installed. For an analysis of "install_requires" vs pip's
|
||||
# requirements files see:
|
||||
# https://packaging.python.org/en/latest/requirements.html
|
||||
install_requires=[],
|
||||
|
||||
# List additional groups of dependencies here (e.g. development dependencies).
|
||||
# You can install these using the following syntax, for example:
|
||||
# $ pip install -e .[dev,test]
|
||||
extras_require = {
|
||||
'dev': ['check-manifest', 'wheel'],
|
||||
'doc': ['sphinx', 'cartouche'],
|
||||
'test': ['coverage', 'hypothesis'],
|
||||
},
|
||||
|
||||
# If there are data files included in your packages that need to be
|
||||
# installed, specify them here. If using Python 2.6 or less, then these
|
||||
# have to be included in MANIFEST.in as well.
|
||||
package_data={
|
||||
},
|
||||
|
||||
# Although 'package_data' is the preferred approach, in some case you may
|
||||
# need to place data files outside of your packages.
|
||||
# see http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files
|
||||
# In this case, 'data_file' will be installed into '<sys.prefix>/my_data'
|
||||
data_files=[],
|
||||
|
||||
# To provide executable scripts, use entry points in preference to the
|
||||
# "scripts" keyword. Entry points provide cross-platform support and allow
|
||||
# pip to create the appropriate form of executable for the target platform.
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
from hypothesis import strategy
|
||||
from hypothesis.specifiers import integers_in_range
|
||||
|
||||
PRINTABLE_ASCII_RANGE = (32, 127)
|
||||
|
||||
|
||||
def multiline_ascii_encodable_text(min_num_lines, max_num_lines):
|
||||
"""A Hypothesis strategy to produce a multiline Unicode string.
|
||||
|
||||
Args:
|
||||
min_num_lines: The minimum number of lines in the produced strings.
|
||||
max_num_lines: The maximum number of lines in the produced strings.
|
||||
|
||||
Returns:
|
||||
A strategy for generating Unicode strings containing only newlines
|
||||
and characters which are encodable as printable 7-bit ASCII characters.
|
||||
"""
|
||||
|
||||
return strategy(integers_in_range(0, 10)) \
|
||||
.flatmap(lambda n: ([integers_in_range(*PRINTABLE_ASCII_RANGE)],) * n) \
|
||||
.map(lambda xs: '\n'.join(bytes(x).decode('ascii') for x in xs))
|
||||
@@ -0,0 +1 @@
|
||||
hypothesis>=1.2
|
||||
@@ -0,0 +1,41 @@
|
||||
import unittest
|
||||
from hypothesis import given
|
||||
from hypothesis.specifiers import sampled_from, just
|
||||
from segpy.encoding import EBCDIC, ASCII
|
||||
from segpy.toolkit import format_extended_textual_header, CARDS_PER_HEADER, END_TEXT_STANZA, CARD_LENGTH
|
||||
from test.strategies import multiline_ascii_encodable_text
|
||||
|
||||
|
||||
class TestFormatExtendedTextualHeader(unittest.TestCase):
|
||||
|
||||
@given(multiline_ascii_encodable_text(0, 100),
|
||||
sampled_from([ASCII, EBCDIC]),
|
||||
bool)
|
||||
def test_forty_lines_per_page(self, text, encoding, include_text_stop):
|
||||
pages = format_extended_textual_header(text, encoding, include_text_stop)
|
||||
self.assertTrue(all(len(page) == CARDS_PER_HEADER for page in pages))
|
||||
|
||||
@given(multiline_ascii_encodable_text(0, 100),
|
||||
sampled_from([ASCII, EBCDIC]),
|
||||
bool)
|
||||
def test_eighty_bytes_per_encoded_line(self, text, encoding, include_text_stop):
|
||||
pages = format_extended_textual_header(text, encoding, include_text_stop)
|
||||
self.assertTrue(all([len(line.encode(encoding)) == CARD_LENGTH for page in pages for line in page]))
|
||||
|
||||
@given(multiline_ascii_encodable_text(0, 100),
|
||||
sampled_from([ASCII, EBCDIC]),
|
||||
bool)
|
||||
def test_lines_end_with_cr_lf(self, text, encoding, include_text_stop):
|
||||
pages = format_extended_textual_header(text, encoding, include_text_stop)
|
||||
self.assertTrue(all([line.endswith('\r\n') for page in pages for line in page]))
|
||||
|
||||
@given(multiline_ascii_encodable_text(0, 100),
|
||||
sampled_from([ASCII, EBCDIC]),
|
||||
just(True))
|
||||
def test_end_text_stanza_present(self, text, encoding, include_text_stop):
|
||||
pages = format_extended_textual_header(text, encoding, include_text_stop)
|
||||
self.assertTrue(pages[-1][0].startswith(END_TEXT_STANZA))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,364 @@
|
||||
from math import trunc, floor
|
||||
import unittest
|
||||
|
||||
from hypothesis import given, assume
|
||||
import math
|
||||
from hypothesis.specifiers import integers_in_range, floats_in_range
|
||||
|
||||
from segpy.portability import byte_string
|
||||
from segpy.ibm_float import (ieee2ibm, ibm2ieee, MAX_IBM_FLOAT, SMALLEST_POSITIVE_NORMAL_IBM_FLOAT,
|
||||
LARGEST_NEGATIVE_NORMAL_IBM_FLOAT, MIN_IBM_FLOAT, IBMFloat, EPSILON_IBM_FLOAT,
|
||||
MAX_EXACT_INTEGER_IBM_FLOAT, MIN_EXACT_INTEGER_IBM_FLOAT, EXPONENT_BIAS)
|
||||
from segpy.util import almost_equal
|
||||
|
||||
|
||||
class Ibm2Ieee(unittest.TestCase):
|
||||
|
||||
def test_zero(self):
|
||||
self.assertEqual(ibm2ieee(b'\0\0\0\0'), 0.0)
|
||||
|
||||
def test_positive_half(self):
|
||||
self.assertEqual(ibm2ieee(byte_string((0b11000000, 0x80, 0x00, 0x00))), -0.5)
|
||||
|
||||
def test_negative_half(self):
|
||||
self.assertEqual(ibm2ieee(byte_string((0b01000000, 0x80, 0x00, 0x00))), 0.5)
|
||||
|
||||
def test_one(self):
|
||||
self.assertEqual(ibm2ieee(b'\x41\x10\x00\x00'), 1.0)
|
||||
|
||||
def test_negative_118_625(self):
|
||||
# Example taken from Wikipedia http://en.wikipedia.org/wiki/IBM_Floating_Point_Architecture
|
||||
self.assertEqual(ibm2ieee(byte_string((0b11000010, 0b01110110, 0b10100000, 0b00000000))), -118.625)
|
||||
|
||||
def test_largest_representable_number(self):
|
||||
self.assertEqual(ibm2ieee(byte_string((0b01111111, 0b11111111, 0b11111111, 0b11111111))), MAX_IBM_FLOAT)
|
||||
|
||||
def test_smallest_positive_normalised_number(self):
|
||||
self.assertEqual(ibm2ieee(byte_string((0b00000000, 0b00010000, 0b00000000, 0b00000000))), SMALLEST_POSITIVE_NORMAL_IBM_FLOAT)
|
||||
|
||||
def test_largest_negative_normalised_number(self):
|
||||
self.assertEqual(ibm2ieee(byte_string((0b10000000, 0b00010000, 0b00000000, 0b00000000))), LARGEST_NEGATIVE_NORMAL_IBM_FLOAT)
|
||||
|
||||
def test_smallest_representable_number(self):
|
||||
self.assertEqual(ibm2ieee(byte_string((0b11111111, 0b11111111, 0b11111111, 0b11111111))), MIN_IBM_FLOAT)
|
||||
|
||||
def test_error_1(self):
|
||||
self.assertEqual(ibm2ieee(byte_string((196, 74, 194, 143))), -19138.55859375)
|
||||
|
||||
def test_error_2(self):
|
||||
self.assertEqual(ibm2ieee(byte_string((191, 128, 0, 0))), -0.03125)
|
||||
|
||||
def test_subnormal(self):
|
||||
self.assertEqual(ibm2ieee(byte_string((0x00, 0x00, 0x00, 0x20))), 1.6472184286297693e-83)
|
||||
|
||||
def test_subnormal_is_subnormal(self):
|
||||
self.assertTrue(0 < ibm2ieee(byte_string((0x00, 0x00, 0x00, 0x20))) < SMALLEST_POSITIVE_NORMAL_IBM_FLOAT)
|
||||
|
||||
def test_subnormal_smallest_subnormal(self):
|
||||
self.assertEqual(ibm2ieee(byte_string((0x00, 0x00, 0x00, 0x01))), 5.147557589468029e-85)
|
||||
|
||||
|
||||
class Ieee2Ibm(unittest.TestCase):
|
||||
|
||||
def test_zero(self):
|
||||
self.assertEqual(ieee2ibm(0.0), b'\0\0\0\0')
|
||||
|
||||
def test_positive_half(self):
|
||||
self.assertEqual(ieee2ibm(-0.5), byte_string((0b11000000, 0x80, 0x00, 0x00)))
|
||||
|
||||
def test_negative_half(self):
|
||||
self.assertEqual(ieee2ibm(0.5), byte_string((0b01000000, 0x80, 0x00, 0x00)))
|
||||
|
||||
def test_one(self):
|
||||
self.assertEqual(ieee2ibm(1.0), b'\x41\x10\x00\x00')
|
||||
|
||||
def test_negative_118_625(self):
|
||||
# Example taken from Wikipedia http://en.wikipedia.org/wiki/IBM_Floating_Point_Architecture
|
||||
self.assertEqual(ieee2ibm(-118.625), byte_string((0b11000010, 0b01110110, 0b10100000, 0b00000000)))
|
||||
|
||||
def test_0_1(self):
|
||||
# Note, this is different from the Wikipedia example, because the Wikipedia example does
|
||||
# round to nearest, and our routine does round to zero
|
||||
self.assertEqual(ieee2ibm(0.1), byte_string((0b01000000, 0b00011001, 0b10011001, 0b10011001)))
|
||||
|
||||
def test_subnormal(self):
|
||||
self.assertEqual(ieee2ibm(1.6472184286297693e-83), byte_string((0x00, 0x00, 0x00, 0x20)))
|
||||
|
||||
def test_smallest_subnormal(self):
|
||||
self.assertEqual(ieee2ibm(5.147557589468029e-85), byte_string((0x00, 0x00, 0x00, 0x01)))
|
||||
|
||||
def test_too_small_subnormal(self):
|
||||
with self.assertRaises(FloatingPointError):
|
||||
ieee2ibm(1e-86)
|
||||
|
||||
def test_nan(self):
|
||||
with self.assertRaises(ValueError):
|
||||
ieee2ibm(float('nan'))
|
||||
|
||||
def test_inf(self):
|
||||
with self.assertRaises(ValueError):
|
||||
ieee2ibm(float('inf'))
|
||||
|
||||
def test_too_large(self):
|
||||
with self.assertRaises(OverflowError):
|
||||
ieee2ibm(MAX_IBM_FLOAT * 10)
|
||||
|
||||
def test_too_small(self):
|
||||
with self.assertRaises(OverflowError):
|
||||
ieee2ibm(MIN_IBM_FLOAT * 10)
|
||||
|
||||
|
||||
class Ibm2IeeeRoundtrip(unittest.TestCase):
|
||||
|
||||
def test_zero(self):
|
||||
ibm_start = b'\0\0\0\0'
|
||||
f = ibm2ieee(ibm_start)
|
||||
ibm_result = ieee2ibm(f)
|
||||
self.assertEqual(ibm_start, ibm_result)
|
||||
|
||||
def test_positive_half(self):
|
||||
ibm_start = byte_string((0b11000000, 0x80, 0x00, 0x00))
|
||||
f = ibm2ieee(ibm_start)
|
||||
ibm_result = ieee2ibm(f)
|
||||
self.assertEqual(ibm_start, ibm_result)
|
||||
|
||||
def test_negative_half(self):
|
||||
ibm_start = byte_string((0b01000000, 0x80, 0x00, 0x00))
|
||||
f = ibm2ieee(ibm_start)
|
||||
ibm_result = ieee2ibm(f)
|
||||
self.assertEqual(ibm_start, ibm_result)
|
||||
|
||||
def test_one(self):
|
||||
ibm_start = b'\x41\x10\x00\x00'
|
||||
f = ibm2ieee(ibm_start)
|
||||
ibm_result = ieee2ibm(f)
|
||||
self.assertEqual(ibm_start, ibm_result)
|
||||
|
||||
def test_subnormal(self):
|
||||
ibm_start = byte_string((0x00, 0x00, 0x00, 0x20))
|
||||
f = ibm2ieee(ibm_start)
|
||||
ibm_result = ieee2ibm(f)
|
||||
self.assertEqual(ibm_start, ibm_result)
|
||||
|
||||
|
||||
class TestIBMFloat(unittest.TestCase):
|
||||
|
||||
def test_zero_from_float(self):
|
||||
zero = IBMFloat.from_float(0.0)
|
||||
self.assertTrue(zero.is_zero())
|
||||
|
||||
def test_zero_from_bytes(self):
|
||||
zero = IBMFloat.from_bytes(b'\x00\x00\x00\x00')
|
||||
self.assertTrue(zero.is_zero())
|
||||
|
||||
def test_subnormal(self):
|
||||
ibm = IBMFloat.from_float(1.6472184286297693e-83)
|
||||
self.assertTrue(ibm.is_subnormal())
|
||||
|
||||
def test_smallest_subnormal(self):
|
||||
ibm = IBMFloat.from_float(5.147557589468029e-85)
|
||||
self.assertEqual(bytes(ibm), byte_string((0x00, 0x00, 0x00, 0x01)))
|
||||
|
||||
def test_too_small_subnormal(self):
|
||||
with self.assertRaises(FloatingPointError):
|
||||
IBMFloat.from_float(1e-86)
|
||||
|
||||
def test_nan(self):
|
||||
with self.assertRaises(ValueError):
|
||||
IBMFloat.from_float(float('nan'))
|
||||
|
||||
def test_inf(self):
|
||||
with self.assertRaises(ValueError):
|
||||
IBMFloat.from_float(float('inf'))
|
||||
|
||||
def test_too_large(self):
|
||||
with self.assertRaises(OverflowError):
|
||||
IBMFloat.from_float(MAX_IBM_FLOAT * 10)
|
||||
|
||||
def test_too_small(self):
|
||||
with self.assertRaises(OverflowError):
|
||||
IBMFloat.from_float(MIN_IBM_FLOAT * 10)
|
||||
|
||||
@given(floats_in_range(MIN_IBM_FLOAT, MAX_IBM_FLOAT))
|
||||
def test_bool(self, f):
|
||||
self.assertEqual(bool(IBMFloat.from_float(f)), bool(f))
|
||||
|
||||
@given(integers_in_range(0, 255),
|
||||
integers_in_range(0, 255),
|
||||
integers_in_range(0, 255),
|
||||
integers_in_range(0, 255))
|
||||
def test_bytes_roundtrip(self, a, b, c, d):
|
||||
b = byte_string((a, b, c, d))
|
||||
ibm = IBMFloat.from_bytes(b)
|
||||
self.assertEqual(bytes(ibm), b)
|
||||
|
||||
@given(floats_in_range(MIN_IBM_FLOAT, MAX_IBM_FLOAT))
|
||||
def test_floats_roundtrip(self, f):
|
||||
ibm = IBMFloat.from_float(f)
|
||||
self.assertTrue(almost_equal(f, float(ibm), epsilon=EPSILON_IBM_FLOAT))
|
||||
|
||||
@given(integers_in_range(0, MAX_EXACT_INTEGER_IBM_FLOAT - 1),
|
||||
floats_in_range(0.0, 1.0))
|
||||
def test_trunc_above_zero(self, i, f):
|
||||
assume(f != 1.0)
|
||||
ieee = i + f
|
||||
ibm = IBMFloat.from_float(ieee)
|
||||
self.assertEqual(trunc(ibm), i)
|
||||
|
||||
@given(integers_in_range(MIN_EXACT_INTEGER_IBM_FLOAT + 1, 0),
|
||||
floats_in_range(0.0, 1.0))
|
||||
def test_trunc_below_zero(self, i, f):
|
||||
assume(f != 1.0)
|
||||
ieee = i - f
|
||||
ibm = IBMFloat.from_float(ieee)
|
||||
self.assertEqual(trunc(ibm), i)
|
||||
|
||||
@given(integers_in_range(MIN_EXACT_INTEGER_IBM_FLOAT, MAX_EXACT_INTEGER_IBM_FLOAT - 1),
|
||||
floats_in_range(0.0, 1.0))
|
||||
def test_ceil(self, i, f):
|
||||
assume(f != 1.0)
|
||||
ieee = i + f
|
||||
ibm = IBMFloat.from_float(ieee)
|
||||
self.assertEqual(math.ceil(ibm), i + 1)
|
||||
|
||||
@given(integers_in_range(MIN_EXACT_INTEGER_IBM_FLOAT, MAX_EXACT_INTEGER_IBM_FLOAT - 1),
|
||||
floats_in_range(0.0, 1.0))
|
||||
def test_floor(self, i, f):
|
||||
assume(f != 1.0)
|
||||
ieee = i + f
|
||||
ibm = IBMFloat.from_float(ieee)
|
||||
self.assertEqual(math.floor(ibm), i)
|
||||
|
||||
def test_normalise_subnormal_expect_failure(self):
|
||||
# This float has an base-16 exponent of -64 (the minimum) and cannot be normalised
|
||||
ibm = IBMFloat.from_float(1.6472184286297693e-83)
|
||||
assert ibm.is_subnormal()
|
||||
with self.assertRaises(FloatingPointError):
|
||||
ibm.normalize()
|
||||
|
||||
def test_normalise_subnormal1(self):
|
||||
ibm = IBMFloat.from_bytes((0b01000000, 0b00000000, 0b11111111, 0b00000000))
|
||||
assert ibm.is_subnormal()
|
||||
normalized = ibm.normalize()
|
||||
self.assertFalse(normalized.is_subnormal())
|
||||
|
||||
def test_normalise_subnormal2(self):
|
||||
ibm = IBMFloat.from_bytes((64, 1, 0, 0))
|
||||
assert ibm.is_subnormal()
|
||||
normalized = ibm.normalize()
|
||||
self.assertFalse(normalized.is_subnormal())
|
||||
|
||||
@given(integers_in_range(128, 255),
|
||||
integers_in_range(0, 255),
|
||||
integers_in_range(0, 255),
|
||||
integers_in_range(4, 23))
|
||||
def test_normalise_subnormal(self, b, c, d, shift):
|
||||
mantissa = (b << 16) | (c << 8) | d
|
||||
assume(mantissa != 0)
|
||||
mantissa >>= shift
|
||||
assert mantissa != 0
|
||||
|
||||
sa = EXPONENT_BIAS
|
||||
sb = (mantissa >> 16) & 0xff
|
||||
sc = (mantissa >> 8) & 0xff
|
||||
sd = mantissa & 0xff
|
||||
|
||||
ibm = IBMFloat.from_bytes((sa, sb, sc, sd))
|
||||
assert ibm.is_subnormal()
|
||||
normalized = ibm.normalize()
|
||||
self.assertFalse(normalized.is_subnormal())
|
||||
|
||||
@given(integers_in_range(128, 255),
|
||||
integers_in_range(0, 255),
|
||||
integers_in_range(0, 255),
|
||||
integers_in_range(4, 23))
|
||||
def test_zero_subnormal(self, b, c, d, shift):
|
||||
mantissa = (b << 16) | (c << 8) | d
|
||||
assume(mantissa != 0)
|
||||
mantissa >>= shift
|
||||
assert mantissa != 0
|
||||
|
||||
sa = EXPONENT_BIAS
|
||||
sb = (mantissa >> 16) & 0xff
|
||||
sc = (mantissa >> 8) & 0xff
|
||||
sd = mantissa & 0xff
|
||||
|
||||
ibm = IBMFloat.from_bytes((sa, sb, sc, sd))
|
||||
assert ibm.is_subnormal()
|
||||
z = ibm.zero_subnormal()
|
||||
self.assertTrue(z.is_zero())
|
||||
|
||||
@given(integers_in_range(0, 255),
|
||||
integers_in_range(0, 255),
|
||||
integers_in_range(0, 255),
|
||||
integers_in_range(0, 255))
|
||||
def test_abs(self, a, b, c, d):
|
||||
ibm = IBMFloat.from_bytes((a, b, c, d))
|
||||
abs_ibm = abs(ibm)
|
||||
self.assertGreaterEqual(abs_ibm.signbit, 0)
|
||||
|
||||
@given(integers_in_range(0, 255),
|
||||
integers_in_range(0, 255),
|
||||
integers_in_range(0, 255),
|
||||
integers_in_range(0, 255))
|
||||
def test_negate_non_zero(self, a, b, c, d):
|
||||
ibm = IBMFloat.from_bytes((a, b, c, d))
|
||||
assume(not ibm.is_zero())
|
||||
negated = -ibm
|
||||
self.assertNotEqual(ibm.signbit, negated.signbit)
|
||||
|
||||
def test_negate_zero(self):
|
||||
zero = IBMFloat.from_float(0.0)
|
||||
negated = -zero
|
||||
self.assertTrue(negated.is_zero())
|
||||
|
||||
@given(floats_in_range(MIN_IBM_FLOAT, MAX_IBM_FLOAT))
|
||||
def test_signbit(self, f):
|
||||
ltz = f < 0
|
||||
ibm = IBMFloat.from_float(f)
|
||||
self.assertEqual(ltz, ibm.signbit)
|
||||
|
||||
@given(floats_in_range(-1.0, +1.0),
|
||||
integers_in_range(-256, 255))
|
||||
def test_ldexp_frexp(self, fraction, exponent):
|
||||
try:
|
||||
ibm = IBMFloat.ldexp(fraction, exponent)
|
||||
except OverflowError:
|
||||
assume(False)
|
||||
else:
|
||||
f, e = ibm.frexp()
|
||||
self.assertTrue(almost_equal(fraction * 2**exponent, f * 2**e, epsilon=EPSILON_IBM_FLOAT))
|
||||
|
||||
@given(floats_in_range(MIN_IBM_FLOAT, MAX_IBM_FLOAT),
|
||||
floats_in_range(0.0, 1.0))
|
||||
def test_add(self, f, p):
|
||||
a = f * p
|
||||
b = f - a
|
||||
|
||||
ibm_a = IBMFloat.from_float(a)
|
||||
ibm_b = IBMFloat.from_float(b)
|
||||
ibm_c = ibm_a + ibm_b
|
||||
|
||||
ieee_a = float(ibm_a)
|
||||
ieee_b = float(ibm_b)
|
||||
ieee_c = ieee_a + ieee_b
|
||||
|
||||
self.assertTrue(almost_equal(ieee_c, ibm_c, epsilon=EPSILON_IBM_FLOAT * 4))
|
||||
|
||||
@given(floats_in_range(0, MAX_IBM_FLOAT),
|
||||
floats_in_range(0, MAX_IBM_FLOAT))
|
||||
def test_sub(self, a, b):
|
||||
ibm_a = IBMFloat.from_float(a)
|
||||
ibm_b = IBMFloat.from_float(b)
|
||||
ibm_c = ibm_a - ibm_b
|
||||
|
||||
ieee_a = float(ibm_a)
|
||||
ieee_b = float(ibm_b)
|
||||
ieee_c = ieee_a - ieee_b
|
||||
|
||||
self.assertTrue(almost_equal(ieee_c, ibm_c, epsilon=EPSILON_IBM_FLOAT))
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,50 @@
|
||||
import unittest
|
||||
|
||||
from hypothesis import given, assume
|
||||
from hypothesis.descriptors import integers_in_range
|
||||
from segpy.util import batched
|
||||
|
||||
|
||||
class TestBatched(unittest.TestCase):
|
||||
|
||||
@given([int],
|
||||
integers_in_range(1, 1000))
|
||||
def test_batch_sizes_unpadded(self, items, batch_size):
|
||||
assume(batch_size > 0)
|
||||
batches = list(batched(items, batch_size))
|
||||
self.assertTrue(all(len(batch) == batch_size for batch in batches[:-1]))
|
||||
|
||||
@given([int],
|
||||
integers_in_range(1, 1000))
|
||||
def test_final_batch_sizes(self, items, batch_size):
|
||||
assume(len(items) > 0)
|
||||
assume(batch_size > 0)
|
||||
batches = list(batched(items, batch_size))
|
||||
self.assertTrue(len(batches[-1]) <= batch_size)
|
||||
|
||||
@given([int],
|
||||
integers_in_range(1, 1000),
|
||||
int)
|
||||
def test_batch_sizes_padded(self, items, batch_size, pad):
|
||||
assume(batch_size > 0)
|
||||
batches = list(batched(items, batch_size, padding=pad))
|
||||
self.assertTrue(all(len(batch) == batch_size for batch in batches))
|
||||
|
||||
@given([int],
|
||||
integers_in_range(1, 1000),
|
||||
int)
|
||||
def test_pad_contents(self, items, batch_size, pad):
|
||||
assume(len(items) > 0)
|
||||
assume(0 < batch_size < 1000)
|
||||
num_left_over = len(items) % batch_size
|
||||
pad_length = batch_size - num_left_over if num_left_over != 0 else 0
|
||||
assume(pad_length != 0)
|
||||
batches = list(batched(items, batch_size, padding=pad))
|
||||
self.assertEqual(batches[-1][batch_size - pad_length:], [pad] * pad_length)
|
||||
|
||||
def test_pad(self):
|
||||
batches = list(batched([0, 0], 3, 42))
|
||||
self.assertEqual(batches[-1], [0, 0, 42])
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# testsegy.py
|
||||
#
|
||||
|
||||
import segypy
|
||||
|
||||
|
||||
def test_read(filename):
|
||||
# Read Segy File
|
||||
with open(filename, 'rb') as f:
|
||||
return segypy.read_segy(f, filename)
|
||||
|
||||
|
||||
def test_write(filename_out, data, sh, sth):
|
||||
sh['DataSampleFormat'] = 5
|
||||
sh['SegyFormatRevisionNumber'] = 100
|
||||
segypy.write_segy_structure(filename_out, data, sh, sth)
|
||||
|
||||
segypy.wiggle(data, sh, 2, .1, .1)
|
||||
|
||||
f_ieee = 'data_IEEE.segy'
|
||||
f_ibm = 'data_IBM_REV1.segy'
|
||||
d_ieee, sh, sth = segypy.read_segy(f_ieee)
|
||||
d_ibm, sh, sth = segypy.read_segy(f_ibm)
|
||||
|
||||
return d_ieee, d_ibm, data
|
||||
|
||||
|
||||
def test_render(d_ieee, d_ibm, data):
|
||||
import pylab
|
||||
|
||||
# imshow(Data)
|
||||
pylab.figure(1)
|
||||
pylab.imshow(d_ieee)
|
||||
pylab.title('ieee')
|
||||
pylab.show()
|
||||
|
||||
pylab.figure(2)
|
||||
pylab.imshow(d_ibm)
|
||||
pylab.title('IBM')
|
||||
pylab.show()
|
||||
|
||||
pylab.figure(3)
|
||||
pylab.imshow(data)
|
||||
pylab.title('TEST')
|
||||
pylab.show()
|
||||
|
||||
|
||||
def parse_args():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument('filename',
|
||||
help='The SEGY input file')
|
||||
|
||||
parser.add_argument('--output', '-o',
|
||||
dest='outfile',
|
||||
default='',
|
||||
help='The output file (optional).')
|
||||
|
||||
parser.add_argument('--render_test', '-r',
|
||||
dest='render_test',
|
||||
action='store_true',
|
||||
help='Whether to test rendering (only applies if '
|
||||
'output is produced.)')
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parse_args()
|
||||
|
||||
read_results = test_read(args.filename)
|
||||
|
||||
if args.outfile:
|
||||
write_results = test_write(args.outfile, *read_results)
|
||||
|
||||
if args.render_test:
|
||||
test_render(*write_results)
|
||||
Reference in New Issue
Block a user