/*
* Copyright (C) 2015 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;
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.gpf.OperatorException;
import org.esa.snap.engine_utilities.datamodel.AbstractMetadata;
import org.esa.snap.engine_utilities.datamodel.Unit;
import org.esa.snap.engine_utilities.gpf.OperatorUtils;
import org.esa.snap.engine_utilities.gpf.StackUtils;
import java.util.ArrayList;
import java.util.List;
/**
* Helper functions for handling polarimetric bands
*/
public class PolBandUtils {
public static enum MATRIX {DUAL_HH_HV, DUAL_VH_VV, DUAL_HH_VV, C2, LCHCP, RCHCP, C3, T3, C4, T4, FULL, UNKNOWN}
public static class PolSourceBand {
public final String productName;
public final Band[] srcBands;
public final String suffix;
public Band[] targetBands;
public double spanMin = 1e+30;
public double spanMax = -1e+30;
public boolean spanMinMaxSet = false;
public PolSourceBand(final String productName, final Band[] bands, final String suffix) {
this.productName = productName;
this.srcBands = bands;
this.suffix = suffix;
}
public void addTargetBands(final Band[] targetBands) {
this.targetBands = targetBands;
}
}
/**
* Check input product format, get source product type.
*
* @param sourceProduct The source product
* @return The product type
*/
public static MATRIX getSourceProductType(final Product sourceProduct) {
final String[] bandNames = sourceProduct.getBandNames();
boolean isC3 = false, isT3 = false, isC2 = false, isLCHS2 = false, isRCHS2 = false;
boolean isHH = false, isHV = false, isVV = false, isVH = false;
for (String name : bandNames) {
if (name.contains("C44")) {
return MATRIX.C4;
} else if (name.contains("T44")) {
return MATRIX.T4;
} else if (name.contains("C33")) {
isC3 = true;
} else if (name.contains("T33")) {
isT3 = true;
} else if (name.contains("C22")) {
isC2 = true;
} else if (name.contains("LH")) {
isLCHS2 = true;
} else if (name.contains("RH")) {
isRCHS2 = true;
} else if (name.contains("_HH")) {
isHH = true;
} else if (name.contains("_HV")) {
isHV = true;
} else if (name.contains("_VV")) {
isVV = true;
} else if (name.contains("_VH")) {
isVH = true;
}
}
if (isC3)
return MATRIX.C3;
else if (isT3)
return MATRIX.T3;
else if (isC2)
return MATRIX.C2;
else if (isLCHS2)
return MATRIX.LCHCP;
else if (isRCHS2)
return MATRIX.RCHCP;
else if (isHH && isHV && !isVH && !isVV)
return MATRIX.DUAL_HH_HV;
else if (!isHH && !isHV && isVH && isVV)
return MATRIX.DUAL_VH_VV;
else if (isHH && !isHV && !isVH && isVV)
return MATRIX.DUAL_HH_VV;
else if (isHH && isHV && isVH && isVV)
return MATRIX.FULL;
return MATRIX.UNKNOWN;
}
/**
* Check input product format, get source bands and set corresponding flag.
*
* @param srcProduct the input product
* @param sourceProductType The source product type
* @return QuadSourceBand[]
* @throws OperatorException if sourceProduct is not quad-pol
*/
public static PolSourceBand[] getSourceBands(final Product srcProduct,
final MATRIX sourceProductType) throws Exception {
final boolean isCoregistered = StackUtils.isCoregisteredStack(srcProduct);
final List<PolSourceBand> quadSrcBandList = new ArrayList<>(10);
if (isCoregistered) {
final String[] mstBandNames = StackUtils.getMasterBandNames(srcProduct);
final Band[] mstBands = getBands(srcProduct, sourceProductType, mstBandNames);
final String suffix = mstBandNames[0].substring(mstBandNames[0].lastIndexOf('_'), mstBandNames[0].length());
quadSrcBandList.add(new PolSourceBand(srcProduct.getName(), mstBands, suffix));
final String[] slvProductNames = StackUtils.getSlaveProductNames(srcProduct);
for (String slvProd : slvProductNames) {
final String[] slvBandNames = StackUtils.getSlaveBandNames(srcProduct, slvProd);
final Band[] slvBands = getBands(srcProduct, sourceProductType, slvBandNames);
final String suf = slvBandNames[0].substring(slvBandNames[0].lastIndexOf('_'), slvBandNames[0].length());
quadSrcBandList.add(new PolSourceBand(slvProd, slvBands, suf));
}
} else {
final String[] bandNames = srcProduct.getBandNames();
final Band[] mstBands = getBands(srcProduct, sourceProductType, bandNames);
quadSrcBandList.add(new PolSourceBand(srcProduct.getName(), mstBands, ""));
}
return quadSrcBandList.toArray(new PolSourceBand[quadSrcBandList.size()]);
}
/**
* Check input product format, get source bands and set corresponding flag.
*
* @param srcProduct The source product
* @param sourceProductType The source product type
* @param bandNames the src band names
* @return QuadSourceBand[]
*/
private static Band[] getBands(final Product srcProduct, final MATRIX sourceProductType, final String[] bandNames) throws Exception {
if (sourceProductType == MATRIX.DUAL_HH_HV) { // dual pol HH HV
return getDualPolSrcBands(srcProduct, getComplexBandNames());
} else if (sourceProductType == MATRIX.DUAL_VH_VV) { // dual VH VV
return getDualPolSrcBands(srcProduct, getComplexBandNames());
} else if (sourceProductType == MATRIX.DUAL_HH_VV) { // dual HH VV
return getDualPolSrcBands(srcProduct, getComplexBandNames());
}else if (sourceProductType == MATRIX.FULL) { // full pol
return getQuadPolSrcBands(srcProduct, bandNames);
} else if (sourceProductType == MATRIX.C3) { // C3
return getProductBands(srcProduct, bandNames, getC3BandNames());
} else if (sourceProductType == MATRIX.T3) { // T3
return getProductBands(srcProduct, bandNames, getT3BandNames());
} else if (sourceProductType == MATRIX.C4) {
return getProductBands(srcProduct, bandNames, getC4BandNames());
} else if (sourceProductType == MATRIX.T4) {
return getProductBands(srcProduct, bandNames, getT4BandNames());
} else if (sourceProductType == MATRIX.C2) { // compact pol C2
return getProductBands(srcProduct, bandNames, getC2BandNames());
} else if (sourceProductType == MATRIX.LCHCP) { // LCH compact pol S2
return getProductBands(srcProduct, bandNames, getLCHModeS2BandNames());
} else if (sourceProductType == MATRIX.RCHCP) { // RCH compact pol S2
return getProductBands(srcProduct, bandNames, getRCHModeS2BandNames());
}
return null;
}
private static Band[] getDualPolSrcBands(final Product srcProduct, final String[] srcBandNames) {
final List<Band> bandList = new ArrayList<>();
for(Band srcBand : srcProduct.getBands()) {
final String bandName = srcBand.getName();
for (String s : srcBandNames) {
if(bandName.startsWith(s)) {
bandList.add(srcBand);
}
}
}
return bandList.toArray(new Band[bandList.size()]);
}
private static Band[] getQuadPolSrcBands(final Product srcProduct, final String[] srcBandNames)
throws Exception {
final MetadataElement absRoot = AbstractMetadata.getAbstractedMetadata(srcProduct);
final boolean isComplex = absRoot.getAttributeString(AbstractMetadata.SAMPLE_TYPE).equals("COMPLEX");
if(!isComplex) {
final List<Band> bandList = new ArrayList<>();
for (final String srcBandName : srcBandNames) {
final Band band = srcProduct.getBand(srcBandName);
final String bandUnit = band.getUnit();
if (bandUnit == null || !bandUnit.contains(Unit.INTENSITY))
continue;
final String pol = OperatorUtils.getBandPolarization(band.getName(), absRoot);
if (pol.contains("hh") || pol.contains("hv") || pol.contains("vh") || pol.contains("vv")) {
bandList.add(band);
}
}
if(bandList.size() < 4) {
throw new OperatorException("A full polarization product is expected as input.");
}
return bandList.toArray(new Band[bandList.size()]);
}
int validBandCnt = 0;
final Band[] sourceBands = new Band[8];
for (final String srcBandName : srcBandNames) {
final Band band = srcProduct.getBand(srcBandName);
final Unit.UnitType bandUnit = Unit.getUnitType(band);
if (!(bandUnit == Unit.UnitType.REAL || bandUnit == Unit.UnitType.IMAGINARY))
continue;
final String pol = OperatorUtils.getBandPolarization(band.getName(), absRoot);
if (pol.contains("hh")) {
if (bandUnit.equals(Unit.UnitType.REAL)) {
sourceBands[0] = band;
++validBandCnt;
} else if (bandUnit.equals(Unit.UnitType.IMAGINARY)) {
sourceBands[1] = band;
++validBandCnt;
}
} else if (pol.contains("hv")) {
if (bandUnit.equals(Unit.UnitType.REAL)) {
sourceBands[2] = band;
++validBandCnt;
} else if (bandUnit.equals(Unit.UnitType.IMAGINARY)) {
sourceBands[3] = band;
++validBandCnt;
}
} else if (pol.contains("vh")) {
if (bandUnit.equals(Unit.UnitType.REAL)) {
sourceBands[4] = band;
++validBandCnt;
} else if (bandUnit.equals(Unit.UnitType.IMAGINARY)) {
sourceBands[5] = band;
++validBandCnt;
}
} else if (pol.contains("vv")) {
if (bandUnit.equals(Unit.UnitType.REAL)) {
sourceBands[6] = band;
++validBandCnt;
} else if (bandUnit.equals(Unit.UnitType.IMAGINARY)) {
sourceBands[7] = band;
++validBandCnt;
}
}
}
if (validBandCnt != 8) {
throw new OperatorException("A full polarization product is expected as input.");
}
return sourceBands;
}
private static Band[] getProductBands(final Product srcProduct, final String[] srcBandNames,
final String[] validBandNames) throws OperatorException {
final Band[] sourceBands = new Band[validBandNames.length];
int validBandCnt = 0;
for (final String bandName : srcBandNames) {
final Band band = srcProduct.getBand(bandName);
if (band == null) {
throw new OperatorException("Band " + bandName + " not found");
}
for (final String validName : validBandNames) {
if (bandName.contains(validName)) {
sourceBands[validBandCnt++] = band;
break;
}
}
}
if (validBandCnt != validBandNames.length) {
throw new OperatorException("Input is not a valid polarimetric matrix");
}
return sourceBands;
}
public static void saveNewBandNames(final Product targetProduct, final PolSourceBand[] srcBandList) {
if (StackUtils.isCoregisteredStack(targetProduct)) {
boolean masterProduct = true;
for (final PolSourceBand bandList : srcBandList) {
if (masterProduct) {
final String[] bandNames = StackUtils.bandsToStringArray(bandList.targetBands);
StackUtils.saveMasterProductBandNames(targetProduct, bandNames);
masterProduct = false;
} else {
final String[] bandNames = StackUtils.bandsToStringArray(bandList.targetBands);
StackUtils.saveSlaveProductBandNames(targetProduct, bandList.productName, bandNames);
}
}
}
}
public static boolean isDualPol(final MATRIX m) {
return m == MATRIX.DUAL_HH_HV || m == MATRIX.DUAL_VH_VV || m == MATRIX.DUAL_HH_VV ||
m == MATRIX.C2 || m == MATRIX.LCHCP || m == MATRIX.RCHCP;
}
public static boolean isQuadPol(final MATRIX m) {
return m == MATRIX.C3 || m == MATRIX.T3 || m == MATRIX.C4 || m == MATRIX.T4;
}
public static boolean isFullPol(final MATRIX m) {
return m == MATRIX.FULL;
}
public static String[] getComplexBandNames() {
return new String[]{
"i_",
"q_"
};
}
/**
* Get band names for compact pol Stokes vector product.
*
* @return The source band names.
*/
public static String[] getG4BandNames() {
return new String[]{
"g0",
"g1",
"g2",
"g3",
};
}
/**
* Get band names for Right Circular Hybrid mode compact pol scattering vector product.
*
* @return The source band names.
*/
public static String[] getLCHModeS2BandNames() {
return new String[]{
"i_LH",
"q_LH",
"i_LV",
"q_LV",
};
}
/**
* Get band names for Left Circular Hybrid mode compact pol scattering vector product.
*
* @return The source band names.
*/
public static String[] getRCHModeS2BandNames() {
return new String[]{
"i_RH",
"q_RH",
"i_RV",
"q_RV",
};
}
/**
* Get compact pol covariance matrix product source band names.
*
* @return The source band names.
*/
public static String[] getC2BandNames() {
return new String[]{
"C11",
"C12_real",
"C12_imag",
"C22",
};
}
public static String[] getC3BandNames() {
return new String[]{
"C11",
"C12_real",
"C12_imag",
"C13_real",
"C13_imag",
"C22",
"C23_real",
"C23_imag",
"C33"
};
}
public static String[] getC4BandNames() {
return new String[]{
"C11",
"C12_real",
"C12_imag",
"C13_real",
"C13_imag",
"C14_real",
"C14_imag",
"C22",
"C23_real",
"C23_imag",
"C24_real",
"C24_imag",
"C33",
"C34_real",
"C34_imag",
"C44"
};
}
public static String[] getT3BandNames() {
return new String[]{
"T11",
"T12_real",
"T12_imag",
"T13_real",
"T13_imag",
"T22",
"T23_real",
"T23_imag",
"T33"
};
}
public static String[] getT4BandNames() {
return new String[]{
"T11",
"T12_real",
"T12_imag",
"T13_real",
"T13_imag",
"T14_real",
"T14_imag",
"T22",
"T23_real",
"T23_imag",
"T24_real",
"T24_imag",
"T33",
"T34_real",
"T34_imag",
"T44"
};
}
public static String getPolarType(final Product product) throws Exception {
final MetadataElement absRoot = AbstractMetadata.getAbstractedMetadata(product);
if (absRoot != null) {
if (!AbstractMetadata.isNoData(absRoot, AbstractMetadata.mds1_tx_rx_polar) &&
!AbstractMetadata.isNoData(absRoot, AbstractMetadata.mds2_tx_rx_polar)) {
if (!AbstractMetadata.isNoData(absRoot, AbstractMetadata.mds3_tx_rx_polar) &&
!AbstractMetadata.isNoData(absRoot, AbstractMetadata.mds4_tx_rx_polar)) {
return "full";
}
return "dual";
}
}
return "single";
}
public static boolean isBandForMatrixElement(final String bandName, final String elemPrefix) {
return bandName.length() > elemPrefix.length() &&
bandName.substring(1, elemPrefix.length()+1).equals(elemPrefix);
}
}