/*
* 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.calibration.gpf;
import com.bc.ceres.core.ProgressMonitor;
import org.esa.s1tbx.calibration.gpf.support.CalibrationFactory;
import org.esa.s1tbx.calibration.gpf.support.Calibrator;
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.Parameter;
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 java.util.ArrayList;
import java.util.List;
/**
* RemoveAntennaPattern for ASAR & ERS products.
*/
@OperatorMetadata(alias = "RemoveAntennaPattern",
category = "Radar/Radiometric",
authors = "Jun Lu, Luis Veci",
copyright = "Copyright (C) 2014 by Array Systems Computing Inc.",
version = "1.0",
description = "Remove Antenna Pattern")
public class RemoveAntennaPatternOp extends Operator {
@SourceProduct(alias = "source")
private Product sourceProduct;
@TargetProduct
private Product targetProduct;
@Parameter(description = "The list of source bands.", alias = "sourceBands",
rasterDataNodeType = Band.class, label = "Source Bands")
private String[] sourceBandNames = null;
private Calibrator calibrator = null;
/**
* 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.checkIfSARProduct();
validator.checkMission(new String[] {"ENVISAT", "ERS"});
getProductType();
getMultilookFlag();
createTargetProduct();
calibrator = CalibrationFactory.createCalibrator(sourceProduct);
calibrator.initialize(this, sourceProduct, targetProduct, true, false);
} catch (Throwable e) {
OperatorUtils.catchOperatorException(getId(), e);
}
}
/**
* Get product type.
*
* @throws OperatorException The exceptions.
*/
private void getProductType() throws OperatorException {
String productType = sourceProduct.getProductType();
if (productType.contains("ASA_IMS_1") && !productType.contains("ASA_APS_1")) {
throw new OperatorException(productType + " is not a valid ASAR product type for the operator.");
}
}
/**
* Get multilook flag from the abstracted metadata.
*
* @throws Exception The exceptions.
*/
private void getMultilookFlag() throws Exception {
final MetadataElement absRoot = AbstractMetadata.getAbstractedMetadata(sourceProduct);
if (AbstractMetadata.getAttributeBoolean(absRoot, AbstractMetadata.multilook_flag)) {
throw new OperatorException("Cannot apply the operator to multilooked product.");
}
}
/**
* Create target product.
*/
private void createTargetProduct() {
targetProduct = new Product(sourceProduct.getName(),
sourceProduct.getProductType(),
sourceProduct.getSceneRasterWidth(),
sourceProduct.getSceneRasterHeight());
addSelectedBands();
ProductUtils.copyProductNodes(sourceProduct, targetProduct);
final MetadataElement absTgt = AbstractMetadata.getAbstractedMetadata(targetProduct);
AbstractMetadata.setAttribute(absTgt, AbstractMetadata.ant_elev_corr_flag, 0);
AbstractMetadata.setAttribute(absTgt, AbstractMetadata.range_spread_comp_flag, 0);
absTgt.setAttributeInt("retro-calibration performed flag", 1);
}
/**
* Add the user selected bands to the target product.
*/
private void addSelectedBands() {
if (sourceBandNames == null || sourceBandNames.length == 0) {
final Band[] bands = sourceProduct.getBands();
final List<String> bandNameList = new ArrayList<>(sourceProduct.getNumBands());
for (Band band : bands) {
final String unit = band.getUnit();
if (unit != null && (unit.contains(Unit.PHASE) || unit.contains(Unit.REAL) || unit.contains(Unit.IMAGINARY))) {
continue;
}
bandNameList.add(band.getName());
}
sourceBandNames = bandNameList.toArray(new String[bandNameList.size()]);
}
final Band[] sourceBands = new Band[sourceBandNames.length];
for (int i = 0; i < sourceBandNames.length; i++) {
final String sourceBandName = sourceBandNames[i];
final Band sourceBand = sourceProduct.getBand(sourceBandName);
if (sourceBand == null) {
throw new OperatorException("Source band not found: " + sourceBandName);
}
sourceBands[i] = sourceBand;
}
for (Band srcBand : sourceBands) {
final String unit = srcBand.getUnit();
String srcBandName = srcBand.getName();
if (unit == null) {
throw new OperatorException("band " + srcBand.getName() + " requires a unit");
}
if (unit.contains(Unit.PHASE) || unit.contains(Unit.REAL) || unit.contains(Unit.IMAGINARY)) {
throw new OperatorException("Please select amplitude or intensity band.");
}
if (targetProduct.getBand(srcBandName) == null) {
final Band targetBand = new Band(srcBandName,
ProductData.TYPE_FLOAT32,
sourceProduct.getSceneRasterWidth(),
sourceProduct.getSceneRasterHeight());
targetBand.setUnit(unit);
targetProduct.addBand(targetBand);
}
}
}
/**
* Called by the framework in order to compute a tile for the given target band.
* <p>The default implementation throws a runtime exception with the message "not implemented".</p>
*
* @param targetBand The target band.
* @param targetTile The current tile associated with the target band to be computed.
* @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 raster.
*/
@Override
public void computeTile(Band targetBand, Tile targetTile, ProgressMonitor pm) throws OperatorException {
try {
final String srcBandName = targetBand.getName();
calibrator.removeFactorsForCurrentTile(targetBand, targetTile, srcBandName);
} 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(RemoveAntennaPatternOp.class);
}
}
}