/* * Copyright (C) 2014 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.snap.rcp.actions.window; import com.bc.ceres.core.ProgressMonitor; import com.bc.ceres.core.SubProgressMonitor; import com.bc.ceres.swing.progress.ProgressMonitorSwingWorker; import org.esa.snap.core.datamodel.Band; import org.esa.snap.core.datamodel.Product; import org.esa.snap.core.datamodel.ProductNode; import org.esa.snap.core.datamodel.RGBImageProfile; import org.esa.snap.core.datamodel.RasterDataNode; import org.esa.snap.core.dataop.barithm.BandArithmetic; import org.esa.snap.core.jexp.ParseException; import org.esa.snap.core.util.ArrayUtils; import org.esa.snap.netbeans.docwin.DocumentWindowManager; import org.esa.snap.rcp.SnapApp; import org.esa.snap.rcp.util.Dialogs; import org.esa.snap.rcp.windows.ProductSceneViewTopComponent; import org.esa.snap.ui.RGBImageProfilePane; import org.esa.snap.ui.UIUtils; import org.esa.snap.ui.product.ProductSceneImage; import org.esa.snap.ui.product.ProductSceneView; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionReferences; import org.openide.awt.ActionRegistration; import org.openide.awt.UndoRedo; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.SwingWorker; import java.awt.Cursor; import java.awt.event.ActionEvent; /** * This action opens an RGB image view on the currently selected Product. * * @author Marco Peters */ @ActionID(category = "View", id = "OpenRGBImageViewAction") @ActionRegistration( displayName = "#CTL_OpenRGBImageViewAction_MenuText", popupText = "#CTL_OpenRGBImageViewAction_MenuText", iconBase = "org/esa/snap/rcp/icons/ImageView.gif", lazy = true ) @ActionReferences({ @ActionReference(path = "Menu/Window", position = 110), @ActionReference(path = "Context/Product/Product", position = 40, separatorBefore = 35), }) @NbBundle.Messages({ "CTL_OpenRGBImageViewAction_MenuText=Open RGB Image Window", "CTL_OpenRGBImageViewAction_ShortDescription=Open an RGB image view for the selected product" }) public class OpenRGBImageViewAction extends AbstractAction implements HelpCtx.Provider { private static final String HELP_ID = "rgbImageProfile"; private Product product; public OpenRGBImageViewAction(ProductNode node) { super(Bundle.CTL_OpenRGBImageViewAction_MenuText()); product = node.getProduct(); putValue(Action.SHORT_DESCRIPTION, Bundle.CTL_OpenRGBImageViewAction_ShortDescription()); } @Override public void actionPerformed(ActionEvent e) { if (product != null) { openProductSceneViewRGB(product, HELP_ID); } } @Override public HelpCtx getHelpCtx() { return new HelpCtx(HELP_ID); } public void openProductSceneViewRGB(Product rgbProduct, final String helpId) { final Product[] openedProducts = SnapApp.getDefault().getProductManager().getProducts(); final int[] defaultBandIndices = getDefaultBandIndices(rgbProduct); final RGBImageProfilePane profilePane = new RGBImageProfilePane(SnapApp.getDefault().getPreferencesPropertyMap(), rgbProduct, openedProducts, defaultBandIndices); final String title = "Select RGB-Image Channels"; final boolean ok = profilePane.showDialog(SnapApp.getDefault().getMainFrame(), title, helpId); if (!ok) { return; } final String[] rgbaExpressions = profilePane.getRgbaExpressions(); if (profilePane.getStoreProfileInProduct()) { RGBImageProfile.storeRgbaExpressions(rgbProduct, rgbaExpressions); } final String sceneName = createSceneName(rgbProduct, profilePane.getSelectedProfile(), "RGB"); openProductSceneViewRGB(sceneName, rgbProduct, rgbaExpressions); } public static int[] getDefaultBandIndices(final Product product) { int[] bandIndices = null; final Band[] bands = product.getBands(); if (bands.length == 1) { return new int[]{0}; } else if (bands.length == 2) { return new int[]{0, 1}; } else if (bands.length > 2) { bandIndices = new int[3]; int cnt = 0, i = 0; for (Band band : product.getBands()) { final String unit = band.getUnit(); if (unit != null && unit.contains("intensity")) { bandIndices[i++] = cnt; } if (i >= bandIndices.length) break; ++cnt; } if (i == 0) { while (i < 3) { bandIndices[i] = i++; } } if (i == 1) { return new int[]{bandIndices[0]}; } else if (i == 2) { return new int[]{bandIndices[0], bandIndices[1]}; } } return bandIndices; } private void openProductSceneViewRGB(final String name, final Product product, final String[] rgbaExpressions) { final SwingWorker<ProductSceneImage, Object> worker = new ProgressMonitorSwingWorker<ProductSceneImage, Object>( SnapApp.getDefault().getMainFrame(), SnapApp.getDefault().getInstanceName() + " - Creating image for '" + name + "'") { @Override protected ProductSceneImage doInBackground(ProgressMonitor pm) throws Exception { return createProductSceneImageRGB(name, product, rgbaExpressions, pm); } @Override protected void done() { SnapApp.getDefault().getMainFrame().setCursor(Cursor.getDefaultCursor()); String errorMsg = "The RGB image view could not be created."; try { ProductSceneView productSceneView = new ProductSceneView(get()); openDocumentWindow(productSceneView); } catch (OutOfMemoryError e) { Dialogs.showOutOfMemoryError(errorMsg); return; } catch (Exception e) { SnapApp.getDefault().handleError(errorMsg, e); return; } SnapApp.getDefault().setStatusBarMessage(""); } }; SnapApp.getDefault().setStatusBarMessage("Creating RGB image view..."); /*I18N*/ UIUtils.setRootFrameWaitCursor(SnapApp.getDefault().getMainFrame()); worker.execute(); } public static ProductSceneViewTopComponent openDocumentWindow(final ProductSceneView view) { UndoRedo.Manager undoManager = SnapApp.getDefault().getUndoManager(view.getProduct()); ProductSceneViewTopComponent psvTopComponent = new ProductSceneViewTopComponent(view, undoManager); DocumentWindowManager.getDefault().openWindow(psvTopComponent); psvTopComponent.requestSelected(); return psvTopComponent; } private ProductSceneImage createProductSceneImageRGB(String name, final Product product, String[] rgbaExpressions, ProgressMonitor pm) throws Exception { Band[] rgbBands = null; boolean errorOccurred = false; ProductSceneImage productSceneImage = null; try { pm.beginTask("Creating RGB image...", 2); rgbBands = allocateRgbBands(product, rgbaExpressions); productSceneImage = new ProductSceneImage(name, rgbBands[0], rgbBands[1], rgbBands[2], SnapApp.getDefault().getPreferencesPropertyMap(), SubProgressMonitor.create(pm, 1)); productSceneImage.initVectorDataCollectionLayer(); productSceneImage.initMaskCollectionLayer(); } catch (Exception e) { errorOccurred = true; throw e; } finally { pm.done(); if (rgbBands != null) { releaseRgbBands(rgbBands, errorOccurred); } } return productSceneImage; } public static Band[] allocateRgbBands(final Product product, final String[] rgbaExpressions) { final Band[] rgbBands = new Band[3]; // todo - set to [4] as soon as we support alpha final boolean productModificationState = product.isModified(); final Product[] products = SnapApp.getDefault().getProductManager().getProducts(); final int elementIndex = ArrayUtils.getElementIndex(product, products); try { if (!BandArithmetic.areRastersEqualInSize(product, rgbaExpressions)) { throw new IllegalArgumentException("Referenced rasters are not of the same size"); } } catch (ParseException e) { throw new IllegalArgumentException("Expressions are invalid"); } for (int i = 0; i < rgbBands.length; i++) { String expression = rgbaExpressions[i].isEmpty() ? "0" : rgbaExpressions[i]; Band rgbBand = product.getBand(expression); if (rgbBand == null) { rgbBand = new ProductSceneView.RGBChannel(product, determineWidth(expression, products, elementIndex), determineHeight(expression, products, elementIndex), RGBImageProfile.RGB_BAND_NAMES[i], expression); } rgbBands[i] = rgbBand; } product.setModified(productModificationState); return rgbBands; } private static int determineWidth(String expression, Product[] products, int index) { int width = products[index].getSceneRasterWidth(); try { final RasterDataNode[] refRasters = BandArithmetic.getRefRasters(expression, products, index); if (refRasters.length > 0) { width = refRasters[0].getRasterWidth(); } } catch (ParseException e) { throw new IllegalArgumentException("Invalid expression: " + expression); } return width; } private static int determineHeight(String expression, Product[] products, int index) { int height = products[index].getSceneRasterHeight(); try { final RasterDataNode[] refRasters = BandArithmetic.getRefRasters(expression, products, index); if (refRasters.length > 0) { height = refRasters[0].getRasterHeight(); } } catch (ParseException e) { throw new IllegalArgumentException("Invalid expression: " + expression); } return height; } public static void releaseRgbBands(Band[] rgbBands, boolean errorOccurred) { for (int i = 0; i < rgbBands.length; i++) { Band rgbBand = rgbBands[i]; if (rgbBand != null) { if (rgbBand instanceof ProductSceneView.RGBChannel) { if (errorOccurred) { rgbBand.dispose(); } } } rgbBands[i] = null; } } public static String createSceneName(Product product, RGBImageProfile rgbImageProfile, String operation) { final StringBuilder nameBuilder = new StringBuilder(); final String productRef = product.getProductRefString(); if (productRef != null) { nameBuilder.append(productRef); nameBuilder.append(" "); } if (rgbImageProfile != null) { nameBuilder.append(rgbImageProfile.getName().replace("_", " ")); nameBuilder.append(" "); } nameBuilder.append(operation); return nameBuilder.toString(); } }