/*
* 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.gamma;
import com.bc.ceres.core.ProgressMonitor;
import org.esa.s1tbx.io.gamma.header.Header;
import org.esa.snap.core.dataio.AbstractProductReader;
import org.esa.snap.core.dataio.ProductReaderPlugIn;
import org.esa.snap.core.datamodel.Band;
import org.esa.snap.core.datamodel.MetadataElement;
import org.esa.snap.core.datamodel.Product;
import org.esa.snap.core.datamodel.ProductData;
import org.esa.snap.core.util.io.FileUtils;
import org.esa.snap.engine_utilities.datamodel.AbstractMetadata;
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.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Reader for stamps insar products
*/
public class GammaReader extends AbstractProductReader {
private Header header;
private boolean isComplex = false;
private boolean isCoregistered = false;
private Map<Band, ImageInputStream> bandImageInputStreamMap = new HashMap<>();
public GammaReader(ProductReaderPlugIn readerPlugIn) {
super(readerPlugIn);
}
protected Product readProductNodesImpl() throws IOException {
final File inputParFile = ReaderUtils.getFileFromInput(getInput());
final BufferedReader headerReader = new BufferedReader(new FileReader(inputParFile));
try {
header = new Header(headerReader);
isComplex = isComplex(inputParFile);
isCoregistered = isCoregistered(inputParFile);
String productType = isComplex ? "SLC" : header.getDataType();
final Product product = new Product(header.getName(), productType,
header.getNumSamples(), header.getNumLines());
product.setProductReader(this);
product.setFileLocation(inputParFile);
product.setDescription(header.getDescription());
product.getMetadataRoot().addElement(header.getAsMetadata());
final int dataType = getDataType();
final File[] imageFiles = findImageFiles(inputParFile);
for (File imgFile : imageFiles) {
final ImageInputStream inStream = new FileImageInputStream(imgFile);
inStream.setByteOrder(header.getJavaByteOrder());
if (isComplex) {
final Band tgtBandI = new Band("i_" + imgFile.getName(), dataType, header.getNumSamples(), header.getNumLines());
tgtBandI.setUnit("real");
product.addBand(tgtBandI);
final Band tgtBandQ = new Band("q_" + imgFile.getName(), dataType, header.getNumSamples(), header.getNumLines());
tgtBandQ.setUnit("imaginary");
product.addBand(tgtBandQ);
bandImageInputStreamMap.put(tgtBandI, inStream);
bandImageInputStreamMap.put(tgtBandQ, inStream);
ReaderUtils.createVirtualIntensityBand(product, tgtBandI, tgtBandQ, imgFile.getName());
ReaderUtils.createVirtualPhaseBand(product, tgtBandI, tgtBandQ, "_" + imgFile.getName());
} else {
final Band tgtBand = new Band(imgFile.getName(), dataType, header.getNumSamples(), header.getNumLines());
product.addBand(tgtBand);
bandImageInputStreamMap.put(tgtBand, inStream);
}
}
addGeoCoding(product);
addMetaData(product);
return product;
} finally {
headerReader.close();
}
}
private boolean isComplex(final File file) {
String name = file.getName().toLowerCase();
name = FileUtils.getFilenameWithoutExtension(name);
String ext = FileUtils.getExtension(name);
return ext != null && (ext.endsWith("slc") || ext.endsWith("diff"));
}
private boolean isCoregistered(final File file) {
String name = file.getName().toLowerCase();
name = FileUtils.getFilenameWithoutExtension(name);
String ext = FileUtils.getExtension(name);
return ext != null && ext.endsWith("rslc");
}
private int getDataType() throws IOException {
final String imageFormat = header.getDataType();
if ("SCOMPLEX".equals(imageFormat)) {
return ProductData.TYPE_INT16;
} else if ("FCOMPLEX".endsWith(imageFormat)) {
return ProductData.TYPE_FLOAT32;
}
return ProductData.TYPE_FLOAT32;
}
private File[] findImageFiles(final File parFile) {
final File[] files = parFile.getParentFile().listFiles();
final String baseName = FileUtils.getFilenameWithoutExtension(parFile);
final List<File> fileList = new ArrayList<>();
if (files != null) {
for (File file : files) {
if (file.getName().startsWith(baseName) && !file.getName().equals(parFile.getName())) {
fileList.add(file);
}
}
}
return fileList.toArray(new File[fileList.size()]);
}
private File getImageFile(final File parFile) {
return new File(parFile.getParentFile(), FileUtils.getFilenameWithoutExtension(parFile));
}
private void addGeoCoding(final Product product) {
}
private void addMetaData(final Product product) {
final MetadataElement root = product.getMetadataRoot();
final MetadataElement absRoot = AbstractMetadata.addAbstractedMetadataHeader(root);
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.PRODUCT, header.getName());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.MISSION, header.getSensorType());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.SPH_DESCRIPTOR, header.getDescription());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.PRODUCT_TYPE, isComplex ? "SLC" : header.getDataType());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.sample_type, isComplex ? "COMPLEX" : "DETECTED");
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.num_samples_per_line, header.getNumSamples());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.num_output_lines, header.getNumLines());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.first_line_time, header.getStartTime());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.last_line_time, header.getEndTime());
product.setStartTime(header.getStartTime());
product.setEndTime(header.getEndTime());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.line_time_interval, header.getLineTimeInterval());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.radar_frequency, header.getRadarFrequency());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.pulse_repetition_frequency, header.getPRF());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.range_looks, header.getRangeLooks());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.azimuth_looks, header.getAzimuthLooks());
AbstractMetadata.setAttribute(absRoot, AbstractMetadata.coregistered_stack, isCoregistered ? 1 : 0);
}
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 ImageInputStream inStream = bandImageInputStreamMap.get(destBand);
if (isComplex) {
final int sourceMaxY = sourceOffsetY + sourceHeight;
final int elemSize = destBuffer.getElemSize();
int bandIndex = 0;
if (destBand.getUnit() != null && destBand.getUnit().equals(Unit.IMAGINARY)) {
bandIndex = 1;
}
// band interleaved by pixel
int numInterleaved = 2;
final long lineSizeInBytes = header.getNumSamples() * numInterleaved * elemSize;
ProductData lineData = ProductData.createInstance(destBuffer.getType(), sourceWidth * numInterleaved);
pm.beginTask("Reading band '" + destBand.getName() + "'...", sourceMaxY - sourceOffsetY);
try {
final long xPos = elemSize * sourceOffsetX * numInterleaved;
final int xLength = sourceWidth * numInterleaved;
int dstCnt = 0;
for (int sourceY = sourceOffsetY; sourceY < sourceMaxY; sourceY += sourceStepY) {
if (pm.isCanceled()) {
break;
}
synchronized (inStream) {
inStream.seek(sourceY * lineSizeInBytes + xPos);
lineData.readFrom(0, xLength, inStream);
}
for (int x = 0; x < sourceWidth; x++) {
destBuffer.setElemDoubleAt(dstCnt++, lineData.getElemDoubleAt(x * numInterleaved + bandIndex));
}
pm.worked(1);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
pm.done();
}
} else {
final int sourceMaxY = sourceOffsetY + sourceHeight - 1;
Product product = destBand.getProduct();
final int elemSize = destBuffer.getElemSize();
final int headerOffset = header.getHeaderOffset();
final int bandIndex = product.getBandIndex(destBand.getName());
final long lineSizeInBytes = header.getNumSamples() * 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 + bandIndex));
}
pm.worked(1);
}
} catch (Exception e) {
} finally {
pm.done();
}
}
}
@Override
public void close() throws IOException {
for (ImageInputStream inStream : bandImageInputStreamMap.values()) {
if (inStream != null) {
inStream.close();
}
}
super.close();
}
}