Working With Seismic Data

Note: You will need to include SeismicData to your Module Dependencies list.

Loading a seismic dataset from a file

The seismic data API provides seamless access to all seismic data formats supported in INTViewer. We load a seismic dataset with the path filePath and print some statistics about the seismic data. If your application requires a file chooser for selecting the seismic file, you can use the one provided with INTViewer. See section Using the Seismic File Chooser below for an example.

    import com.interactive.intviewerapi.data.IData;
    import com.interactive.intviewerapi.data.seismic.ISeismicData;
    import com.interactive.intviewerapi.data.seismic.ISeismicStatistics;
    ...
    try {
        ISeismicData sd = IData.factory.createNewInstance(filePath, ISeismicData.class);
    } catch (Exception ex) {
        String msg = "Error opening dataset " + filePath;
        DialogManager.getDefault().showMessageDialog(msg,
            "Error Loading Seismic", DialogManager.ERROR);
        return;
    }
     ISeismicStatistics stats = sd.getDataStatistics();
     System.out.println("Number of traces = " + stats.getNumberOfTraces());
     System.out.println("Number of samples per trace = "  + stats.getSamplesPerTrace());

Internally the default implementation of the factory uses Netbeans FileObject and DataObject to load the data files. Netbeans filesystem supports the notion of a data pool, so that if you open several times the same file, you will get the same data object instance.

Using the seismic file chooser

This example shows how to let a user select a seismic file using the built-in seismic file selection dialog in INTViewer. The chooser is automatically setup to filter valid seismic filenames.

To select one dataset:

   import com.interactive.intviewerapi.data.ISingleDataChooser;
   // get the chooser for seismic data type
   ISingleDataChooser<ISeismicData> singleChooser = ISingleDataChooser.Factory.createNewInstance(IDataChooser.SEISMIC);
   // popup the chooser dialog
   int rc = singleChooser.setDataChooserVisible(true);
   if (rc == JFileChooser.APPROVE_OPTION) {
      ISeismicData seismicData = singleChooser.getSelectedData();
   }

To select multiple datasets:

   import com.interactive.intviewerapi.data.IMultipleDataChooser;
   // get the file chooser for seismic data type
   IMultipleDataChooser<ISeismicData> multipleChooser = IMultipleDataChooser.Factory.createNewInstance(IDataChooser.SEISMIC);
   // popup the chooser dialog
   int rc = multipleChooser.setDataChooserVisible(true);
   if (rc == JFileChooser.APPROVE_OPTION) {
      for (ISeismicData seismicData : multipleChooser.getSelectedData()) {
      }
   }

Getting information about the data keys

Range keys provide information about how to access the seismic data. They can be accessed as follows:

List<IKeyRange> keyRangeList = sd.getDataRange();

For basic dataset in formats like Segy or SU, there will only be two keys defined ("TraceNumber" and "Time"). For an indexed dataset, the number of keys in the list will be the number of keys used for indexing plus the sample key. For example, if a Segy dataset has been indexed with two keys, let say INLINE and XLINE, then keyRangeList will contain 3 keys, "INLINE", "XLINE" and "Time". Please note that the keys are always ordered from the slowest varying to the fastest one. The last key is always the trace sample key ("Time" or "Depth" typically).

The example below prints the values for the keys. For seismic data, the keys also implement ISeismicKeyRange to provide access to the data increment and sort order.

    import com.interactive.intviewerapi.data.IKeyRange;
    import com.interactive.intviewerapi.data.seismic.ISeismicKeyRange;
    ...
    for (IKeyRange key: keyRangeList) {
        System.out.println("keyname=" + key.getName() + " min=" + key.getMinimum() +
                           " max=" + key.getMaximum());
        System.out.println("increment=" + ((ISeismicKeyRange)key).getIncrement() +
                       " sortOrder=" + ((ISeismicKeyRange)key).getKeySortOrder());
     }

Querying the traces

The interface ISeismicData has a select method which returns a reader that can be used to access the selected traces. A query needs to be passed as an argument to the select as shown below.

    import com.interactive.intviewerapi.data.seismic.SeismicKeyRange;
    import com.interactive.intviewerapi.data.seismic.ISeismicReader;
    import com.interactive.intviewerapi.data.query.SeismicRangeQuery;
    ...
    // in this example we query for both an inline and xline range
    SeismicRangeQuery query = new SeismicRangeQuery(ISeismicData.ORDER_XSECTION);
    SeismicKeyRange inlineRange  = new SeismicKeyRange("INLINE", 10, 25);
    query.addKeyRange(inlineRange);
    SeismicKeyRange xlineRange = new SeismicKeyRange("XLINE", 1, 400);
    query.addKeyRange(xlineRange);
    try {
        ISeismicReader reader = sd.select(query);
    } catch (Exception ex) {
        DialogManager.getDefault().showMessageDialog("Error during query: " + ex.getMessage(),
            "Error Querying Seismic", DialogManager.ERROR);
    }

In the above example we set the first key (typically the INLINE) to the first value. If our dataset is a volume, this query will give us access to the first inline.

