In this sample code, we will demonstrate how to use the INTGeo API to create virtual datasets and save them to disk.
For the purpose of this demonstration, we will take as input an image file and create a virtual dataset from that file. The transparent pixels are interpreted as traces with a sample value of 0 while all other pixels represent a sample value of 10. When seen as a map, the virtual dataset represents the original image. This virtual dataset is then written to disk. In this example, we take this image:
When the program completes, the dataset can be seen as follow from a screen capture of INTViewer's Map window. Note the INLINE and XLINE axes. Each horizontal pixel in the original file represents one INLINE. Each vertical pixel represents one XLINE.
The CreateLogoSeismic class contains the main method for the demo. Given an icon image path, a custom seismic data format LogoSeismicData is created. The data is then written to a file by the IDataWriter.
This class assumes that the INT_Logo.png file is located in the writable C:\image directory. Change the paths in CreateLogoSeismic.java to match your configuration.
The LogoSeismicData class represents the virtual seismic data created from the specified image file. This is a volume dataset with no missing traces. The inline key is based on the width of the image, the xline key is based on the height.
LOGOSEISMICDATA.JAVA
package logoseismic;
import com.interactive.intgeo.seismic.data.EbcdicHeader;
import com.interactive.intgeoapi.IMemento;
import com.interactive.intgeoapi.PersistentException;
import com.interactive.intgeoapi.data.AbstractData;
import com.interactive.intgeoapi.data.FieldDesc;
import com.interactive.intgeoapi.data.IDataChooser;
import com.interactive.intgeoapi.data.IFieldDesc;
import com.interactive.intgeoapi.data.IJToXYTransformation;
import com.interactive.intgeoapi.data.IKeyRange;
import com.interactive.intgeoapi.data.query.IDataQuery;
import com.interactive.intgeoapi.data.seismic.ISeismicData;
import com.interactive.intgeoapi.data.seismic.ISeismicReader;
import com.interactive.intgeoapi.data.seismic.ISeismicStatistics;
import com.interactive.intgeoapi.data.seismic.SeismicKeyRange;
import com.interactive.intgeoapi.units.IUnit;
import com.interactive.seismicDataAdapter.util.StepPrecisionUtil;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
public class LogoSeismicData extends AbstractData implements ISeismicData {
public static final int INLINE_ID = 1;
public static final int XLINE_ID = 2;
private IUnit iunit = IUnit.Factory.getUnit(IUnit.SECONDS_SYMBOL);
private String epsgCode;
private IJToXYTransformation transformation;
private EbcdicHeader ebcdicHeader = new EbcdicHeader(new String[]{"Generated using INTGeo API"});
private BufferedImage bi;
private int minInline = 100;
private int minXline = 200;
private double minTime = 0;
private int samplesPerTrace = 100;
private double sampleRate = 0.004;
private int numberOfInlineIncrements;
private int numberOfXlineIncrements;
private int numberOfTraces;
public LogoSeismicData(String imagePath) {
bi = createBufferedImage(imagePath);
numberOfInlineIncrements = bi.getWidth();
numberOfXlineIncrements = bi.getHeight();
numberOfTraces = numberOfInlineIncrements * numberOfXlineIncrements;
}
private BufferedImage createBufferedImage(String imagePath) {
try {
File file = new File(imagePath);
return ImageIO.read(file);
} catch(Exception ex) {
Logger.getLogger(LogoSeismicData.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
@Override
public boolean hasOrder(int i) {
if (i == ISeismicData.ORDER_XSECTION) {
return true;
} else {
return false;
}
}
@Override
public IKeyRange getSampleKey(int i) {
if (i == ISeismicData.ORDER_XSECTION) {
List<IKeyRange> keyRanges = this.getDataRange();
return keyRanges.get(keyRanges.size() - 1);
} else {
throw new UnsupportedOperationException("Can't create seismic map reader");
}
}
@Override
public IJToXYTransformation getIJToXYTransformation() {
return this.transformation;
}
@Override
public void setIJToXYTransformation(IJToXYTransformation ijtxyt) {
this.transformation = ijtxyt;
}
@Override
public String getEPSGCode() {
return this.epsgCode;
}
@Override
public void setEPSGCode(String epsgCode) {
this.epsgCode = epsgCode;
}
@Override
public void addPropertyChangeListener(PropertyChangeListener pl) {
// Do nothing.
}
@Override
public void removePropertyChangeListener(PropertyChangeListener pl) {
// Do nothing.
}
@Override
public String getDataType() {
return IDataChooser.SEISMIC;
}
@Override
public String getDataPath() {
return "";
}
@Override
public void saveState(IMemento im) {
// Does nothing.
}
@Override
public void restoreState(IMemento im) throws PersistentException {
// Does nothing.
}
@Override
public void setZUnit(IUnit iunit) {
this.iunit = iunit;
}
@Override
public IUnit getZUnit() {
return iunit;
}
@Override
protected Object[] getLookupObjects() {
List<Object> result = new ArrayList<>();
result.add(ebcdicHeader);
return result.toArray();
}
@Override
public List<IKeyRange> getDataRange() {
List<IKeyRange> keyRanges = new ArrayList<>();
int maxInline = minInline + numberOfInlineIncrements;
SeismicKeyRange inlineKeyRange = new SeismicKeyRange("INLINE", minInline, maxInline);
inlineKeyRange.setIncrement(1);
keyRanges.add(inlineKeyRange);
int maxXline = minXline + numberOfXlineIncrements;
SeismicKeyRange xlineKeyRange = new SeismicKeyRange("XLINE", minXline, maxXline);
xlineKeyRange.setIncrement(1);
keyRanges.add(xlineKeyRange);
double maxTime = StepPrecisionUtil.getNormalizedLineValue(minTime, samplesPerTrace - 1, sampleRate);
SeismicKeyRange timeKeyRange = new SeismicKeyRange("Time", minTime, maxTime);
timeKeyRange.setIncrement(sampleRate);
keyRanges.add(timeKeyRange);
return keyRanges;
}
@Override
public String getDataName() {
return "Int logo seismic data";
}
@Override
public ISeismicStatistics getDataStatistics() {
return new LogoSeismicStatistics(minTime, sampleRate, samplesPerTrace, numberOfTraces);
}
@Override
public ISeismicReader select(IDataQuery selection) throws Exception {
return new LogoSeismicReader(bi, 1, numberOfInlineIncrements,
1, numberOfXlineIncrements, minTime, sampleRate, samplesPerTrace);
}
public IFieldDesc[] getTraceHeaderFields() {
IFieldDesc[] fieldDescs = new IFieldDesc[2];
fieldDescs[0] = new FieldDesc("INLINE", INLINE_ID, true);
fieldDescs[1] = new FieldDesc("XLINE", XLINE_ID, true);
return fieldDescs;
}
@Override
public IFieldDesc[] getTraceHeaderFields(int order) {
if (order == ISeismicData.ORDER_XSECTION) {
return getTraceHeaderFields();
} else {
return null;
}
}
}
The LogoSeismicReader class represents the collection of traces for the LogoSeismicData . This reader is responsible for providing the header and sample values of each trace. The number of traces is determined by the number of pixels in the image file.
LOGOSEISMICREADER
package logoseismic;
import com.interactive.intgeoapi.data.FieldDesc;
import com.interactive.intgeoapi.data.IFieldDesc;
import com.interactive.intgeoapi.data.query.SeismicRangeQuery;
import com.interactive.intgeoapi.data.seismic.ISeismicReader;
import com.interactive.intgeoapi.data.seismic.ISeismicStatistics;
import com.interactive.intgeoapi.data.seismic.ITrace;
import java.awt.image.BufferedImage;
public class LogoSeismicReader implements ISeismicReader{
private int inlineIncrement;
private int xlineIncrement;
private int numberOfXlineIncrements;
private double minTime;
private double sampleRate;
private int samplesPerTrace;
private int numberOfTraces;
private BufferedImage bi;
public LogoSeismicReader(BufferedImage image, int inlineIncrement,
int numberOfInlineIncrements, int xlineIncrement,
int numberOfXlineIncrements, double minTime, double sampleRate, int samplesPerTrace) {
this.bi = image;
this.inlineIncrement = inlineIncrement;
this.xlineIncrement = xlineIncrement;
this.numberOfXlineIncrements = numberOfXlineIncrements;
this.minTime = minTime;
this.sampleRate = sampleRate;
this.samplesPerTrace = samplesPerTrace;
this.numberOfTraces = numberOfInlineIncrements * numberOfXlineIncrements;
}
@Override
public ISeismicStatistics getDataStatistics() {
return new LogoSeismicStatistics(minTime, sampleRate, samplesPerTrace, numberOfTraces);
}
@Override
public int getNumberOfTraces() {
return numberOfTraces;
}
@Override
public ITrace getTrace(int i) throws IllegalArgumentException {
int inlineIndex = i / numberOfXlineIncrements;
inlineIndex = (inlineIndex * inlineIncrement);
int xlineIndex = i % numberOfXlineIncrements;
xlineIndex = (xlineIndex * xlineIncrement);
return new LogoTrace(bi, samplesPerTrace, inlineIndex, xlineIndex);
}
@Override
public IFieldDesc[] getTraceHeaderFields() {
IFieldDesc[] fieldDescs = new IFieldDesc[2];
fieldDescs[0] = new FieldDesc("INLINE", LogoSeismicData.INLINE_ID, true);
fieldDescs[1] = new FieldDesc("XLINE", LogoSeismicData.XLINE_ID, true);
return fieldDescs;
}
@Override
public IFieldDesc getTraceHeaderField(String string) {
switch (string) {
case "INLINE":
return new FieldDesc("INLINE", LogoSeismicData.INLINE_ID, true);
case "XLINE":
return new FieldDesc("XLINE", LogoSeismicData.XLINE_ID, true);
}
return null;
}
@Override
public int findFirstTrace(SeismicRangeQuery srq) {
return 0; // not important for generation
}
@Override
public double getPreviousValue(String string, double d) {
return 0; // not important for generation
}
@Override
public double getNextValue(String string, double d) {
return 0; // not important for generation
}
}
The LogTrace class represents each seismic trace for the LogoSeismicData. Each trace is represented by a pixel from the image file. As mentioned above the sample values are determined by the transparency of the pixel.
LOGOTRACE.JAVA
package logoseismic;
import com.interactive.intgeoapi.data.seismic.ITrace;
import java.awt.image.BufferedImage;
public class LogoTrace implements ITrace {
private int samplesPerTrace;
private int inlineLocation;
private int xlineLocation;
private BufferedImage bi;
public LogoTrace(BufferedImage image, int samplesPerTrace, int inlineIndex, int xlineIndex) {
this.bi = image;
this.samplesPerTrace = samplesPerTrace;
this.inlineLocation = inlineIndex;
this.xlineLocation = xlineIndex;
}
@Override
public Number getHeader(int i) {
if(i == LogoSeismicData.INLINE_ID) {
return inlineLocation;
}
if (i == LogoSeismicData.XLINE_ID) {
return xlineLocation;
}
return 0d;
}
@Override
public void putHeader(Integer intgr, Number number) {
// Does nothing.
}
@Override
public int getSamples(float[] floats) {
int rgb = bi.getRGB(inlineLocation, xlineLocation);
float value;
//negative rgb means opaque
if (rgb < 0) {
value = 0;
} else {
value = 10;
}
for (int i = 0; i < floats.length; i++) {
floats[i] = value;
}
return samplesPerTrace;
}
@Override
public void setSamples(float[] floats) {
// Does nothing.
}
@Override
public int getSize() {
return samplesPerTrace;
}
}
The LogoSeismicStatistics class provides the general statistics for the LogoSeismicData. Most of these values are not relevant for this demonstration.
LOGOSEISMICSTATISTICS.JAVA
package logoseismic;
import com.interactive.intgeoapi.data.seismic.ISeismicStatistics;
public class LogoSeismicStatistics implements ISeismicStatistics{
private double minTime;
private double sampleRate;
private int samplesPerTrace;
private int numberOfTraces;
public LogoSeismicStatistics(double minTime, double sampleRate, int samplesPerTrace, int numberOfTraces) {
this.minTime = minTime;
this.sampleRate = sampleRate;
this.samplesPerTrace = samplesPerTrace;
this.numberOfTraces = numberOfTraces;
}
@Override
public float getSampleRate() {
return (float)this.sampleRate;
}
@Override
public int getNumberOfTraces() {
return this.numberOfTraces;
}
@Override
public int getSamplesPerTrace() {
return this.samplesPerTrace;
}
@Override
public float getStartValue() {
return (float)this.minTime;
}
@Override
public float getMinimumAmplitude() {
return 0; // not important for generation
}
@Override
public float getMaximumAmplitude() {
return 10; // not important for generation
}
@Override
public float getAverage() {
return 5; // not important for generation
}
@Override
public float getRMS() {
return 0; // not important for generation
}
@Override
public float[] getPercentiles() {
return new float[0];
}
}
In this implementation, samples values are identical for all samples of a trace. As a result, INLINE and XLINE slices show the same sample values for any selected time: all time slices show the same logo. This is best shown in a 3D visualization as follow:
More options to write Segy files are described in these articles: