/*
* 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.rcp;
import org.esa.s1tbx.calibration.gpf.Sentinel1RemoveThermalNoiseOp;
import org.esa.s1tbx.calibration.gpf.calibrators.Sentinel1Calibrator;
import org.esa.s1tbx.insar.gpf.support.Sentinel1Utils;
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.TiePointGrid;
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.rcp.SnapApp;
import org.esa.snap.rcp.actions.AbstractSnapAction;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionRegistration;
import org.openide.util.ContextAwareAction;
import org.openide.util.Lookup;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.openide.util.WeakListeners;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
/**
* S1CalibrationTPGAction action.
*/
@ActionID(category = "Raster", id = "S1CalibrationTPGAction")
@ActionRegistration(displayName = "#CTL_S1CalibrationTPGActionName")
@ActionReference(path = "Menu/Radar/Radiometric", position = 800)
@NbBundle.Messages({
"CTL_S1CalibrationTPGActionName=Create Calibration LUT TPG",
"CTL_S1CalibrationTPGActionDescription=Creates a tie point grid from calibration and noise vectors"
})
public class S1CalibrationTPGAction extends AbstractSnapAction implements ContextAwareAction, LookupListener {
private final Lookup lkp;
private final static String CALIBRATION_VECTOR_TPG = "CalibrationVector";
private final static String NOISE_VECTOR_TPG = "NoiseVector";
public S1CalibrationTPGAction() {
this(Utilities.actionsGlobalContext());
}
public S1CalibrationTPGAction(Lookup lkp) {
this.lkp = lkp;
Lookup.Result<Product> lkpContext = lkp.lookupResult(Product.class);
lkpContext.addLookupListener(WeakListeners.create(LookupListener.class, this, lkpContext));
setEnableState();
putValue(NAME, Bundle.CTL_S1CalibrationTPGActionName());
putValue(SHORT_DESCRIPTION, Bundle.CTL_S1CalibrationTPGActionDescription());
}
@Override
public Action createContextAwareInstance(Lookup actionContext) {
return new S1CalibrationTPGAction(actionContext);
}
@Override
public void resultChanged(LookupEvent ev) {
setEnableState();
}
public void setEnableState() {
final Product product = lkp.lookup(Product.class);
boolean enable = false;
if (product != null) {
final InputProductValidator validator = new InputProductValidator(product);
enable = validator.isSentinel1Product();
}
setEnabled(enable);
}
@Override
public void actionPerformed(ActionEvent event) {
final Product product = lkp.lookup(Product.class);
if (product != null) {
final S1CalibrationTPGDialog dlg = new S1CalibrationTPGDialog();
dlg.show();
if (dlg.IsOK()) {
try {
Sentinel1Utils su = new Sentinel1Utils(product);
int numSubswaths = su.getNumOfSubSwath();
List<String> selectedPolList = new ArrayList<>();
for (String pol : OperatorUtils.getPolarisations(product)) {
if (pol != null) {
selectedPolList.add(pol.toUpperCase());
}
}
boolean doSigma0 = dlg.doSigma0();
boolean doGamma = dlg.doGamma();
boolean doBeta0 = dlg.doBeta0();
boolean doDN = dlg.doDN();
boolean doNoise = dlg.doNoise();
if (doSigma0 || doGamma || doBeta0 || doDN) {
Sentinel1Calibrator.CalibrationInfo[] calInfo =
Sentinel1Calibrator.getCalibrationVectors(product, selectedPolList,
doSigma0, doBeta0, doGamma, doDN);
addCalibrationTiePointGrids(product, calInfo, numSubswaths,
doSigma0, doGamma, doBeta0, doDN);
}
if (doNoise) {
MetadataElement origMetadataRoot = AbstractMetadata.getOriginalProductMetadata(product);
Sentinel1RemoveThermalNoiseOp.ThermalNoiseInfo[] noiseList =
Sentinel1RemoveThermalNoiseOp.getThermalNoiseVectors(
origMetadataRoot, selectedPolList, numSubswaths);
addNoiseTiePointGrids(product, noiseList, numSubswaths);
}
} catch (Exception e) {
SnapApp.getDefault().handleError("Unable to add TPG", e);
}
}
}
}
private void addCalibrationTiePointGrids(final Product product, final Sentinel1Calibrator.CalibrationInfo[] calInfoList,
final int numSubswaths,
boolean doSigma0, boolean doGamma, boolean doBeta0, boolean doDN) {
for (Sentinel1Calibrator.CalibrationInfo calInfo : calInfoList) {
final Band referenceBand = findBand(product, numSubswaths, calInfo.subSwath, calInfo.polarization);
final int gridWidth = calInfo.calibrationVectorList[0].pixels.length;
final int gridHeight = calInfo.calibrationVectorList.length;
final int gridSize = gridWidth * gridHeight;
final String suffix = '_' + calInfo.subSwath + '_' + calInfo.polarization;
if (doSigma0) {
final String name = CALIBRATION_VECTOR_TPG + '_' + "Sigma0LUT" + suffix;
final float[] data = new float[gridSize];
int i = 0;
for (Sentinel1Utils.CalibrationVector calVec : calInfo.calibrationVectorList) {
for (float value : calVec.sigmaNought) {
data[i++] = value;
}
}
addTPG(product, gridWidth, gridHeight, referenceBand, name, data);
}
if (doGamma) {
final String name = CALIBRATION_VECTOR_TPG + '_' + "GammaLUT" + suffix;
final float[] data = new float[gridSize];
int i = 0;
for (Sentinel1Utils.CalibrationVector calVec : calInfo.calibrationVectorList) {
for (float value : calVec.gamma) {
data[i++] = value;
}
}
addTPG(product, gridWidth, gridHeight, referenceBand, name, data);
}
if (doBeta0) {
final String name = CALIBRATION_VECTOR_TPG + '_' + "Beta0LUT" + suffix;
final float[] data = new float[gridSize];
int i = 0;
for (Sentinel1Utils.CalibrationVector calVec : calInfo.calibrationVectorList) {
for (float value : calVec.betaNought) {
data[i++] = value;
}
}
addTPG(product, gridWidth, gridHeight, referenceBand, name, data);
}
if (doDN) {
final String name = CALIBRATION_VECTOR_TPG + '_' + "DNLUT" + suffix;
final float[] data = new float[gridSize];
int i = 0;
for (Sentinel1Utils.CalibrationVector calVec : calInfo.calibrationVectorList) {
for (float value : calVec.dn) {
data[i++] = value;
}
}
addTPG(product, gridWidth, gridHeight, referenceBand, name, data);
}
}
}
private void addTPG(final Product product, final int gridWidth, final int gridHeight,
final Band referenceBand,
final String name, final float[] data) {
final int sceneRasterWidth = referenceBand.getRasterWidth();
final int sceneRasterHeight = referenceBand.getRasterHeight();
final double subSamplingX = (double) sceneRasterWidth / (gridWidth - 1);
final double subSamplingY = (double) sceneRasterHeight / (gridHeight - 1);
if (product.getTiePointGrid(name) == null) {
final TiePointGrid tpg = new TiePointGrid(name,
gridWidth, gridHeight, 0.5f, 0.5f, subSamplingX, subSamplingY, data);
tpg.setUnit(Unit.INTENSITY);
product.addTiePointGrid(tpg);
}
}
private void addNoiseTiePointGrids(final Product product,
final Sentinel1RemoveThermalNoiseOp.ThermalNoiseInfo[] noiseList,
final int numSubswaths) {
for (Sentinel1RemoveThermalNoiseOp.ThermalNoiseInfo noiseInfo : noiseList) {
final Band referenceBand = findBand(product, numSubswaths, noiseInfo.subSwath, noiseInfo.polarization);
final int gridWidth = noiseInfo.noiseVectorList[0].pixels.length + 1;
final int gridHeight = noiseInfo.noiseVectorList.length;
final int gridSize = gridWidth * gridHeight;
final String suffix = '_' + noiseInfo.subSwath + '_' + noiseInfo.polarization;
final String name = NOISE_VECTOR_TPG + suffix;
final float[] data = new float[gridSize];
int i = 0;
for (Sentinel1Utils.NoiseVector noiseVec : noiseInfo.noiseVectorList) {
for (float value : noiseVec.noiseLUT) {
data[i++] = value;
}
}
addTPG(product, gridWidth, gridHeight, referenceBand, name, data);
}
}
private static Band findBand(final Product product, final int numSubswaths,
final String subSwath, final String polarization) {
Band matchingBand = product.getBandAt(0);
for (Band band : product.getBands()) {
String name = band.getName().toUpperCase();
if (name.contains(polarization)) {
if (numSubswaths == 1) {
return band;
} else if (name.contains(subSwath)) {
return band;
}
}
}
return matchingBand;
}
}