Bob Courtney
Geological Survey of
Natural Resources
Bob.courtney@nrcan.gc.ca
March 30, 2009
SegyJp2
Viewer Version 1.0 (DRAFT VERSION)
Intellectual
and Property Rights
Routines to Manipulate JPEG2000 Box Data
Appendix
2 - Default Viewer for jp2 Files; Hyperlinks in ArcMap
SegyJp2Viewer is a Windows XP program that is used to view and interpret single-channel SGYJP2 files (SEGY files that have been converted into JPEG2000 format). We have added a functionality to export interpreted features as shape files so that they may be added to digital map sheets compiled in ESRI ArcMap.
The attempt here is to implement a database- and internet-ready framework that will allow researchers and others to easily exchange and archive both sonar data and their interpretations.
Our experience has shown that high-resolution seismic data is cumbersome and voluminous and many standard seismic processing packages do a poor job at handling and exchanging this type of information. In addition, most of our existing interpretations of seismic data reside on Mylar film, which become detached to the original source data; these interpretations are often lost. Even when these hand-crafted interpretations have been located, it is an arduous job to convert these interpretations to a digital form.
JPEG2000 encoding of seismic and sidescan offers the capability to encode SEGY data efficiently ( typical compression of 10:1 though to 40:1), offer quick evaluation of data quality through image viewing technology and, importantly, this offers a framework for embedding value-added interpretations the data. These files may contain 100,000’s of traces efficiently without undue allocation of system resources
One of the notable advantages of this new approach is the multiresolution or multiscale nature of the JPEG 2000 storage framework. In representing data in a multi-resolution form, near-optimal images of these data can be displayed as one zooms in and out with very little effort and very little load on computer resources. This feature makes the onscreen representation of the seismic data very similar to what one would see viewing a printed seismic section, with the added advantage of course that these data and the interpretations will make them are geographically registered.
The program used image/trace data stored in the image section of the JPEG2000 format with file and trace header data embedded as compressed XML data in the JPEG2000 files. Any interpretations added through this package will be stored in the file as an speparate compressed XML bundle .
.
The SegyJp2Viewer distribution package may be freely distributed as it contains no proprietary code. It does utilize Kakadu (www.kakadusoftware.com) , GSL (http://www.network-theory.com/gsl/) and TChart ( www.steema.com) libraries which are distributed in compliance with each of their respective licensing agreements.
The product will undoubtedly evolve over time so it is prudent to
contact the author to obtain the most recent version. Since this product is
free, sometimes one must wait until time is available to address bugs, etc.
Note : This program does not handle strictly image-type JPEG2000
files that have been derived from scanned analog field records. I would
recommend Lizardtech’s free browser plug-in-in for
these files (www.lizardtech.com). SegyJp2Viewer does not handle files in SEGY
format.
This viewer was developed as part of the Geoscience for Ocean Management
Program as a means of viewing and disseminating high-resolution digital seismic
and sidescan data collected though marine operations of the Geological Survey
of Canada.
This
program is the intellectual property of the Government of Canada. All rights to
modify and distribute this software are retained by the Government of Canada.
"Intellectual
Property" means all rights in respect of Confidential Information, any
know‑how, inventions and improvements related to Confidential
Information, including without limitation, patents, copyrights, trade secrets,
trade‑marks, registered industrial designs, any applications for same and
all rights therein.
"Confidential
Information" means any information of a scientific or technical nature
disclosed to, generated or acquired by the Student in the course of carrying
out the Student activities defined in this Agreement, whether oral or recorded
in any form or medium and whether or not protectable by copyright.
We allow this program to be redistributed
freely in its original form. However, please send me an email if you have a
copy – I’ll ensure you get on a mailing list for upgrades.
Contact
the author, Bob Courtney, Natural
Resources Canada,
Unfortunately
we have limited capacity to support needs of outside users. We do however encourage
joint research agreements to extend this framework.
SegyJp2Viewer is easy to install.
(a) If you are within the Natural Resources Canada network, you can install it directly from a network drive. Open a window to \\s5-dar-data1\shared_software\SegyJp2_Viewer\setup.exe .
If you are outside GSCA but within the NRCan network , try using this link: \\192.55.224.44\shared_software\SegyJp2_Viewer\setup.exe
Proceed as is typical for any Windows application installation. A Canadian Flag icon will be added to your desktop and an entry under the folder NRCan will be added to your Program list.
(b) If you obtain a ZIP file containing the release, unpack the zip file to a directory on a local drive and use Explore to enter the SegyJP2 subdirectory that is unpacked into the specified spot. Double click on setup.exe and proceed as above.
To remove the package, choose Control Panel from the Windows Start Menu. Double click on Add or Remove Programs. Scroll down to SegyJp2_Viewer and click the Remove button. Follow the prompts.
Note : It is necessary to manually remove an existing package before a
newer version can be installed.
To start the program, double click on the desktop SegyJ2Viewer icon or the icon within the Program Files/NRCan menu entry.
If all goes well, the Launcher window will appear of the desktop ( Fig 1). The window will list SegyJp2 files that a researcher wishes to examine.
Figure 1 - Launcher Window of SegyJp2Viewer
Press the Add button and select the desired SegyJp2 files from local or network sources. You can multiply select files from different directories and different drives. The program should accept only SegyJP2 files, so don’t worry if you choose the wrong files – they shouldn’t appear in the list. The chosen items will appear in the uppermost listbox.
Hint: If you are going to do a lot of zooming and panning
of a given file, it is best to make a local copy of the file rather than
running the operation off a network file system or network attached storage box
(NAS). The network file will work but the process may prove slower.
When you single-click on any selected item, summary data are shown in the lower text box that is derived from the SEGY header info encoded in the SegyJp2 files (Fig 2)
Figure 2 - Single Click on a item to display info
To view the contents of a file, double click on any file listed in the upper box. A viewer window will appear on the screen (Fig. 3). Multiple windows may be brought up for a single file, each with its own view and settings, but only one of these windows may be active (zooming or panning) at a time due to file-locking restrictions imposed during these operations. Because JPEG2000 is “lightweight” , most computer systems do not become bogged down when multiple windows are open.
Figure 3a - Viewing Window
If any interpretations had be added to the section the window will look like this :
Figure 3b - Viewing Window with existing interpretations
The status of any point in the section will be displayed in the upper right hand info box when a left button mouse click is pressed when over the image. The date and trace number is show as well as the two way time (TWT ) in msec. A depth is displayed assuming a water sound speed of 1500 m/s. Positions corresponding to the source locations in the SEGY trace header are also shown.
Figure 4 – Status
Window
On first view, the image may appear distorted, however the viewing window may be resized to a more sensible aspect ratio by pulling on the main window’s corners. To help guide this resizing, Stretch Hor and Vert parameters are displayed in the status box in a box when resizing in the upper rand hand text box. These parameters reflect the image stretching that has been applied to image data that was retrieved from the SegyJp2 file.
When either parameter is greater than one, then the image has been compressed in that axis to fit the viewing window. If the parameter is less than one then the image has been stretched. Optimal image quality is achieved when these parameters are both close to one. High values ( >> 1 ) of the stretch parameter may result in aliasing and yield a sub-optimal, noisy image. Try resizing the window and watch how these parameters change.
Once the window is resized, the image will be regenerated. Typically the regeneration will take in the order of 0.5 sec. In Figure 5, the window has been resized and now displays a more typical seismic section.
Figure 5 – Viewing window
resized to achieve better stretch ratios.
It may be necessary to zoom in on one axis (see next section) , in order to achieve acceptable imagery.
By default, the left axis shows the two-way time in milliseconds, and the right axis gives the equivalent depth based on the 1500 m/s sound speed. The bottom axis is the sequential trace number in the input file, counting from zero for the first trace. The user can override the interval on the trace labels by entering the desired value in the numeric box.
If the segy traces are time-tagged,
then the bottom axes can display survey time. Use the bottom axis combo box on
the main page and choose “Time”. The interval between time fixes is given in
minutes when this option is chosen. This
option does not assume a constant trace firing rate, and these labels may be
spaced unevenly if the firing rate changes during the line.
If the segy traces are position-tagged (i.e., data in the source X and Y positions of the segy trace header), then the bottom axis can display cumulative distance along track. Again, distance labels are often unevenly spaced along the bottom axis, representing speed changes in the survey vessel.
The axes and the grid properties may be interactively altered or hidden by using the chart editor, which can be turned on by clicking the triangle in the tool bar. This editor can change many other features of the plot, and I will leave it to the user to explore these options. For more information, I would look at the manufacturer’s website (http://www.steema.com/products/teechart/net/overview.html) for this graphics plug-in.
Zoom In :
The user may zoom into an area of the image by “rubber-banding” a box, i.e., dragging the mouse pointer from the upper left-hand corner of the desired region to the lower right hand corner while holding the left mouse button down.
Note: Rubber-band mode is unavailable when horizons are being digitized.
Zoom buttons should be used instead.
Zoom Out :
To zoom out to the full extent, “rubber-band” a box from the bottom right-hand corner back to the left topmost corner, the reverse of the zoom-in procedure.
Pan :
The user may pan an image by holding the right mouse button down on a selected part of the image and dragging the image to another point. As with the zoom function, the image will refresh shortly.
Note: Panning using the mouse button does work in horizon mode.
Pan and Zoom Buttons :
The user can shift the viewport left and right and up and down using the directional buttons, Left (<), Right ( >), Up (^), and Down (v).
The user may zoom in (Z) and zoom out (z) or zoom in and out along the x or y axis separately (Zx, zx, Zy, zy).
The amount of zoom and pan can be adjusted in the setup panel.
The colour mapping of the seismic image may be manipulated by pressing the Pallete button. When this button is pressed, a grey scale legend is displayed in a floating window that will remain on top of the viewing window (Figure 6). The colour map of the displayed image can be changed using the sliders on the right hand side of the palette bar.
Note: At present only
grey scale mapping has be implemented.
Figure 6 - Palette Window
The Setup button activates a window that allows the user to set default values for the zoom and pan buttons and for the refresh rate for regenerating images as the main window is resized or the image is zoomed or panned.
In Figure 7, Pan is set to 50%. In this case, the image will be shifted by half when the Left, Right, Up or Down button is pushed ( when in zoom mode).
Zoom is set to 50%. In this case, the Z, Zx, or Zy button will zoom in to 50% of the current window settings. If z, zx, or zy is clicked then the image will be zoomed out by 100/50 of the current window settings.
Figure 7 - Setup window
This viewer allows the user to digitize three types of features:
Horizons are continuous line interpretations of seabed and sub-seabed interfaces. Horizons can be discontinuous with breaks; they can be complex and multi-valued.
To bring up the horizon panel, press the “Horizon” button on the main window.
To dismiss the panel, toggle the “Horizon” button.
To add a horizon:
If no name is entered, a default name will be generated (horizon 0).
Note: A list of names can be read into the drop-down
menu of the Horizon Name entry box by pressing the Open HList button . This list should have
be prepared using Notepad or another ASCII editor with one horizon name on each
line of the file. By default, only files with a “.hor”extension
will be displayed but files with other extensions can be entered by choosing
“All files” in the file type entrybox in the file selection box. One can save a
list of horizon names that had been manually entered during use by pressing the
Save HList
button.
Choose the current horizon by clicking on the desired horizon in the Horizons list box. The appropriate horizon name should be displayed along with description and color. At this stage, we have a number of options for adding, deleting, inserting, moving and breaking the line segment.
Choose the desired mode in the lower digitizer mode panel by selecting the appropriate radio button. By default, the digitizer should be in “add” mode.
Points can be added to the line segment, either by clicking successively on points in the image or by dragging the mouse across the image with the left button mouse button down.
I find the streaming mode difficult to control, and I prefer to enter points with a single mouse clicks.
Choose the delete mode in the digitizer mode panel by
clicking on the radio button, “
When the radio button is clicked, a symbol will be drawn at each of the digitized points.
When the cursor is moved over these points, a red locator circle will be drawn over the closest digitized point.
This point can be deleted by clicking the left mouse button.
To some degree this process may be streamed by holding the left mouse button down and dragging the cursor across the screen where points are to be deleted. Again, this streaming mode can be a bit difficult to control.
Digitized, or pick, positions can be adjusted by choosing the “move” mode.
Again, symbols will be drawn each for the pick positions.
Move the cursor to the current pick position, hold the left mouse button down, and drag the point to the desired position.
Choose the insert option to add points to the digitized line.
When this option is chosen, a red locator circle will be drawn around the closest point to the cursor.
When the left mouse button is clicked, a point will be added midway between the closest point and the next point in the horizon line.
Horizon lines may be discontinuous.
When one has finished digitizing a line segment, a break between segments can be inserted by clicking the break button.
To delete a a horizon, choose a horizon by clicking on the name in the horizons box and click the delete button.
To change the current horizon name, the horizon description, or the horizon color, choose the horizon in the horizons list box, and edit the desired entries, and press the edit button.
No work is saved until you press the “Save Digitize Horizons” button. When this is pressed, the changes will be stored in compressed XML in the SGYJP2 file.
If a mistake has been made and recognized before the save button has been pressed, the most recent stored XML data can be reread by pressing the refresh button, restoring the display to the previous save state.
Note : When in the
horizon mode, the use of the left button
for zooming in and zooming out is
disabled as that event is used for horizon manipulation. The right mouse button
can still be used for panning and the zoom and pan buttons on the Main page can
still be used. The left mouse button zoom function is restored when the horizon
window is dismissed.
As opposed to horizons which are line-type events, markers are point events.
These point events can be used to designate start and end of line, geological features, or any other such information as desired.
The marker form can be made visible and can be hidden by toggling the marker button on the main form.
Marker Box
This checkbox is used to turn the labels of the markers on or off.
The labels show the description that was entered when the market was created.
When this radio button is highlighted, a mouse click on the screen will select and highlight the closest marker.
This option is used to add markers and interpretation.
Enter a description in the description text box, and click on the image where you want the marker.
Often, if one is mapping repetitive features, the user can continue to click on the screen at different locations, and the last entered description will be used and saved with each marker.
Markers can be interactively moved on the canvas.
Check the move radio button and then click with your left mouse button on the desired marker. Hold the mouse button down, and drag the marker to the new position.
Markers can be interactively deleted using the mouse click.
Choose this option, then move close to the desired marker, and the program will designate the closest marker as active. This marker will be deleted by clicking the left mouse button.
One can change the description associated with the marker by clicking on the desired marker in the top list box, and then changing the description shown in the text box. Then click the edit selected button.
Choose one or more markers in the list box, using standard Windows type selection rules, and click delete selected.
Press this button to save the entire horizon package (including horizons and other features) to the source SGYJP2 file.
Note : When in the Marker
mode, the use of the left button for
zooming in and zooming out is disabled
as that event is used for marker manipulation. The right mouse button can still
be used for panning and the zoom and pan buttons on the main window can still be used. The left mouse button zoom
function is restored when the horizon window is dismissed.
The section functionality is used to segment the sonar file into areas of interest.
Often when conducting regional mapping, we may not want to map individual events, but would prefer to map areas of similar events; e.g., mapping an area of sand waves, as opposed to mapping each sound wave individually. The section functionality allows us to this and more.
To use this section
option, it is necessary to have positional information in the source sonar
data.
Section Form
Left mouse button zoom and right mouse button pan are available on this window is exposed, so one can navigate within the sonar file with no restrictions.
The generate auto sections button is used to segment the sonar file into sections of constant along-track length. This length interval is prescribed through the numeric input box.
The two direction buttons (<< and >>) can be used to switch the viewport to the next or previously-defined section. Similarly, a different viewport can be chosen by double-clicking on the entry in the list box.
The “Hold Y” checkbox can be used to ensure that only the horizontal axes is changed during the viewport change. This feature is useful if one needs vertical expansion to show detail in a seismic section.
This button will capture the currently defined window boundaries into the list box. The description entered into the lower text box will be saved along with the viewport.
As before, a previously-saved viewport can be chosen by double-clicking on the entry in the list box.
Select a desired section by double-clicking on the entry in the list box, and modify the description for this entry by typing in the lower text box. Then press the edit current button to save this modification.
Use this button to delete sections in the list box.
Delete
all:
Use this button to delete the entire list.
Use this button to save all input into the source sgyjp2 file. All modifications will be lost if this button or similar buttons in the horizon or marker windows have not been pressed before exiting.
The export button on the Main window is used to export interpretations as shape files for subsequent import into GIS systems. By default, there are four separate shape files that will be generated:
It is assumed that positional data is contained in the original source file.
The XY positions along the track of the survey can be exported into a shape file.
By default, these XY positions are exported in five-minute segments as a multi-segment POLYLINE. The export interval can be changed by changing the nav segment frequency numeric entry box.
At present, the “nav frequency” input has no effect.
The navigation shape file (which has the name of the original sonar file plus “_nav”) is accompanied by a DBF (database IV) file that gives the start and end times of each line segment within the shape file. This DBF file can be opened up with Microsoft Excel or many other spreadsheet programs.
If the sgyjp2 viewer
program has been assigned as the default program for opening jp2 files within
the Windows context, the hyperlink column in the DBF file will become active when
the _nav shape file is queried with the identify tool in the
ArcMap. This hyperlink will allow a user to seamlessly open up a designated
sgyjp2 sonar file in the viewer from within ArcMap.
Markers will be exported as POINT-type shape files with the extension, “_mark”. The associated DBF file will have columns for Julian day, time, trace number, two way time down the section (msec), description, and hyperlink.
As with the navigation shape files, the identify tool in ArcMap can be used to show the section if the sgyjp2 viewer is designated the default program for opening up “.jp2” files.
In future, the x and y positions and distance along track will be added as an additional column . So that the DBF file can be used as a stand-alone export format.
The sections defined in the section window will be exported as multi-segment polyline shape files with the extension, “_sections”. Each segment will be labeled in a DBF file with the start and end time, or the start and end trace number if trace time is not available, the description entered for each section, and a hyperlink to the file.
When this kind of file is imported ArcMap, the description of each of the sections can be used to label each line segment, providing an easy way of mapping the character of the section on an interval basis onto the digital map sheet within ArcMap.
Horizons are exported as a multi-segment POLYLINE ZM shape-file type with the extension, “_hor”. The Z. component of the file is the two way time of the horizon pick. The measure option within the shape file is not used.
The horizon export option allows both the picks alone to be exported or picks that have been linearly interpolated to give the value for each trace between picks. Obviously the latter option will generate larger files.
The horizon datum option is used to use one of the horizons is the datum, and the vertical time between a horizon and the data horizon is exported in the shape file. This option is useful, if they are errors in the trigger time delay of the sections or if the mapper is only interested in the two way time difference between horizons. Typically, the seabed is digitized along the whole section to establish a convenient datum.
It is not common to have precise projection and datum information contained within segy files. This added information is assigned in the section.
By default, it is assumed that the positions in the segy trace headers are in the WGS 84 (or equivalently the NAD83) datum. One can choose the NAD27 option to define an older datum for the trace header position data.
Often data are stored in the header in UTM coordinates, and in this case, the UTM zone must be entered manually.
If an error is made in
assigning these projection parameters, or if positional information is in a
projection or datum not defined here, these parameters can be overridden or
assigned within the GIS that imports the shape files.
By default, the shape files are exported to the same directory containing the sonar file. This output directory can be changed by pressing the shape file directory button.
Press this button to export layers that have been checked in the layers to export area of the window.
Trace data are stored in the image part of the JPEG2000 file in a compressed, variable bit length format.
Segy file and trace header information and other related ancillary data (e.g., JPEG 2000 compression and bounding box data) are stored in a user-defined box within the JPEG 2000 file as gzipped XML with the box label, “sxml”.
Interpretation data are stored in a separate box as gzipped XML with the box label, “hxml”.
These schema are subject to change. However, effort will be made to make newer versions backwards compatible with the versions presented here.
The schema were created in Visual Studio 2005 and consequently, the schema may be used to generate class libraries for I/O using the utility, XSD.exe for C++, C# or Visual Basic. I believe Java and other languages offer similar functionality.
A class library (written in C++ for Visual Studio 2005) for manipulating boxes within JPEG 2000 files is given in Appendix 2 and is available from the author.
We have limited capacity to provide assistance and support to outside users.
?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:ns1="SEGY_rev_1" attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="JPEG2000_SEGY">
<xs:complexType>
<xs:sequence>
<xs:element name="FileInfo">
<xs:complexType>
<xs:all>
<xs:element name="file_name" type="xs:string" maxOccurs="1" minOccurs="1" />
<xs:element type="xs:string" name="instrument_type" maxOccurs="1" minOccurs="1" />
<xs:element name="instrument_comments" type="xs:string" minOccurs="0" maxOccurs="1" />
<xs:element name="jpeg2000_compression_parameters" type="JPEG2000CompressionParameters" maxOccurs="1" minOccurs="1">
</xs:element>
<xs:element name="bounding_box" type="BoundingBox" maxOccurs="1" minOccurs="1">
</xs:element>
<xs:element name="URI_x0020__to_digital_file" type="xs:anyURI" minOccurs="0" maxOccurs="1" />
<xs:element name="URI_to_scanned_image_file" type="xs:anyURI" minOccurs="0" maxOccurs="1" />
<xs:element name="input_byte_order" type="xs:boolean" minOccurs="1" maxOccurs="1" />
<xs:element name="expedition_id" type="xs:string" />
<xs:element name="NAP_metadata_profile" type="NAPMetadataProfile" minOccurs="0" maxOccurs="1" />
<xs:element name="input_file_contents" type="FileContents" minOccurs="1" maxOccurs="1" />
<xs:element name="expedition_comments" type="xs:string" maxOccurs="1" minOccurs="0" />
<xs:element name="scientist" type="xs:string" minOccurs="0" maxOccurs="1" />
<xs:element name="data_type" type="xs:string" />
<xs:element name="transducer" type="xs:string" />
</xs:all>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="BoundingBox">
<xs:sequence>
<xs:element name="start_x0020_time" type="xs:dateTime" minOccurs="1" maxOccurs="1" />
<xs:element name="end_x0020_time" type="xs:dateTime" maxOccurs="1" minOccurs="1" />
<xs:element name="southern_latititude" type="xs:double" maxOccurs="1" minOccurs="1" />
<xs:element name="northern_latititude" type="xs:double" maxOccurs="1" minOccurs="1" />
<xs:element name="western_longitude" type="xs:double" maxOccurs="1" minOccurs="1" />
<xs:element name="eastern_longitude" type="xs:double" maxOccurs="1" minOccurs="1" />
<xs:element name="number_of_channels" type="xs:unsignedInt" maxOccurs="1" minOccurs="1" />
<xs:element name="number_of_traces" type="xs:unsignedInt" maxOccurs="1" minOccurs="1" />
<xs:element name="minimum_trace_time" type="xs:int" maxOccurs="1" minOccurs="1" />
<xs:element name="maximum_trace_time_x0020_" type="xs:int" maxOccurs="1" minOccurs="1" />
<xs:element name="number_of_samples_per_trace" type="xs:unsignedInt" />
<xs:any />
</xs:sequence>
</xs:complexType>
<xs:complexType name="BinaryFileHeader">
<xs:sequence>
<xs:element name="job_identification_number" type="xs:int" />
<xs:element name="line_number" type="xs:int">
</xs:element>
<xs:element name="reel_number" type="xs:int" />
<xs:element name="number_of_data_traces_per_ensemble" type="xs:short" />
<xs:element name="number_of_auxiliary_traces_per_ensemble" type="xs:short" />
<xs:element name="sample_interval_in_microseconds" type="xs:unsignedShort" />
<xs:element type="xs:unsignedShort" name="sample_interval_in_usec_of_original_field_recording" />
<xs:element name="number_of_samples_per_data_trace" type="xs:unsignedShort" />
<xs:element name="number_of_samples_per_data_trace_for_original_field_recording" type="xs:unsignedShort" />
<xs:element name="data_sample_format_code" type="xs:unsignedShort" />
<xs:element name="ensemble_fold" type="xs:unsignedShort" />
<xs:element name="trace_sorting_code" type="xs:short" />
<xs:element name="vertical_sum_code" type="xs:short" />
<xs:element name="sweep_frequency_at_start__x0028_Hz_x0029_" type="xs:unsignedShort" />
<xs:element name="sweep_frequency_at_end__x0028_Hz_x0029_" type="xs:unsignedShort" />
<xs:element name="sweep_length__x0028_ms_x0029_" type="xs:unsignedShort" />
<xs:element name="sweep_type_code" type="xs:unsignedShort" />
<xs:element name="trace_number_of_sweep_channel" type="xs:unsignedShort" />
<xs:element name="sweep_trace_taper_length_in_milliseconds_at_start" type="xs:unsignedShort" />
<xs:element name="sweep_trace_taper_length_in_milliseconds_at_end" type="xs:unsignedShort" />
<xs:element name="taper_type" type="xs:unsignedShort" />
<xs:element name="correlated_data_traces" type="xs:unsignedShort" />
<xs:element name="binary_gain_recovered" type="xs:unsignedShort" />
<xs:element name="amplitude_recovery_method" type="xs:unsignedShort" />
<xs:element name="measurement_system" type="xs:unsignedShort" />
<xs:element name="impulse_signal_polarity" type="xs:unsignedShort" />
<xs:element name="vibratory_polarity_code" type="xs:unsignedShort" />
<xs:element name="unassigned_3261_3500" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:base64Binary">
<xs:length value="240" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="SEG_Y_Format_Revision_Number" type="xs:unsignedShort" default="0" minOccurs="0" maxOccurs="1" />
<xs:element name="fixed_length_trace_flag" type="xs:unsignedShort" default="0" minOccurs="0" maxOccurs="1" />
<xs:element name="number_of_extended_Textual_files" type="xs:short" default="0" minOccurs="0" maxOccurs="1" />
<xs:element name="reservered">
<xs:simpleType>
<xs:restriction base="xs:base64Binary">
<xs:length value="94" />
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="BinaryTrace">
<xs:sequence>
<xs:element name="trace_in_line" type="xs:unsignedInt" minOccurs="0" maxOccurs="1" />
<xs:element name="trace_in_file" type="xs:unsignedInt" minOccurs="0" maxOccurs="1" />
<xs:element name="orig_field_record" type="xs:unsignedInt" minOccurs="0" maxOccurs="1" />
<xs:element name="trace_in_orig_field_record" type="xs:unsignedInt" minOccurs="0" maxOccurs="1" />
<xs:element name="energy_source_point" type="xs:unsignedInt" minOccurs="0" maxOccurs="1" />
<xs:element name="ensemble" type="xs:int" minOccurs="0" maxOccurs="1" />
<xs:element name="trace_in_ensemble" type="xs:int" minOccurs="0" maxOccurs="1" />
<xs:element name="trace_id_code" type="xs:short" minOccurs="0" maxOccurs="1" />
<xs:element name="vertically_summed_traces" type="xs:short" minOccurs="0" maxOccurs="1" />
<xs:element name="horizontally_summed_traces" type="xs:short" minOccurs="0" maxOccurs="1" />
<xs:element name="data_use" type="xs:short" minOccurs="0" maxOccurs="1" />
<xs:element name="distance_from_tx_to_rx" type="xs:int" minOccurs="0" maxOccurs="1" />
<xs:element name="rx_elevation_" type="xs:int" minOccurs="0" maxOccurs="1" />
<xs:element name="surface_elev_at_tx" type="xs:int" minOccurs="0" maxOccurs="1" />
<xs:element name="tx_depth_below_surface" type="xs:int" minOccurs="0" maxOccurs="1" />
<xs:element name="datum_elev_at_rx" type="xs:int" minOccurs="0" maxOccurs="1" />
<xs:element name="datum_elev_at_tx" type="xs:int" minOccurs="0" maxOccurs="1" />
<xs:element name="water_depth_at_tx" type="xs:int" minOccurs="0" maxOccurs="1" />
<xs:element name="water_depth_at_rx" type="xs:int" minOccurs="0" maxOccurs="1" />
<xs:element name="scalar_multiplier_69-70" type="xs:short" minOccurs="0" maxOccurs="1" />
<xs:element name="scalar_multiplier_71-72" type="xs:short" minOccurs="0" maxOccurs="1" />
<xs:element name="tx_x" type="xs:int" minOccurs="0" maxOccurs="1" />
<xs:element name="tx_y" type="xs:int" minOccurs="0" maxOccurs="1" />
<xs:element name="rx_x" type="xs:int" minOccurs="0" maxOccurs="1" />
<xs:element name="rx_y" type="xs:int" minOccurs="0" maxOccurs="1" />
<xs:element name="coordinate_units" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="weathering_velocity" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="subweathering_velocity" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="uphole_time_at_tx" type="xs:unsignedShort" minOccurs="0" maxOccurs="1 " />
<xs:element name="uphole_time_at_rx" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="tx_static_correction_" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="rx_static_correction_" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="total_static_correction_" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="lag_time_A" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="lag_time_B" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="delay_record_time" type="xs:short" minOccurs="0" maxOccurs="1" />
<xs:element name="mute_start_time" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="mute_end_time" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="number_of_samples_in_trace" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="sample_interval__x0028_usec_x0029_" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="gain_type_of_field_instruments" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="instrument_gain_constant" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="instrument_initial_gain" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="correlated" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="sweep_freq_start" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="sweep_freq_end" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="sweep_length__x0028_msec_x0029_" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="sweep_type" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="sweep_taper_length_start" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="sweep_taper_length_end" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="taper_type" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="antialias_filter_frequency" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="antialias_filter_slope" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="notch_filter_freq" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="notch_filter_slope" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="low-cut_freq" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="high_cut_freq" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="low-cut_slope" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="high-cut_slope" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="year_data_recorded" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="day" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="hr" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="min" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="sec" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="msec" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="time_weighting_factor" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="group_number_of_roll_sw_1" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="group_number_of_tr1" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="group_number_of_last_trace" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="gap_size" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="over_travel" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />
<xs:element name="trace_data" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:base64Binary">
<xs:maxLength value="64000" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="taper" type="xs:short" minOccurs="0" maxOccurs="1" />
<xs:element name="TriggerInterval" type="xs:short" minOccurs="0" maxOccurs="1" />
<xs:element name="reserved">
<xs:simpleType>
<xs:restriction base="xs:base64Binary">
<xs:length value="58" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:any />
</xs:sequence>
<xs:attribute name="is_reference_header" type="xs:boolean" default="false" />
<xs:attribute name="trc_num" type="xs:int" />
</xs:complexType>
<xs:complexType name="TextualFileHeader">
<xs:sequence>
<xs:element name="text" minOccurs="40" maxOccurs="40">
<xs:complexType>
<xs:sequence>
<xs:element name="ascii_string">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:length value="80" />
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
<xs:attribute name="line_number" type="xs:unsignedShort" />
</xs:complexType>
</xs:element>
<xs:element name="is_EBCDIC" type="xs:boolean" />
</xs:sequence>
<xs:attribute name="extended_header_number" type="xs:unsignedByte" />
</xs:complexType>
<xs:complexType name="JPEG2000CompressionParameters">
<xs:sequence>
<xs:element name="transform_offset" type="xs:double" />
<xs:element name="transform_gain" type="xs:double" />
<xs:element name="transform_function" type="xs:short" />
<xs:element name="number_of_significant_bits_in_input_stream" type="xs:unsignedByte" />
<xs:element type="xs:float" name="number_of_bits_per_sample_in_encoding" />
<xs:element name="rms_error_specified_in_encoding" type="xs:double" />
<xs:element name="input_trace_bipolar" type="xs:boolean" />
<xs:element name="significant_bits_at_top_of_word" type="xs:boolean" />
<xs:element name="minimum_trace_delay_x0020__x0028_msec_x0029_" type="xs:int" />
<xs:element name="padded_trace_length" type="xs:unsignedInt">
</xs:element>
<xs:element name="jp2000_wavelet_transform" type="xs:unsignedByte" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="SegyContents">
<xs:sequence>
<xs:element name="text_file_header" type="TextualFileHeader" minOccurs="1" />
<xs:element name="binary_file_header" type="BinaryFileHeader" minOccurs="1" maxOccurs="1" />
<xs:element name="binary_trace" type="BinaryTrace" minOccurs="1" maxOccurs="unbounded" />
<xs:element name="original_trace_converted_to_envelope" type="xs:boolean" minOccurs="0" maxOccurs="1" default="false" />
<xs:element name="envelope_exponent" type="xs:double" minOccurs="0" maxOccurs="1" default="1.0" />
<xs:element name="time_lines_embedded_in_trace_data" type="xs:boolean" default="false" maxOccurs="1" minOccurs="0" />
<xs:element name="horizontal_time_line_separation_msec" type="xs:double" minOccurs="0" maxOccurs="1" default="250" />
<xs:element name="vertical_time_line_separation_sec" type="xs:double" minOccurs="0" maxOccurs="1" default="600." />
<xs:element name="original_trace_converted_to_half_wave" type="xs:boolean" default="false" minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="NAPMetadataProfile">
<xs:sequence>
<xs:element name="to_x0020_be_x0020_defined" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="FileContents">
<xs:sequence>
<xs:element name="file_type" type="xs:string" />
<xs:element name="segy_contents" type="SegyContents" minOccurs="0" maxOccurs="1" />
<xs:element name="xtf_contents" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence />
</xs:complexType>
</xs:element>
<xs:element name="gsf_contents" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
Interpretations are
stored in a separate box in structured XML. The intent here is to separate
interpretations from the data, but keep them packaged together.
Although it has not
yet been implemented, it is intended to offer the capacity to have multiple
interpretations included with a given data set, offering the capacity to
exchange and reinterpret data sets in the lab and over the Internet.
In the following
schema, all X coordinates are sequential trace numbers and all Y coordinates
are two-way travel times in milliseconds.
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="horizons" targetNamespace="http://tempuri.org/horizons.xsd" elementFormDefault="qualified" xmlns="http://tempuri.org/horizons.xsd" xmlns:mstns="http://tempuri.org/horizons.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="horizon_x0020_pick">
<xs:sequence>
<xs:element name="x" type="xs:double" />
<xs:element name="y" type="xs:double" />
<xs:element name="line_break" type="xs:boolean" default="false" minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="horizon">
<xs:sequence>
<xs:element name="horizon_name" type="xs:string" minOccurs="1" maxOccurs="1" />
<xs:element name="description" type="xs:string" minOccurs="0" />
<xs:element name="color" type="xs:string" minOccurs="1" maxOccurs="1" />
<xs:element name="picks" type="horizon_x0020_pick" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="date_x0020_created" type="xs:dateTime" minOccurs="0" />
<xs:element name="date_x0020_modified" type="xs:dateTime" />
<xs:element name="mapper" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="horizon_x0020_group">
<xs:sequence>
<xs:element name="source_x0020_sgyjp2_x0020_file" type="xs:string" />
<xs:element name="horizons" type="horizon" minOccurs="1" maxOccurs="unbounded" />
<xs:element name="group_x0020_name" type="xs:string" />
<xs:element name="point_x0020_markers" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="x_x0020_time" type="xs:double" />
<xs:element name="y_x0020_time" type="xs:double" />
<xs:element name="trace_x0020_number" type="xs:int" />
<xs:element name="comment" type="xs:string" />
<xs:element name="marker" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="size" type="xs:double" />
<xs:element name="color" type="xs:int" />
<xs:element name="type" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="lat" type="xs:double" minOccurs="0" maxOccurs="1" />
<xs:element name="lon" type="xs:double" minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="frame_x0020_description" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="x_x0020_time_x0020_min" type="xs:double" />
<xs:element name="x_x0020_time_x0020_max" type="xs:double" />
<xs:element name="y_x0020_time_x0020_min" type="xs:double" />
<xs:element name="y_x0020_time_x0020_max" type="xs:double" />
<xs:element name="date_x0020_created" type="xs:dateTime" />
<xs:element name="orginal_x0020_author" type="xs:string" />
<xs:element name="last_x0020_date_x0020_modified" type="xs:dateTime" />
<xs:element name="last_x0020_author" type="xs:string" />
<xs:element name="description" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="version" type="xs:string" minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
<xs:element name="horizon_x0020_top_x0020_level">
<xs:complexType>
<xs:sequence>
<xs:element name="group" type="horizon_x0020_group" minOccurs="1" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
JPEG2000_Manage.h ( Visual Studio 2005 .NET)
#pragma once
using namespace
System;
using namespace
System::IO;
using namespace
System::IO::Compression;
using namespace
System::Runtime::InteropServices;
using namespace
System::Xml;
using namespace
System::Xml::Serialization;
using namespace
System::Collections;
ref class jp2_box_data
{
public:
unsigned int position;
unsigned int box_length;
String^ box_label;
Boolean xl_box;
Boolean zero_length_box;
};
#define IO_BUFFER_LENGTH
5000000; // define a 5 mbyte io buffer
ref class
JPEG2000_Manage
{
public:
JPEG2000_Manage(void);
property String^ filename;
property unsigned int box_length;
property Boolean xl_box;
property Boolean zero_length_box;
property FileStream^
filestream;
property BinaryReader^
binaryreader;
property BinaryWriter^
binarywriter;
Boolean has_sxml_content(void); // check to see if
it has segy formatted xml content
// open up a file for read write
Boolean open(void); // file name preset with property filename;
Boolean open(String^);
Boolean open(Stream^ s);
Boolean close(void);
Boolean is_valid_jp2(void); // looks for
"jP " header box
// get the next box header - returns box label as String^ ;
box length is in this->box_length; returns null string if failed or EOF
// leave the cuurent stream of file at start of box
contents
String^ get_next_box_header(void);
String^ get_next_box_header(Stream^ s);
// skip over the box contents leaving the file postion at
start of next jp2000 box
Boolean skip_box_contents(void);
Boolean skip_box_contents(Stream^ s);
// set filestream
void go_to_top_of_file(void);
// checks to see if at least one box with a given box_label
exists
bool find_box(String^ box_label);
// fix zero length last box in file
bool fix_zero_length_box();
// write an serializable object to an xml file
static Boolean write_object_to_xml_text_file(Object^ input_object,
String^ xml_text_file_name);
static Object^ read_object_from_xml_text_file(System::Type^ input_object_type, String^
xml_text_file_name);
// write a serializable object to a gzipped xml file
static Boolean write_object_to_xml_gzip_file(Object^ input_object,
String^ xml_gzip_file_name);
static Object^ read_object_from_xml_gzip_file(System::Type^ output_object_type, String^
xml_gzip_file_name);
static System::Void read_object_from_xml_gzip_file(Object^ output_object,
String^ xml_gzip_file_name);
// write an xml version of an object in gzipped format to a
stream
Boolean write_object_to_xml_gzip_stream(Object^ input_object,
Stream^ ms);
// write an xml version of an object in text format to a
stream
Boolean write_object_to_xml_stream(Object^ input_object,
Stream^ ms);
// write serializable object in gzipped xml
format to a jp2000 compliant box at the current stream position
Boolean write_compressed_xml_box(Object^ input_object,
String^ box_label,
String^ gzip_xml_box_file_name);
// write serializable object in gzipped xml format to a
jp2000 compliant box at the current stream position
Boolean write_compressed_xml_box(Object^ input_object,
String^ box_label,
Stream^ s);
// write serializable object in readable xml format to a jp2000 compliant
box in a file
Boolean write_xml_box(Object^ input_object,
String^ box_label,
String^ xml_box_file_name);
// write serializable object in readable xml format to a jp2000 compliant
box at the current stream position
Boolean write_xml_box(Object^ input_object,
String^ box_label,
Stream^ s);
// Read an xml object from a stream whether compressed or
not
Object^ read_xml_box(System::Type^ input_object_type, String^
box_label , Stream^
s);
// Copy a jp2000 compliant
box from one stream to another
Boolean copy_box(Stream^ input_stream,
Stream^ output_stream);
// get a summary list of jp2000 boxes in current file -
creates an arraylist of type jp2_box_data
ArrayList^ get_box_data(void);
// compress current jp2 file
Boolean compress_existing_sxml_box(String^ new_file_name);
// does the file contain compressed xml content
Boolean has_compressed_sxml_content(void);
private:
Boolean input_file_is_open;
array<unsigned char,1>^ box_type;
array<unsigned char,1>^ box_XL;
void Write_Box_Header_to_MemoryStream(String ^box_label, BinaryWriter ^bw, MemoryStream ^ms);
};
JPEG2000_Manage.cpp ( Visual Studio 2005
.NET)
#include "StdAfx.h"
#include "stdio.h"
#include "JPEG2000_Manage.h"
#include "SEGY.h"
// sxml header class
// utilities
System::UInt32 swap(UInt32 x)
{
// implement a swap
array<unsigned char,1>^ c = BitConverter::GetBytes(x);
unsigned char tmp;
tmp = c[0];
c[0] = c[3];
c[3] = tmp;
tmp = c[1];
c[1] = c[2];
c[2] = tmp;
return BitConverter::ToUInt32(c,0);
}
System::UInt16 swap(UInt16 x)
{
// implement a swap
array<unsigned char,1>^ c = BitConverter::GetBytes(x);
unsigned char tmp;
tmp = c[0];
c[0] = c[1];
c[1] = tmp;
return BitConverter::ToUInt16(c,0);
}
System::Int16 swap(Int16 x)
{
// implement a swap
array<unsigned char,1>^ c = BitConverter::GetBytes(x);
unsigned char tmp;
tmp = c[0];
c[0] = c[1];
c[1] = tmp;
return BitConverter::ToInt16(c,0);
}
JPEG2000_Manage::JPEG2000_Manage(void)
{
this->filestream
= nullptr;
this->binaryreader
= nullptr;
this->filename = nullptr;
this->input_file_is_open
= false;
this->box_length
= -1;
}
System::Boolean
JPEG2000_Manage::has_sxml_content(void)
{
return this->find_box("sxml");
}
System::Boolean
JPEG2000_Manage::open(void)
{
if( this->filename == nullptr)
return false;
this->input_file_is_open=
false;
try
{
this->filestream
= File::Open(this->filename,FileMode::OpenOrCreate);
}
catch (System::Exception^ )
{
return false;
}
try
{
this->binaryreader
= gcnew BinaryReader(this->filestream);
}
catch ( System::Exception^)
{
return false;
}
try
{
this->binarywriter=
gcnew BinaryWriter(this->filestream);
}
catch ( System::Exception^)
{
return false;
}
this->input_file_is_open
= true;
return true;
}
System::Boolean
JPEG2000_Manage::open(String^ input_name)
{
this->filename = String::Copy(input_name); // set
filename
return this->open();
}
System::Boolean
JPEG2000_Manage::open(Stream^ s)
{
this->filestream
= (FileStream^) s;
try
{
this->binaryreader
= gcnew BinaryReader(this->filestream);
}
catch ( System::Exception^)
{
return false;
}
try
{
this->binarywriter=
gcnew BinaryWriter(this->filestream);
}
catch ( System::Exception^)
{
return false;
}
this->filename = "Stream input"; //
set filename
return true;
}
String^ JPEG2000_Manage::get_next_box_header(void)
{
return this->get_next_box_header(this->filestream);
}
String^ JPEG2000_Manage::get_next_box_header(Stream
^s)
{
if ( s->Position >= s->Length ) return nullptr; // at eof
BinaryReader^
binaryreader = gcnew BinaryReader(s);
// read the box header bytes
// jp2 are
big-endian
box_length = swap(binaryreader->ReadUInt32()) ;
// set zero box
length flag
this->zero_length_box = false;
if( !box_length)
{
this->zero_length_box = true;
this->box_length = s->Length - s->Position + 4;
}
//
read box type
box_type = binaryreader->ReadBytes(4);
this->xl_box = false;
// read next eight
bytes - note first 4 bytes should be all zeroes for 32 bit file sizes
if ( box_length == 1 )
{
this->xl_box = true;
box_XL = binaryreader->ReadBytes(8);
// byte swap part of
word
for ( int i = 0; i < 4 ; i++ ) {
unsigned char tmp = box_XL[i];
if ( tmp != NULL )
{
// high bytes should
be zero for 32 bit files lengths
return nullptr;
}
box_XL[i] = box_XL[7-i];
box_XL[7-i] = tmp;
}
// limit to 32 bit
lengths; adjust length for preamble bytes
box_length =Convert::ToUInt32( BitConverter::ToUInt64(box_XL,0)) ;
}
// copy box_type
into holding array - this is a real hack!!
unsigned char tmp[5];
for ( int i = 0; i < 4; i++) tmp[i] = box_type[i];
tmp[4] = 0;
String^ name = Marshal::PtrToStringAnsi( (IntPtr) tmp);
return name;
}
Boolean JPEG2000_Manage::close(void)
{
this->filestream->Close();
this->input_file_is_open
= false;
return true;
}
Boolean JPEG2000_Manage::is_valid_jp2(void)
{
this->go_to_top_of_file();
// make sure the first box type is jp2/jp2
if(String::Compare(this->get_next_box_header(),"jP ") == 0 )
{
this->go_to_top_of_file();
return true;
}
return false;
}
void JPEG2000_Manage::go_to_top_of_file(void)
{
if( this->input_file_is_open) this->filestream->Seek(0,SeekOrigin::Begin);
}
Boolean JPEG2000_Manage::skip_box_contents(void)
{
return this->skip_box_contents(this->filestream);
}
Boolean JPEG2000_Manage::skip_box_contents(Stream^
s)
{
// advance to end of box contents
unsigned int new_position;
if( this->box_length > 0 )
{
new_position = s->Position + this->box_length
- 8;
if ( this->xl_box) new_position
-= 8; // adjust skip for extra 8 bytes in xl box
if ( new_position
>= s->Length)
{
// EOF
s->Position = s->Length ;
return false;
}
else {
s->Position = new_position ;
}
return true;
}
else
{
// box extends to end of file - zero length trace
// find current file position
int box_start = s->Position - 8;
s->Position = s->Length; // move to EOF
this->box_length
= s->Position
- box_start;
return true;
}
}
bool JPEG2000_Manage::fix_zero_length_box()
{
ArrayList^ boxdata
= this->get_box_data();
// last box
this->filestream->Position = ((jp2_box_data^)boxdata[boxdata->Count - 1])->position;
if ( ((jp2_box_data^)boxdata[boxdata->Count - 1])->zero_length_box
)
{
unsigned int swapped = swap(((jp2_box_data^)boxdata[boxdata->Count -
1])->box_length);
BinaryWriter^ bw = gcnew BinaryWriter(this->filestream);
bw->Write( swapped);
}
// done
return true;
}
bool JPEG2000_Manage::find_box(String^ boxname)
{
if( !this->input_file_is_open ) return
false;
if( !this->is_valid_jp2()) return
false;
// loop through boxes and box contents
while ( String^ name = this->get_next_box_header())
{
if ( String::Compare(name,boxname) == 0 )
{
this->go_to_top_of_file();
return true;
}
this->skip_box_contents();
if( this->filestream->Position
== this->filestream->Length - 1 ) // eof
condition
{
this->go_to_top_of_file();
return false;
}
}
return false;
}
// this routine writes
an object to a XML text file
Boolean JPEG2000_Manage::write_object_to_xml_text_file(Object^ input_object,
String^ xml_text_file_name)
{
XmlSerializer^ ser
= gcnew XmlSerializer(input_object->GetType());
XmlTextWriter^ writer
= gcnew XmlTextWriter(
xml_text_file_name, System::Text::Encoding::UTF8);
ser->Serialize(writer,input_object);
writer->Close();
return true;
}
Object^ JPEG2000_Manage::read_object_from_xml_text_file(System::Type^ input_object_type, String^
xml_text_file_name)
{
Object^ output_object
= nullptr;
XmlSerializer^ ser
= gcnew XmlSerializer(input_object_type);
XmlTextReader^ reader
= gcnew XmlTextReader(xml_text_file_name);
output_object = ser->Deserialize(reader);
reader->Close();
return output_object;
}
Object^
JPEG2000_Manage::read_object_from_xml_gzip_file(System::Type^ output_object_type, String^
xml_gzip_file_name)
{
// we should be a the start of the desired box
// check the next two bytes if they are 1f8b then its a
gzip compressed box
Object^ output_object = nullptr;
FileStream^ s = gcnew FileStream(xml_gzip_file_name,System::IO::FileMode::Open);
XmlSerializer^ ser
= gcnew XmlSerializer(output_object_type);
GZipStream^ gs = gcnew GZipStream(s,CompressionMode::Decompress);
output_object
= ser->Deserialize(gs);
gs->Close();
s->Close();
return output_object;
}
System::Void JPEG2000_Manage::read_object_from_xml_gzip_file(Object^ output_object,
String^ xml_gzip_file_name)
{
// we should be a the start of the desired box
// check the next two bytes if they are 1f8b then its a
gzip compressed box
FileStream^ s = gcnew FileStream(xml_gzip_file_name,System::IO::FileMode::Open);
XmlSerializer^ ser
= gcnew XmlSerializer(output_object->GetType());
GZipStream^ gs = gcnew GZipStream(s,CompressionMode::Decompress);
output_object
= ser->Deserialize(gs);
return;
}
Boolean JPEG2000_Manage::write_object_to_xml_gzip_file(Object^ input_object,
String^ xml_gzip_file_name)
{
FileStream^ fs
= nullptr;
try
{
fs = gcnew FileStream(xml_gzip_file_name,FileMode::Create);
}
catch ( System::Exception^ e)
{
return false;
}
GZipStream ^ compressedzipStream
= gcnew GZipStream(fs,CompressionMode::Compress,true );
XmlSerializer^ ser
= gcnew XmlSerializer(input_object->GetType());
XmlTextWriter^ writer
= gcnew XmlTextWriter(
compressedzipStream, System::Text::Encoding::UTF8);
ser->Serialize(writer,input_object);
writer->Close();
compressedzipStream->Close();
fs->Close();
return true;
}
Boolean JPEG2000_Manage::write_object_to_xml_gzip_stream(Object^ input_object,
Stream^ ms)
{
GZipStream ^ compressedzipStream
= gcnew GZipStream(ms,CompressionMode::Compress,true );
this->write_object_to_xml_stream(input_object,compressedzipStream);
compressedzipStream->Close();
return true;
}
Boolean JPEG2000_Manage::write_object_to_xml_stream(Object^
input_object, Stream^
ms)
{
XmlSerializer^ ser
= gcnew XmlSerializer(input_object->GetType());
XmlTextWriter^ writer
= gcnew XmlTextWriter(
ms, System::Text::Encoding::UTF8);
ser->Serialize(writer,input_object);
return true;
}
Boolean JPEG2000_Manage::write_compressed_xml_box(Object^
input_object, String^
box_label, String^
gzip_xml_box_file_name)
{
FileStream^ ms = gcnew FileStream(gzip_xml_box_file_name,FileMode::Create);
BinaryWriter^
bw = gcnew BinaryWriter(ms);
// write 8 byte
preamble to box
bw->Write((int)0);
char* str2 = (char*)(void*)Marshal::StringToHGlobalAnsi(box_label);
for( int i = 0; i < 4; i++) bw->Write((unsigned char) str2[i]);
// write gzipped
contents
this->write_object_to_xml_gzip_stream(input_object,ms);
// update length of
box
unsigned int ms_size = ms->Length;
ms->Position = 0;
bw->Write(swap(ms_size));
// close up
bw->Close();
return true;
}
Boolean JPEG2000_Manage::write_xml_box(Object^
input_object, String^
box_label, String^
xml_box_file_name)
{
FileStream^ ms = gcnew FileStream(xml_box_file_name,FileMode::Create);
BinaryWriter^
bw = gcnew BinaryWriter(ms);
// write 8 byte
preamble to box
bw->Write((int)0);
char* str2 = (char*)(void*)Marshal::StringToHGlobalAnsi(box_label);
for( int i = 0; i < 4; i++) bw->Write((unsigned char) str2[i]);
// write contents
.......
this->write_object_to_xml_stream(input_object,ms);
// update length of
box
unsigned int ms_size = ms->Length;
ms->Position = 0;
bw->Write(swap(ms_size));
// close up
bw->Close();
return true;
}
void JPEG2000_Manage::Write_Box_Header_to_MemoryStream(String ^box_label, BinaryWriter ^bw, MemoryStream ^ms)
{
// write preamble to stream - note that a memory stream
< 2 Gbyte
bw->Write(swap((unsigned int)(ms->Length + 8)));
char* str2 = (char*)(void*)Marshal::StringToHGlobalAnsi(box_label);
for( int i = 0; i < 4; i++) bw->Write((unsigned char) str2[i]);
}
Boolean JPEG2000_Manage::write_xml_box(Object^
input_object, String^
box_label, Stream^
s)
{
BinaryWriter^
bw = gcnew BinaryWriter(s);
MemoryStream^
ms = gcnew MemoryStream();
// put text xml in a
memory stream
this->write_object_to_xml_stream(input_object,ms);
Write_Box_Header_to_MemoryStream(box_label, bw, ms);
// dump memory stream to parent stream
ms->WriteTo(s);
return true;
}
Boolean JPEG2000_Manage::write_compressed_xml_box(Object^
input_object, String^
box_label, Stream^
s)
{
BinaryWriter^
bw = gcnew BinaryWriter(s);
MemoryStream^
ms = gcnew MemoryStream();
// put gzip xml in a
memory stream
this->write_object_to_xml_gzip_stream(input_object,ms);
Write_Box_Header_to_MemoryStream(box_label, bw, ms);
// dump memory stream to parent stream
ms->WriteTo(s);
return true;
}
ArrayList^ JPEG2000_Manage::get_box_data(void)
{
ArrayList^ r = gcnew ArrayList();
this->go_to_top_of_file();
while(this->filestream->Position
< this->filestream->Length)
{
jp2_box_data^ bd = gcnew jp2_box_data();
bd->position = this->filestream->Position;
bd->box_label = this->get_next_box_header();
bd->box_length =
this->box_length;
bd->xl_box = this->xl_box;
bd->zero_length_box
= this->zero_length_box;
r->Add(bd);
this->skip_box_contents();
}
return r;
}
// Read an xml object
from a stream whether compressed or not
Object^ JPEG2000_Manage::read_xml_box(System::Type^ input_object_type,
String^ box_label
, Stream^ s)
{
Object^ output_object
= nullptr;
while ( String::Compare(this->get_next_box_header(s),
box_label) != 0 )
{
this->skip_box_contents(s);
if( s->Position >=
s->Length )return nullptr;
}
// we should be a the start of the desired box
// check the next two bytes if they are 1f8b then its a
gzip compressed box
BinaryReader^ br = gcnew BinaryReader(s);
unsigned char c1 = br->ReadByte();
unsigned char c2 = br->ReadByte();
s->Position -=
2; // back up
// read box contents
MemoryStream^ ms ;
if( this->xl_box )
{
ms = gcnew MemoryStream(br->ReadBytes(this->box_length-16));
}
else
{
ms = gcnew MemoryStream(br->ReadBytes(this->box_length-8));
}
XmlSerializer^ ser
= gcnew XmlSerializer(input_object_type);
if ( c1 == 31
&& c2 == 139 )
{
// this is gzipped xml
GZipStream^ gs = gcnew GZipStream(ms,CompressionMode::Decompress);
output_object
= ser->Deserialize(gs);
}
else {
output_object
= ser->Deserialize(ms);
}
return output_object;
}
Boolean JPEG2000_Manage::copy_box(Stream^ is, Stream^ os)
{
// straight copy of one box from one stream to another
// read box header
String^ is_box_label
= this->get_next_box_header(is);
if( this->xl_box)
{
is->Position -=
16; // xl box
}
else {
is->Position -=
8; // back up
}
if ( is_box_label ==
nullptr ) return
false;
// total length of box
unsigned int is_length = this->box_length;
if( this->box_length == 0 )
{
// this is the last box in the file
is_length = is->Length - is->Position;
}
// assign an io buffer;
unsigned int buffer_length = IO_BUFFER_LENGTH;
if( buffer_length
> is_length
) buffer_length = is_length ;
array<unsigned char>^ buffer = gcnew array<unsigned char>(buffer_length);
// number of reads
unsigned int nread_full = is_length/buffer_length;
for ( int i = 0; i < nread_full; i++ )
{
is->Read(buffer,0,buffer_length);
os->Write(buffer,0,buffer_length);
}
unsigned int nread_partial = is_length
% buffer_length;
if( nread_partial )
{
is->Read(buffer,0,nread_partial);
os->Write(buffer,0,nread_partial);
}
return true;
}
Boolean JPEG2000_Manage::has_compressed_sxml_content(void)
{
unsigned int save_position = this->filestream->Position;
ArrayList^ bd = this->get_box_data();
// get input box info
// look for sxml header
for ( int i = 0; i < bd->Count; i++ )
{
jp2_box_data^ b = (jp2_box_data^)bd[i];
if ( String::Compare(b->box_label,"sxml")
== 0 )
{
// found sxml tag
this->filestream->Position = b->position + 8;
if( b->xl_box ) this->filestream->Position
+= 8; // xl box
// next two characters are gzip magic numbers
unsigned char c1 = this->binaryreader->ReadByte();
unsigned char c2 = this->binaryreader->ReadByte();
if ( c1 == 31
&& c2 == 139 )
{
this->filestream->Position = save_position;
return true;
}
}
}
this->filestream->Position = save_position;
return false;
}
Boolean
JPEG2000_Manage::compress_existing_sxml_box(String^
new_file_name)
{
try
{
JPEG2000_Manage^ newjp2
= gcnew JPEG2000_Manage();
newjp2->open(new_file_name); // open a
blank file
ArrayList^ bd = this->get_box_data();
// get input box info
this->go_to_top_of_file();
// rewind existing file
for ( int i = 0; i < bd->Count; i ++ ) // cycle through
boxes
{
jp2_box_data^
bdi = (::jp2_box_data^)
bd[i];
if ( String::Compare(bdi->box_label,"sxml") == 0 )
{
JPEG2000_SEGY^
jp2sgy = gcnew
JPEG2000_SEGY() ;
jp2sgy = (JPEG2000_SEGY^) this->read_xml_box(jp2sgy->GetType(),"sxml",this->filestream);
//rewrite in a
compressed format
this->write_compressed_xml_box(jp2sgy,"sxml",newjp2->filestream);
} else {
this->copy_box(this->filestream, newjp2->filestream);
}
}
// insure output file has not zero length boxes
newjp2->fix_zero_length_box();
newjp2->close();
}
catch ( System::Exception^
)
{
return false;
}
return true;
}
This application can be made the default viewer for “.jp2” files. To do this, right click on a “.jp2” file on your desktop in an explorer window. Choose the “Open With” option followed by “Choose Program”.
Click onthe Browse button and navigate to the NRCan/SegyJp2Viewer subdirectory under Program Files. Double click on the SegyViewer.exe entry. Then click the “Always use the….” checkbox.
From this point, any sgyjp2 file can be viewed by double clicking on the file in a file browser.
In addition, shape file withbhyperlinks pointing to “.jp2” files in ArcMap will now use this viewer.
The catch : image- type jp2 files can’t be viewed with
this application.