/*
* Copyright (C) 2016 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.sunraster;
import com.bc.ceres.core.ProgressMonitor;
import org.esa.snap.core.dataio.AbstractProductReader;
import org.esa.snap.core.datamodel.Band;
import org.esa.snap.core.datamodel.Product;
import org.esa.snap.core.datamodel.ProductData;
import org.esa.snap.engine_utilities.datamodel.Unit;
import org.esa.snap.engine_utilities.gpf.ReaderUtils;
import javax.imageio.stream.FileImageInputStream;
import javax.imageio.stream.ImageInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.ByteOrder;
/**
* Reader for sun raster files
*/
public class SunRasterReader extends AbstractProductReader {
private ImageInputStream inStream;
private int width, height, depth;
private static final int RAS_MAGIC = 0x59a66a95;
private static final int COMPRESSION_NONE = 0x00000001;
private static final int RAS_HEADER_SIZE = 32;
public SunRasterReader() {
super(null);
}
protected Product readProductNodesImpl() throws IOException {
final File inputFile = ReaderUtils.getFileFromInput(getInput());
inStream = new FileImageInputStream(inputFile);
inStream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
readHeader();
final Product product = new Product(inputFile.getName(), "SunRaster",
width, height);
product.setProductReader(this);
product.setFileLocation(inputFile);
final Band band = new Band("data", ProductData.TYPE_FLOAT32, width, height);
band.setUnit(Unit.AMPLITUDE);
product.addBand(band);
return product;
}
private void readHeader() throws IOException {
byte[] header = new byte[RAS_HEADER_SIZE];
inStream.readFully(header);
int magic = getIntBE(header, 0);
if (magic != RAS_MAGIC) {
throw new IOException("This stream is not a valid " +
"Sun RAS stream (bad magic: " + Integer.toHexString(magic) +
" instead of " + Integer.toHexString(RAS_MAGIC));
}
width = getIntBE(header, 4);
height = getIntBE(header, 8);
if (width < 1 || height < 1) {
throw new IOException("Width and height must both " +
"be larger than zero; found width=" + width + ", height=" +
height + ".");
}
depth = getIntBE(header, 12);
}
private static int getIntBE(byte[] src, int srcOffset) {
return (src[srcOffset + 3] & 0xff) |
((src[srcOffset + 2] & 0xff) << 8) |
((src[srcOffset + 1] & 0xff) << 16) |
((src[srcOffset] & 0xff) << 24);
}
protected void readBandRasterDataImpl(int sourceOffsetX, int sourceOffsetY,
int sourceWidth, int sourceHeight,
int sourceStepX, int sourceStepY,
Band destBand,
int destOffsetX, int destOffsetY,
int destWidth, int destHeight,
ProductData destBuffer,
ProgressMonitor pm) throws IOException {
final int sourceMaxY = sourceOffsetY + sourceHeight - 1;
final int elemSize = destBuffer.getElemSize();
final int headerOffset = RAS_HEADER_SIZE;
final long lineSizeInBytes = width * elemSize;
ProductData lineData = ProductData.createInstance(destBuffer.getType(), sourceWidth);
pm.beginTask("Reading band '" + destBand.getName() + "'...", sourceMaxY - sourceOffsetY);
try {
int destPos = 0;
for (int sourceY = sourceOffsetY; sourceY <= sourceMaxY; sourceY += sourceStepY) {
if (pm.isCanceled()) {
break;
}
synchronized (inStream) {
long lineStartPos = headerOffset + sourceY * lineSizeInBytes;
inStream.seek(lineStartPos + elemSize * sourceOffsetX);
lineData.readFrom(0, sourceWidth, inStream);
}
for (int x = 0; x < sourceWidth; x++) {
destBuffer.setElemDoubleAt(destPos++, lineData.getElemDoubleAt(x));
}
pm.worked(1);
}
} finally {
pm.done();
}
}
@Override
public void close() throws IOException {
if (inStream != null) {
inStream.close();
}
super.close();
}
}