If you work with volumes or pre-stack dataset, you can also perform a query by path. The following example shows how to use SeismicPathQuery to query traces along a path.

    import com.interactive.intviewerapi.data.query.SeismicPathQuery;
    ...
    SeismicPathQuery query = new SeismicPathQuery(
        "INLINE",
        new double[] {367,386,327,247,274},
        "XLINE",
        new double[] {246,402,472,539,606});
    layer.select(query);

Reading the trace samples

The example below shows how to extract the first trace from the reader obtained in the previous section.

    import com.interactive.intviewerapi.data.seismic.ITrace;
    ...
    try {
        int ntraces = reader.getNumberOfTraces();
        if (ntraces > 0) {
            ITrace trace = reader.getTrace(0);
            float values[] = new float[trace.getSize()];
            trace.getSamples(values);
        }
    } catch (Exception ex) {
        ...
    }

Reading the header values

The example below print the OFFSET header values for the traces selected above.

    import com.interactive.intviewerapi.data.IFieldDesc;
    ...
    IFieldDesc offsetHeader = reader.getTraceHeaderField("OFFSET");
    if (offsetHeader != null) {
        for (int i=0; i<reader.getNumberOfTraces(); i++) {
            Float value = reader.getTrace(i).getHeader(offsetHeader.getIdentifier()).floatValue();
            System.out.println("Offset value = " + value);
         }
     }

Transforming a file based seismic dataset

There are cases where you need to transform and display the transformed seismic data on the fly. If the transformed data has the same number of samples per trace and the same sample rate as the initial dataset, you can use the TraceProcessor API. If you cannot use this API, you can implement your own ISeismicData object that wraps and transforms on-the-fly a file based ISeismicData object. A typical transformation would be a time to depth conversion. In the example below and for the sake of simplicity, we implement a simple transformation that resamples the original traces. This transformation changes both the sample rate and the number of samples. This may be better in many cases than creating a memory based seismic dataset because:

  1. you can transform a dataset of any size without having to worry about memory
  2. you can save the transformed dataset into one of the supported file formats.
  3. It supports persistence (to save and restore a session)

The implementation of this transformation requires to implement four methods, to provide implementation for ISeismic, ISeismicStatistics, ISeismicReader and ITrace. Although this sounds complicated, in reality it is quite simple as we rely on the implementation of the original SeismicData object. Our new classes are called ResampledSeismicData, ResampledStatistics, ResampledSeismicReader and ResampledTrace.

The code for this example is organized in two files:

Saving a seismic dataset

IDataWriter interface has methods to let users write an entire IDataObject or a subset of IDataObject into an OutputStream. All data formats for ISeismicData ( example segy format, su format, and so on ...) has a different mime type. Each mime type has a IDataWriter registered in the layer.xml.

            // to get the correct mime type for output file TestOutput.sgy, we would do the following
            import org.openide.filesystems.FileObject;
            import org.openide.filesystems.FileUtil;
            String outputFileName  = TestOutput.sgy;
            File selFile = new File(outputFileName);
            FileObject fo = null;
            if (selFile.exists())
                  fo = FileUtil.toFileObject(selFile);
            else
                  fo = FileUtil.createData(selFile);
           
            String mimeType  =  fo.getMIMEType();
            //Now to write a ISeismicData into the TestOutputFile.sgy, do the following:
                          ISeismicData data=.....
            IDataQuery query=...... ( if we want to save a subset of the ISeismicData, create a IDataQuery that includes time range as well)
            DataUtil.writeData(data, query, outputFileName, mimeType, new ILongTaskProgressMonitor.NullProgressMonitor());

Currently we support IDataWriter for *.sgy, *.xgy, *.su, *.xsu, and *.sep file formats. Alternatively, you can select the output file through a ISingleDataSaver to write the ISeismicData in the above formats, like so:

        import com.interactive.intviewerapi.layers.ISeismicLayer;
        import com.interactive.intviewerapi.data.seismic.ISeismicData;
        import com.interactive.intviewerapi.data.ISingleDataSaver;
        ISeismicLayer layer = .....
        IDataQuery currentQuery = layer.getQuery();
        ISeismicData data = (ISeismicData) layer.getData();
        ISingleDataSaver ds = ISingleDataSaver.Factory.createNewInstance(IDataChooser.SEISMIC);
        if (ds.setDataSaverVisible(true, data.getDataPath()) == JFileChooser.APPROVE_OPTION) {
            try {
                ds.saveDataAndWait(data, currentQuery);
            } catch (Exception ex) {
                String msg = "Error saving Seismic Data in a File :\n" + ex.getMessage();
                DialogManager.getDefault().showMessageDialog(msg, "Error saving Seismic", DialogManager.ERROR);
            }
        }

Creating or loading seismic datasets in memory

Examples are available in this section.

Implementing your own seismic format loading

Examples are available in this section.

Discovering the vertical unit of a seismic dataset

Unit handling is explained in this section

Indexing one or several seismic datasets

Examples are available in this section.