/* * 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.insar.gpf; import com.bc.ceres.core.ProgressMonitor; import org.esa.snap.core.dataio.ProductIO; import org.esa.snap.core.dataio.ProductSubsetBuilder; import org.esa.snap.core.dataio.ProductSubsetDef; import org.esa.snap.core.dataio.ProductWriter; 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.datamodel.VirtualBand; 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.engine_utilities.datamodel.AbstractMetadata; import org.esa.snap.engine_utilities.gpf.InputProductValidator; import org.esa.snap.engine_utilities.gpf.StackUtils; import java.awt.*; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * Split a stack product into individual products */ @OperatorMetadata(alias = "Stack-Split", description = "Writes all bands to files.", authors = "Jun Lu, Luis Veci", version = "1.0", copyright = "Copyright (C) 2014 by Array Systems Computing Inc.", autoWriteDisabled = true, category = "Radar/Coregistration/Stack Tools") public class StackSplitWriter extends Operator { @TargetProduct private Product targetProduct; @SourceProduct(alias = "source", description = "The source product to be written.") private Product sourceProduct; @Parameter(defaultValue = "target", description = "The output folder to which the data product is written.") private File targetFolder; @Parameter(defaultValue = "BEAM-DIMAP", description = "The name of the output file format.") private String formatName; private final Map<Band, SubsetInfo> bandMap = new HashMap<>(); public StackSplitWriter() { setRequiresAllBands(true); } @Override public void initialize() throws OperatorException { try { final InputProductValidator validator = new InputProductValidator(sourceProduct); validator.checkIfCoregisteredStack(); if(targetFolder == null) { throw new OperatorException("Please add a target folder"); } if (!targetFolder.exists()) { if(!targetFolder.mkdirs()) { throw new IOException("Failed to create directory '" + targetFolder + "'."); } } final int width = sourceProduct.getSceneRasterWidth(); final int height = sourceProduct.getSceneRasterHeight(); targetProduct = sourceProduct; targetProduct.setPreferredTileSize(new Dimension(width, height)); final MetadataElement absRoot = AbstractMetadata.getAbstractedMetadata(sourceProduct); final String mstProductName = absRoot.getAttributeString(AbstractMetadata.PRODUCT, sourceProduct.getName()); final String[] mstNames = StackUtils.getMasterBandNames(sourceProduct); createSubset(mstProductName, getBandNames(mstNames)); final String[] slvProductNames = StackUtils.getSlaveProductNames(sourceProduct); for(String slvProductName : slvProductNames) { final String[] slvBandNames = StackUtils.getSlaveBandNames(sourceProduct, slvProductName); createSubset(slvProductName, getBandNames(slvBandNames)); } } catch (Throwable t) { throw new OperatorException(t); } } private String[] getBandNames(final String[] names) { final Set<String> bandNames = new HashSet<>(); for(String name : names) { final String suffix = StackUtils.getBandSuffix(name); for(String srcBandName : sourceProduct.getBandNames()) { if(srcBandName.endsWith(suffix)) { bandNames.add(srcBandName); } } } return bandNames.toArray(new String[bandNames.size()]); } private void createSubset(final String productName, final String[] bandNames) throws IOException { final int width = sourceProduct.getSceneRasterWidth(); final int height = sourceProduct.getSceneRasterHeight(); final ProductSubsetDef subsetDef = new ProductSubsetDef(); subsetDef.addNodeNames(sourceProduct.getTiePointGridNames()); subsetDef.addNodeNames(bandNames); subsetDef.setRegion(0, 0, width, height); subsetDef.setSubSampling(1, 1); subsetDef.setIgnoreMetadata(true); SubsetInfo subsetInfo = new SubsetInfo(); subsetInfo.subsetBuilder = new ProductSubsetBuilder(); subsetInfo.subsetProduct = subsetInfo.subsetBuilder.readProductNodes(sourceProduct, subsetDef); subsetInfo.file = new File(targetFolder, productName); // update band name for(Band trgBand : subsetInfo.subsetProduct.getBands()) { final String newBandName = StackUtils.getBandNameWithoutDate(trgBand.getName()); subsetInfo.newBandNamingMap.put(newBandName, trgBand.getName()); trgBand.setName(newBandName); // update virtual band expressions for(Band vBand : subsetInfo.subsetProduct.getBands()) { if(vBand instanceof VirtualBand) { final VirtualBand virtBand = (VirtualBand)vBand; String expression = virtBand.getExpression().replaceAll(trgBand.getName(), newBandName); virtBand.setExpression(expression); } } } subsetInfo.productWriter = ProductIO.getProductWriter(formatName); if (subsetInfo.productWriter == null) { throw new OperatorException("No data product writer for the '" + formatName + "' format available"); } subsetInfo.productWriter.setFormatName(formatName); subsetInfo.productWriter.setIncrementalMode(false); subsetInfo.subsetProduct.setProductWriter(subsetInfo.productWriter); bandMap.put(targetProduct.getBand(bandNames[0]), subsetInfo); } @Override public void computeTile(Band targetBand, Tile targetTile, ProgressMonitor pm) throws OperatorException { try { final SubsetInfo subsetInfo = bandMap.get(targetBand); if(subsetInfo == null) return; subsetInfo.productWriter.writeProductNodes(subsetInfo.subsetProduct, subsetInfo.file); final Rectangle trgRect = subsetInfo.subsetBuilder.getSubsetDef().getRegion(); if (!subsetInfo.written) { writeTile(subsetInfo, trgRect); } } catch (Exception e) { if (e instanceof OperatorException) { throw (OperatorException) e; } else { throw new OperatorException(e); } } } private synchronized void writeTile(final SubsetInfo info, final Rectangle trgRect) throws IOException { if (info.written) return; for(Band trgBand : info.subsetProduct.getBands()) { final String oldBandName = info.newBandNamingMap.get(trgBand.getName()); final Tile sourceTile = getSourceTile(sourceProduct.getBand(oldBandName), trgRect); final ProductData rawSamples = sourceTile.getRawSamples(); //final String newBandName = StackUtils.getBandNameWithoutDate(bandName); info.productWriter.writeBandRasterData(trgBand, 0, 0, trgBand.getRasterWidth(), trgBand.getRasterHeight(), rawSamples, ProgressMonitor.NULL); } info.written = true; } @Override public void dispose() { try { for (Band band : bandMap.keySet()) { SubsetInfo info = bandMap.get(band); info.productWriter.close(); } } catch (IOException ignore) { } super.dispose(); } private static class SubsetInfo { Product subsetProduct; ProductSubsetBuilder subsetBuilder; File file; ProductWriter productWriter; boolean written = false; final Map<String, String> newBandNamingMap = new HashMap<>(); } public static class Spi extends OperatorSpi { public Spi() { super(StackSplitWriter.class); } } }