/*
* 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.insar.gpf;
import com.bc.ceres.core.ProgressMonitor;
import org.esa.s1tbx.insar.gpf.support.SARUtils;
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.gpf.Operator;
import org.esa.snap.core.gpf.OperatorException;
import org.esa.snap.core.gpf.OperatorSpi;
import org.esa.snap.core.gpf.Tile;
import org.esa.snap.core.gpf.annotations.OperatorMetadata;
import org.esa.snap.core.gpf.annotations.SourceProduct;
import org.esa.snap.core.gpf.annotations.TargetProduct;
import org.esa.snap.core.util.ProductUtils;
import org.esa.snap.engine_utilities.datamodel.AbstractMetadata;
import org.esa.snap.engine_utilities.datamodel.Unit;
import org.esa.snap.engine_utilities.gpf.InputProductValidator;
import org.esa.snap.engine_utilities.gpf.OperatorUtils;
import org.esa.snap.engine_utilities.gpf.TileIndex;
import java.awt.*;
import java.util.Map;
@OperatorMetadata(alias = "PhaseToDisplacement",
category = "Radar/Interferometric/Products",
authors = "Jun Lu, Luis Veci",
version = "1.0",
copyright = "Copyright (C) 2016 by Array Systems Computing Inc.",
description = "Phase To Displacement Conversion along LOS")
public final class PhaseToDisplacementOp extends Operator {
@SourceProduct(alias = "source")
private Product sourceProduct;
@TargetProduct
private Product targetProduct;
private int sourceImageWidth = 0;
private int sourceImageHeight = 0;
private double wavelength = 0.0;
private Band unwrappedPhaseBand;
private static final String PRODUCT_SUFFIX = "_Disp";
/**
* Initializes this operator and sets the one and only target product.
* <p>The target product can be either defined by a field of type {@link Product} annotated with the
* {@link TargetProduct TargetProduct} annotation or
* by calling {@link #setTargetProduct} method.</p>
* <p>The framework calls this method after it has created this operator.
* Any client code that must be performed before computation of tile data
* should be placed here.</p>
*
* @throws OperatorException If an error occurs during operator initialisation.
* @see #getTargetProduct()
*/
@Override
public void initialize() throws OperatorException {
try {
final InputProductValidator validator = new InputProductValidator(sourceProduct);
validator.checkIfMapProjected(false);
// input should be flat-Earth-phase and topo-phase removed unwrapped phase
getMetadata();
createTargetProduct();
} catch (Throwable e) {
OperatorUtils.catchOperatorException(getId(), e);
}
}
/**
* Retrieve required data from Abstracted Metadata
*
* @throws Exception if metadata not found
*/
private void getMetadata() throws Exception {
final MetadataElement absRoot = AbstractMetadata.getAbstractedMetadata(sourceProduct);
wavelength = SARUtils.getRadarFrequency(absRoot);
sourceImageWidth = sourceProduct.getSceneRasterWidth();
sourceImageHeight = sourceProduct.getSceneRasterHeight();
}
/**
* Create target product.
*/
private void createTargetProduct() {
targetProduct = new Product(sourceProduct.getName() + PRODUCT_SUFFIX,
sourceProduct.getProductType(),
sourceImageWidth,
sourceImageHeight);
addSelectedBands();
ProductUtils.copyProductNodes(sourceProduct, targetProduct);
}
/**
* Add user selected bands to target product.
*/
private void addSelectedBands() {
final Band[] sourceBands = sourceProduct.getBands();
boolean validProduct = false;
for (Band band : sourceBands) {
if (band.getName().toLowerCase().startsWith("unw")) {
validProduct = true;
unwrappedPhaseBand = band;
break;
}
}
if (!validProduct) {
throw new OperatorException("Cannot find UnwrappedPhase band in the source product.");
}
final Band targetBand = new Band("displacement", ProductData.TYPE_FLOAT32, sourceImageWidth, sourceImageHeight);
targetBand.setUnit(Unit.METERS);
targetProduct.addBand(targetBand);
}
/**
* Called by the framework in order to compute the stack of tiles for the given target bands.
* <p>The default implementation throws a runtime exception with the message "not implemented".</p>
*
* @param targetTiles The current tiles to be computed for each target band.
* @param targetRectangle The area in pixel coordinates to be computed (same for all rasters in <code>targetRasters</code>).
* @param pm A progress monitor which should be used to determine computation cancelation requests.
* @throws OperatorException if an error occurs during computation of the target rasters.
*/
@Override
public void computeTileStack(Map<Band, Tile> targetTiles, Rectangle targetRectangle, ProgressMonitor pm)
throws OperatorException {
try {
final Band sourceBand = unwrappedPhaseBand;
final Tile sourceTile = getSourceTile(sourceBand, targetRectangle);
final ProductData sourceData = sourceTile.getDataBuffer();
final Band targetBand = targetProduct.getBand("displacement");
final Tile targetTile = targetTiles.get(targetBand);
final ProductData targetData = targetTile.getDataBuffer();
final TileIndex srcIndex = new TileIndex(sourceTile);
final TileIndex trgIndex = new TileIndex(targetTile);
final int x0 = targetRectangle.x;
final int y0 = targetRectangle.y;
final int w = targetRectangle.width;
final int h = targetRectangle.height;
final double wavelengthOver4PI = wavelength / (4.0*Math.PI);
for (int y = y0; y < y0 + h; y++) {
srcIndex.calculateStride(y);
trgIndex.calculateStride(y);
for (int x = x0; x < x0 + w; x++) {
final double phase = sourceData.getElemDoubleAt(srcIndex.getIndex(x));
final double displacement = -wavelengthOver4PI*phase;
targetData.setElemDoubleAt(trgIndex.getIndex(x), displacement);
}
}
} catch (Throwable e) {
OperatorUtils.catchOperatorException(getId(), e);
}
}
/**
* The SPI is used to register this operator in the graph processing framework
* via the SPI configuration file
* {@code META-INF/services/org.esa.snap.core.gpf.OperatorSpi}.
* This class may also serve as a factory for new operator instances.
*
* @see OperatorSpi#createOperator()
* @see OperatorSpi#createOperator(java.util.Map, java.util.Map)
*/
public static class Spi extends OperatorSpi {
public Spi() {
super(PhaseToDisplacementOp.class);
}
}
}