/*
* Copyright (C) 2014 by Array Systems Computing Inc. http://www.array.ca
*
* 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 3 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, see http://www.gnu.org/licenses/
*/
package org.esa.s1tbx.io.generic;
import com.bc.ceres.core.ProgressMonitor;
import org.esa.snap.core.dataio.AbstractProductWriter;
import org.esa.snap.core.dataio.ProductWriterPlugIn;
import org.esa.snap.core.datamodel.Band;
import org.esa.snap.core.datamodel.MetadataElement;
import org.esa.snap.core.datamodel.ProductData;
import org.esa.snap.core.datamodel.ProductNode;
import org.esa.snap.core.datamodel.VirtualBand;
import org.esa.snap.core.util.Guardian;
import org.esa.snap.engine_utilities.datamodel.AbstractMetadata;
import org.esa.snap.engine_utilities.datamodel.metadata.AbstractMetadataIO;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.ByteOrder;
public class GenericBSQWriter extends AbstractProductWriter {
private ImageOutputStream _outputStream = null;
private String bandName = null;
private int bandCounter = 0;
private int numOfWriteBands = 0; // number of bands that are written (no virtual bands)
/**
* Construct a new instance of a product writer for the given product writer plug-in.
*
* @param writerPlugIn the given product writer plug-in, must not be <code>null</code>
*/
public GenericBSQWriter(final ProductWriterPlugIn writerPlugIn) {
super(writerPlugIn);
}
/**
* Writes the in-memory representation of a data product. This method was called by <code>writeProductNodes(product,
* output)</code> of the AbstractProductWriter.
*
* @throws IllegalArgumentException if <code>output</code> type is not one of the supported output sources.
* @throws java.io.IOException if an I/O error occurs
*/
@Override
protected void writeProductNodesImpl() throws IOException {
// _outputStream = null;
final File file;
if (getOutput() instanceof String) {
file = new File((String) getOutput());
} else {
file = (File) getOutput();
}
_outputStream = new FileImageOutputStream(file);
// Default to nativeOrder
_outputStream.setByteOrder(ByteOrder.nativeOrder());
final MetadataElement absRoot = AbstractMetadata.getAbstractedMetadata(getSourceProduct());
AbstractMetadataIO.saveExternalMetadata(getSourceProduct(), absRoot, file);
// Get number of Real (not Virtual) bands
final int numOfBands = getSourceProduct().getNumBands();
for (int i = 0; i < numOfBands; i++) {
if (!(getSourceProduct().getBandAt(i) instanceof VirtualBand)) {
numOfWriteBands++;
}
}
}
/**
* {@inheritDoc}
*/
public void writeBandRasterData(final Band sourceBand,
final int sourceOffsetX,
final int sourceOffsetY,
final int sourceWidth,
final int sourceHeight,
final ProductData sourceBuffer,
ProgressMonitor pm) throws IOException {
Guardian.assertNotNull("sourceBand", sourceBand);
Guardian.assertNotNull("sourceBuffer", sourceBuffer);
checkBufferSize(sourceWidth, sourceHeight, sourceBuffer);
final int sourceBandWidth = sourceBand.getRasterWidth();
final int sourceBandHeight = sourceBand.getRasterHeight();
checkSourceRegionInsideBandRegion(sourceWidth, sourceBandWidth, sourceHeight, sourceBandHeight, sourceOffsetX, sourceOffsetY);
// Define order number of Band that is being saved
if (bandName == null) {
bandName = sourceBand.getName();
} else if (!bandName.equals(sourceBand.getName())) {
bandName = sourceBand.getName();
bandCounter++;
}
// Write all source NOT VIRTUAL bands in BSQ : Band Sequential Format
long outputPos = sourceOffsetY * (numOfWriteBands * sourceBandWidth) + sourceOffsetX + (bandCounter * sourceBandWidth);
pm.beginTask("Writing band '" + sourceBand.getName() + "'...", sourceHeight);
try {
final long max = sourceHeight * sourceWidth;
for (int sourcePos = 0; sourcePos < max; sourcePos += sourceWidth) {
sourceBuffer.writeTo(sourcePos, sourceWidth, _outputStream, outputPos);
outputPos += (numOfWriteBands * sourceBandWidth);
}
pm.worked(1);
} finally {
pm.done();
}
}
private static void checkSourceRegionInsideBandRegion(int sourceWidth, final int sourceBandWidth, int sourceHeight,
final int sourceBandHeight, int sourceOffsetX,
int sourceOffsetY) {
Guardian.assertWithinRange("sourceWidth", sourceWidth, 1, sourceBandWidth);
Guardian.assertWithinRange("sourceHeight", sourceHeight, 1, sourceBandHeight);
Guardian.assertWithinRange("sourceOffsetX", sourceOffsetX, 0, sourceBandWidth - sourceWidth);
Guardian.assertWithinRange("sourceOffsetY", sourceOffsetY, 0, sourceBandHeight - sourceHeight);
}
// from BEAM EnviProductWriter
private static void checkBufferSize(int sourceWidth, int sourceHeight, ProductData sourceBuffer) {
final int expectedBufferSize = (sourceWidth * sourceHeight);
final int actualBufferSize = sourceBuffer.getNumElems();
Guardian.assertEquals("sourceWidth * sourceHeight", actualBufferSize, expectedBufferSize); /*I18N*/
}
/**
* Deletes the physically representation of the given product from the hard disk.
*/
public void deleteOutput() {
}
/**
* Writes all data in memory to disk. After a flush operation, the writer can be closed safely
*
* @throws java.io.IOException on failure
*/
public void flush() throws IOException {
if (_outputStream != null) {
_outputStream.flush();
}
}
/**
* Closes all output streams currently open.
*
* @throws java.io.IOException on failure
*/
public void close() throws IOException {
if (_outputStream != null) {
_outputStream.flush();
_outputStream.close();
_outputStream = null;
}
}
/**
* Returns wether the given product node is to be written.
*
* @param node the product node
* @return <code>true</code> if so
*/
@Override
public boolean shouldWrite(ProductNode node) {
return !(node instanceof VirtualBand) && super.shouldWrite(node);
}
}