/* * Copyright (C) 2012 Brockmann Consult GmbH (info@brockmann-consult.de) * * 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.utilities.gpf; import com.bc.ceres.core.ProgressMonitor; import org.esa.snap.core.datamodel.Band; import org.esa.snap.core.datamodel.Product; 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.SourceProducts; import org.esa.snap.core.gpf.annotations.TargetProduct; import org.esa.snap.core.util.ProductUtils; /** * The merge operator allows copying raster data from other products to a specified product. The first product provided * is considered the 'master product', into which the raster data coming from the other products is copied. Existing * nodes are kept. * <p> * It is mandatory that the products share the same scene, that is, their width and height need to match with those of * the master product as well as their geographic position. * * @author Olaf Danne * @author Norman Fomferra * @author Marco Peters * @author Ralf Quast * @author Marco Zuehlke * @author Thomas Storm */ @OperatorMetadata(alias = "BandMerge", category = "Raster", description = "Allows copying raster data from any number of source products to a specified 'master'" + " product.", authors = "BEAM team", version = "1.0", copyright = "(c) 2012 by Brockmann Consult", internal = false) public class BandMergeOp extends Operator { @SourceProducts(description = "The products to be merged into the master product.") private Product[] sourceProducts; @TargetProduct private Product targetProduct; @Parameter(description = "The list of source bands.", alias = "sourceBands", label = "Source Bands") private String[] sourceBandNames; @Parameter(defaultValue = "1.0E-5f", description = "Defines the maximum lat/lon error in degree between the products.") private float geographicError; @Override public void initialize() throws OperatorException { targetProduct = new Product(sourceProducts[0].getName(), sourceProducts[0].getProductType(), sourceProducts[0].getSceneRasterWidth(), sourceProducts[0].getSceneRasterHeight()); ProductUtils.copyProductNodes(sourceProducts[0], targetProduct); for (Product prod : sourceProducts) { for (Band band : prod.getBands()) { final Band sourceBand = targetProduct.getBand(band.getName()); String targetBandName = band.getName(); if (sourceBand != null) { int cnt = 2; targetBandName = band.getName() + "_"+cnt; while(targetProduct.containsRasterDataNode(targetBandName)) { ++cnt; targetBandName = band.getName() + "_"+cnt; } } ProductUtils.copyBand(band.getName(), prod, targetBandName, targetProduct, true); } } validateSourceProducts(); /* if (sourceBandNames == null || sourceBandNames.length == 0) { List<NodeDescriptor> nodeDescriptorList = new ArrayList<>(); for (final Product sourceProduct : sourceProducts) { if (sourceProduct != sourceProducts[0]) { for (String bandName : sourceProduct.getBandNames()) { final NodeDescriptor nodeDescriptor = new NodeDescriptor(); nodeDescriptor.name = bandName; nodeDescriptor.productId = getSourceProductId(sourceProduct); nodeDescriptorList.add(nodeDescriptor); } } } includes = nodeDescriptorList.toArray(new NodeDescriptor[nodeDescriptorList.size()]); } Set<Product> allSrcProducts = new HashSet<>(); for (NodeDescriptor nodeDescriptor : includes) { Product srcProduct = getSourceProduct(nodeDescriptor.productId); if (srcProduct == targetProduct) { continue; } if (StringUtils.isNotNullAndNotEmpty(nodeDescriptor.name)) { if (StringUtils.isNotNullAndNotEmpty(nodeDescriptor.newName)) { copyBandWithFeatures(srcProduct, nodeDescriptor.name, nodeDescriptor.newName); } else { copyBandWithFeatures(srcProduct, nodeDescriptor.name, nodeDescriptor.name); } allSrcProducts.add(srcProduct); } else if (StringUtils.isNotNullAndNotEmpty(nodeDescriptor.namePattern)) { Pattern pattern = Pattern.compile(nodeDescriptor.namePattern); for (String bandName : srcProduct.getBandNames()) { Matcher matcher = pattern.matcher(bandName); if (matcher.matches()) { copyBandWithFeatures(srcProduct, bandName, bandName); allSrcProducts.add(srcProduct); } } } }*/ for (Product srcProduct : sourceProducts) { if (srcProduct != sourceProducts[0]) { mergeAutoGrouping(srcProduct); ProductUtils.copyMasks(srcProduct, targetProduct); ProductUtils.copyOverlayMasks(srcProduct, targetProduct); } } } private void mergeAutoGrouping(Product srcProduct) { final Product.AutoGrouping srcAutoGrouping = srcProduct.getAutoGrouping(); if (srcAutoGrouping != null && !srcAutoGrouping.isEmpty()) { final Product.AutoGrouping targetAutoGrouping = targetProduct.getAutoGrouping(); if (targetAutoGrouping == null) { targetProduct.setAutoGrouping(srcAutoGrouping); } else { for (String[] grouping : srcAutoGrouping) { if (!targetAutoGrouping.contains(grouping)) { targetProduct.setAutoGrouping(targetAutoGrouping.toString() + ":" + srcAutoGrouping); } } } } } private void copyBandWithFeatures(Product sourceProduct, String oldBandName, String newBandName) { Band sourceBand = sourceProduct.getBand(oldBandName); if (sourceBand == null) { final String msg = String.format("Source product [%s] does not contain a band with name [%s]", sourceProduct.getName(), oldBandName); throw new OperatorException(msg); } if (targetProduct.containsBand(newBandName)) { return; } ProductUtils.copyBand(oldBandName, sourceProduct, newBandName, targetProduct, true); } private void validateSourceProducts() { for (Product sourceProduct : getSourceProducts()) { if (!targetProduct.isCompatibleProduct(sourceProduct, geographicError)) { throw new OperatorException(String.format("Product [%s] is not compatible to master product.", getSourceProductId(sourceProduct))); } } } @Override public void computeTile(Band band, Tile targetTile, ProgressMonitor pm) throws OperatorException { getLogger().warning("Wrongly configured operator. Tiles should not be requested."); } public static class Spi extends OperatorSpi { public Spi() { super(BandMergeOp.class); } } }