/* * Copyright (C) 2010 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.file.export; import com.bc.ceres.core.ProgressMonitor; import com.bc.ceres.swing.progress.ProgressMonitorSwingWorker; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageEncoder; import org.esa.snap.core.util.Debug; import org.esa.snap.core.util.ProductUtils; import org.esa.snap.core.util.SystemUtils; import org.esa.snap.core.util.geotiff.GeoTIFF; import org.esa.snap.core.util.geotiff.GeoTIFFMetadata; import org.esa.snap.core.util.io.FileUtils; import org.esa.snap.core.util.io.SnapFileFilter; import org.esa.snap.rcp.SnapApp; import org.esa.snap.rcp.util.Dialogs; import org.esa.snap.runtime.Config; import org.esa.snap.ui.SnapFileChooser; import org.esa.snap.ui.product.ProductSceneView; import org.openide.util.ContextAwareAction; import org.openide.util.HelpCtx; import org.openide.util.LookupListener; import javax.media.jai.operator.BandSelectDescriptor; import javax.swing.AbstractAction; import javax.swing.JFileChooser; import java.awt.Cursor; import java.awt.Dimension; import java.awt.image.RenderedImage; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; public abstract class AbstractExportImageAction extends AbstractAction implements LookupListener, ContextAwareAction, HelpCtx.Provider { protected static final String[] BMP_FORMAT_DESCRIPTION = {"BMP", "bmp", "BMP - Microsoft Windows Bitmap"}; protected static final String[] PNG_FORMAT_DESCRIPTION = {"PNG", "png", "PNG - Portable Network Graphics"}; protected static final String[] JPEG_FORMAT_DESCRIPTION = {"JPEG", "jpg,jpeg", "JPEG - Joint Photographic Experts Group"}; protected static final String[] GEOTIFF_FORMAT_DESCRIPTION = {"GeoTIFF", "tif,tiff", "GeoTIFF - TIFF with geo-location"}; protected static final String[] TIFF_FORMAT_DESCRIPTION = {"TIFF", "tif,tiff", "TIFF - Tagged Image File Format"}; // not yet used // private static final String[] JPEG2K_FORMAT_DESCRIPTION = {"JPEG2000", "jpg,jpeg", "JPEG 2000 - Joint Photographic Experts Group"}; private static final String[] TRANSPARENCY_IMAGE_FORMATS = new String[]{"TIFF", "PNG"}; private static final String IMAGE_EXPORT_DIR_PREFERENCES_KEY = "user.image.export.dir"; private String dialogTitle = null; private String helpId; public AbstractExportImageAction(String name, String helpId) { super(name); this.dialogTitle = name; this.helpId = helpId; Config.instance().load(); } @Override public HelpCtx getHelpCtx() { return new HelpCtx(helpId); } protected void exportImage(final SnapFileFilter[] filters) { final ProductSceneView view = SnapApp.getDefault().getSelectedProductSceneView(); if (view == null) { return; } final String lastDir = Config.instance().preferences().get(IMAGE_EXPORT_DIR_PREFERENCES_KEY, SystemUtils.getUserHomeDir().getPath()); final File currentDir = new File(lastDir); final SnapFileChooser fileChooser = new SnapFileChooser(); HelpCtx.setHelpIDString(fileChooser, getHelpCtx().getHelpID()); fileChooser.setCurrentDirectory(currentDir); for (int i = 0; i < filters.length; i++) { SnapFileFilter filter = filters[i]; Debug.trace("export image: supported format " + (i + 1) + ": " + filter.getFormatName()); fileChooser.addChoosableFileFilter(filter); // note: also selects current file filter! } fileChooser.setAcceptAllFileFilterUsed(false); String name = view.getProduct().getName(); final String imageBaseName = FileUtils.getFilenameWithoutExtension(name).replace('.', '_'); configureFileChooser(fileChooser, view, imageBaseName); fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); Dimension fileChooserSize = fileChooser.getPreferredSize(); if (fileChooserSize != null) { fileChooser.setPreferredSize(new Dimension(fileChooserSize.width + 120, fileChooserSize.height)); } else { fileChooser.setPreferredSize(new Dimension(512, 256)); } int result = fileChooser.showSaveDialog(SnapApp.getDefault().getMainFrame()); File file = fileChooser.getSelectedFile(); fileChooser.addPropertyChangeListener(evt -> { // @todo never comes here, why? Debug.trace(evt.toString()); }); final File currentDirectory = fileChooser.getCurrentDirectory(); if (currentDirectory != null) { Config.instance().preferences().put(IMAGE_EXPORT_DIR_PREFERENCES_KEY, currentDirectory.getPath()); } if (result != JFileChooser.APPROVE_OPTION) { return; } if (file == null || file.getName().equals("")) { return; } final boolean entireImageSelected = isEntireImageSelected(); final SnapFileFilter fileFilter = fileChooser.getSnapFileFilter(); String imageFormat = fileFilter != null ? fileFilter.getFormatName() : "TIFF"; if (imageFormat.equals(GEOTIFF_FORMAT_DESCRIPTION[0]) && !entireImageSelected) { final String msg = "GeoTIFF is not applicable to image clippings. Please select 'Full scene' option." + "\nShall TIFF format be used instead?"; Dialogs.Answer status = Dialogs.requestDecision(dialogTitle, msg, true, null); if (status == Dialogs.Answer.YES) { imageFormat = "TIFF"; } else { return; } } if (Boolean.TRUE.equals(Dialogs.requestOverwriteDecision(dialogTitle, file))) { exportImage(imageFormat, view, entireImageSelected, file); } } protected void exportImage(final String imageFormat, final ProductSceneView view, final boolean entireImageSelected, final File file) { final SaveImageSwingWorker worker = new SaveImageSwingWorker(SnapApp.getDefault(), "Save Image", imageFormat, view, entireImageSelected, file); worker.executeWithBlocking(); } protected abstract RenderedImage createImage(String imageFormat, ProductSceneView view); protected abstract boolean isEntireImageSelected(); protected abstract void configureFileChooser(SnapFileChooser fileChooser, ProductSceneView view, String imageBaseName); protected static boolean isTransparencySupportedByFormat(String formatName) { final String[] formats = TRANSPARENCY_IMAGE_FORMATS; for (final String format : formats) { if (format.equalsIgnoreCase(formatName)) { return true; } } return false; } protected static SnapFileFilter createFileFilter(String[] description) { final String formatName = description[0]; final String formatExt = description[1]; final String formatDescr = description[2]; return new SnapFileFilter(formatName, formatExt, formatDescr); } private class SaveImageSwingWorker extends ProgressMonitorSwingWorker { private final String imageFormat; private final ProductSceneView view; private final boolean entireImageSelected; private final File file; private final SnapApp snapApp; SaveImageSwingWorker(SnapApp snapApp, String message, String imageFormat, ProductSceneView view, boolean entireImageSelected, File file) { super(snapApp.getMainFrame(), message); this.snapApp = snapApp; this.imageFormat = imageFormat; this.view = view; this.entireImageSelected = entireImageSelected; this.file = file; } @Override protected Object doInBackground(ProgressMonitor pm) throws Exception { try { final String message = "Saving image as " + file.getPath() + "..."; pm.beginTask(message, 1); snapApp.setStatusBarMessage(message); snapApp.getMainFrame().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); RenderedImage image = createImage(imageFormat, view); boolean geoTIFFWritten = false; if (imageFormat.equals("GeoTIFF") && entireImageSelected) { final GeoTIFFMetadata metadata = ProductUtils.createGeoTIFFMetadata(view.getProduct()); if (metadata != null) { GeoTIFF.writeImage(image, file, metadata); geoTIFFWritten = true; } } if (!geoTIFFWritten) { if ("JPEG".equalsIgnoreCase(imageFormat)) { image = BandSelectDescriptor.create(image, new int[]{0, 1, 2}, null); } try (OutputStream stream = new FileOutputStream(file)) { ImageEncoder encoder = ImageCodec.createImageEncoder(imageFormat, stream, null); encoder.encode(image); } } } catch (OutOfMemoryError e) { Dialogs.showOutOfMemoryError("The image could not be exported."); } catch (Throwable e) { snapApp.handleError("The image exportation is not possible\n Please check the documentation.", e); //handleUnknownException(e); } finally { snapApp.getMainFrame().setCursor(Cursor.getDefaultCursor()); snapApp.setStatusBarMessage(""); pm.done(); } return null; } } }