/*
* 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.layermanager.layersrc.image;
import com.bc.ceres.glayer.tools.Tools;
import org.esa.snap.core.util.io.FileUtils;
import org.esa.snap.rcp.SnapApp;
import org.esa.snap.rcp.layermanager.layersrc.FilePathListCellRenderer;
import org.esa.snap.rcp.layermanager.layersrc.HistoryComboBoxModel;
import org.esa.snap.ui.FileHistory;
import org.esa.snap.ui.layer.AbstractLayerSourceAssistantPage;
import org.esa.snap.ui.layer.LayerSourcePageContext;
import javax.media.jai.Interpolation;
import javax.media.jai.PlanarImage;
import javax.media.jai.operator.FileLoadDescriptor;
import javax.media.jai.operator.ScaleDescriptor;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingWorker;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.prefs.Preferences;
// todo - Check, if image is GeoTIFF -> no world file is needed
class ImageFileAssistantPage1 extends AbstractLayerSourceAssistantPage {
private static final String LAST_IMAGE_PREFIX = "ImageFileAssistantPage1.ImageFile.history";
private static final String LAST_DIR = "ImageFileAssistantPage1.ImageFile.lastDir";
private JComboBox imageFileBox;
private JTextField worldFileField;
private HistoryComboBoxModel imageHistoryModel;
private JLabel imagePreviewLabel;
ImageFileAssistantPage1() {
super("Select Image File");
}
@Override
public boolean validatePage() {
String imageFilePath = (String) getContext().getPropertyValue(ImageFileLayerSource.PROPERTY_NAME_IMAGE_FILE_PATH);
String worldFilePath = (String) getContext().getPropertyValue(ImageFileLayerSource.PROPERTY_NAME_WORLD_FILE_PATH);
if (imageFilePath == null) {
return false;
}
return new File(imageFilePath).exists() && (worldFilePath == null || new File(worldFilePath).exists());
}
@Override
public boolean hasNextPage() {
return getContext().getPropertyValue(ImageFileLayerSource.PROPERTY_NAME_IMAGE_FILE_PATH) != null;
}
@Override
public AbstractLayerSourceAssistantPage getNextPage() {
imageHistoryModel.getHistory().copyInto(SnapApp.getDefault().getPreferences());
createTransform(getContext());
return new ImageFileAssistantPage2();
}
@Override
public boolean canFinish() {
return getContext().getPropertyValue(ImageFileLayerSource.PROPERTY_NAME_IMAGE_FILE_PATH) != null;
}
@Override
public boolean performFinish() {
imageHistoryModel.getHistory().copyInto(SnapApp.getDefault().getPreferences());
createTransform(getContext());
return ImageFileLayerSource.insertImageLayer(getContext());
}
@Override
public Component createPageComponent() {
GridBagConstraints gbc = new GridBagConstraints();
final JPanel panel = new JPanel(new GridBagLayout());
gbc.anchor = GridBagConstraints.WEST;
gbc.gridy = 0;
final Preferences preferences = SnapApp.getDefault().getPreferences();
FileHistory fileHistory = new FileHistory(5, LAST_IMAGE_PREFIX);
fileHistory.initBy(preferences);
imageHistoryModel = new HistoryComboBoxModel(fileHistory);
imageFileBox = new JComboBox(imageHistoryModel);
imageFileBox.addActionListener(new ImageFileItemListener());
imageFileBox.setRenderer(new FilePathListCellRenderer(80));
final JLabel imageFileLabel = new JLabel("Path to image file (.png, .jpg, .tif, .gif):");
JButton imageFileButton = new JButton("...");
final FileNameExtensionFilter imageFileFilter = new FileNameExtensionFilter("Image Files",
"png", "jpg", "tif", "gif");
imageFileButton.addActionListener(new FileChooserActionListener(imageFileFilter) {
@Override
protected void onFileSelected(LayerSourcePageContext pageContext, String filePath) {
pageContext.setPropertyValue(ImageFileLayerSource.PROPERTY_NAME_IMAGE_FILE_PATH, filePath);
}
});
addRow(panel, gbc, imageFileLabel, imageFileBox, imageFileButton);
worldFileField = new JTextField();
worldFileField.getDocument().addDocumentListener(new WorldFilePathDocumentListener());
final JLabel worldFileLabel = new JLabel("Path to world file (.pgw, .jgw, .tfw, .gfw):");
JButton worldFileButton = new JButton("...");
final FileNameExtensionFilter worldFileFilter = new FileNameExtensionFilter("World Files",
"pgw", "jgw", "tfw", "gfw");
worldFileButton.addActionListener(new FileChooserActionListener(worldFileFilter) {
@Override
protected void onFileSelected(LayerSourcePageContext pageContext, String filePath) {
pageContext.setPropertyValue(ImageFileLayerSource.PROPERTY_NAME_WORLD_FILE_PATH, filePath);
}
});
addRow(panel, gbc, worldFileLabel, worldFileField, worldFileButton);
gbc.insets = new Insets(4, 4, 4, 4);
gbc.weightx = 1;
gbc.weighty = 1;
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.gridx = 0;
gbc.gridy++;
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = 1;
imagePreviewLabel = new JLabel();
imagePreviewLabel.setPreferredSize(new Dimension(200, 200));
panel.add(imagePreviewLabel, gbc);
getContext().setPropertyValue(ImageFileLayerSource.PROPERTY_NAME_IMAGE_FILE_PATH, null);
getContext().setPropertyValue(ImageFileLayerSource.PROPERTY_NAME_WORLD_FILE_PATH, null);
// getContext().setPropertyValue(ImageFileLayerSource.PROPERTY_NAME_IMAGE, null);
getContext().setPropertyValue(ImageFileLayerSource.PROPERTY_NAME_WORLD_TRANSFORM, null);
return panel;
}
private void addRow(JPanel panel, GridBagConstraints gbc, JLabel label, JComponent component, JButton button) {
gbc.insets = new Insets(4, 4, 4, 4);
gbc.gridx = 0;
gbc.gridy++;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = 1;
panel.add(label, gbc);
gbc.insets = new Insets(0, 4, 0, 4);
gbc.weightx = 0;
gbc.weighty = 0;
gbc.gridx = 0;
gbc.gridy++;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = 1;
panel.add(component, gbc);
gbc.weightx = 0;
gbc.weighty = 0;
gbc.gridx = 1;
gbc.fill = GridBagConstraints.NONE;
gbc.gridwidth = 1;
panel.add(button, gbc);
}
private static void createTransform(LayerSourcePageContext pageContext) {
AffineTransform transform = new AffineTransform();
String worldFilePath = (String) pageContext.getPropertyValue(ImageFileLayerSource.PROPERTY_NAME_WORLD_FILE_PATH);
if (worldFilePath != null && !worldFilePath.isEmpty()) {
try {
transform = Tools.loadWorldFile(worldFilePath);
} catch (IOException e) {
e.printStackTrace();
pageContext.showErrorDialog(e.getMessage());
}
}
pageContext.setPropertyValue(ImageFileLayerSource.PROPERTY_NAME_WORLD_TRANSFORM, transform);
}
private class WorldFilePathDocumentListener implements DocumentListener {
@Override
public void insertUpdate(DocumentEvent e) {
getContext().setPropertyValue(ImageFileLayerSource.PROPERTY_NAME_WORLD_FILE_PATH, worldFileField.getText());
getContext().updateState();
}
@Override
public void removeUpdate(DocumentEvent e) {
getContext().setPropertyValue(ImageFileLayerSource.PROPERTY_NAME_WORLD_FILE_PATH, worldFileField.getText());
getContext().updateState();
}
@Override
public void changedUpdate(DocumentEvent e) {
getContext().setPropertyValue(ImageFileLayerSource.PROPERTY_NAME_WORLD_FILE_PATH, worldFileField.getText());
getContext().updateState();
}
}
private abstract class FileChooserActionListener implements ActionListener {
private final FileFilter filter;
private FileChooserActionListener(FileFilter fileFilter) {
filter = fileFilter;
}
@Override
public void actionPerformed(ActionEvent e) {
JFileChooser fileChooser = new JFileChooser();
fileChooser.addChoosableFileFilter(filter);
fileChooser.setCurrentDirectory(getLastDirectory());
LayerSourcePageContext pageContext = getContext();
fileChooser.showOpenDialog(pageContext.getWindow());
if (fileChooser.getSelectedFile() != null) {
String filePath = fileChooser.getSelectedFile().getPath();
imageHistoryModel.setSelectedItem(filePath);
Preferences preferences = SnapApp.getDefault().getPreferences();
preferences.put(LAST_DIR, fileChooser.getCurrentDirectory().getAbsolutePath());
onFileSelected(pageContext, filePath);
pageContext.updateState();
}
}
protected abstract void onFileSelected(LayerSourcePageContext pageContext, String filePath);
private File getLastDirectory() {
Preferences preferences = SnapApp.getDefault().getPreferences();
String dirPath = preferences.get(LAST_DIR, System.getProperty("user.home"));
File lastDir = new File(dirPath);
if (!lastDir.isDirectory()) {
lastDir = new File(System.getProperty("user.home"));
}
return lastDir;
}
}
private class ImageFileItemListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String imageFilePath = (String) imageFileBox.getSelectedItem();
if (imageFilePath == null || !new File(imageFilePath).isFile()) {
return;
}
ImagePreviewWorker worker = new ImagePreviewWorker(imageFilePath, imagePreviewLabel);
worker.execute();
getContext().setPropertyValue(ImageFileLayerSource.PROPERTY_NAME_IMAGE_FILE_PATH, imageFilePath);
String worldFilePath = createWorldFilePath(imageFilePath);
if (new File(worldFilePath).isFile()) {
worldFileField.setText(worldFilePath);
} else {
worldFileField.setText(null);
}
getContext().updateState();
}
private String createWorldFilePath(String imageFilePath) {
String imageFileExt = FileUtils.getExtension(imageFilePath);
// Rule for world file extension: <name>.<a><b><c> --> <name>.<a><c>w
// see http://support.esri.com/index.cfm?fa=knowledgebase.techarticles.articleShow&d=17489
String worldFilePath;
if (imageFileExt != null && imageFileExt.length() == 4) { // three chars + leading dot
String worldFileExt = imageFileExt.substring(0, 2) +
imageFileExt.charAt(imageFileExt.length() - 1) + "w";
worldFilePath = FileUtils.exchangeExtension(imageFilePath, worldFileExt);
} else {
worldFilePath = imageFilePath + "w";
}
return worldFilePath;
}
private class ImagePreviewWorker extends SwingWorker<Image, Object> {
private final Dimension targetDimension;
private final String imageFilePath;
private final JLabel imageLabel;
private ImagePreviewWorker(String imageFilePath, JLabel imageLabel) {
this.imageFilePath = imageFilePath;
this.imageLabel = imageLabel;
this.targetDimension = this.imageLabel.getSize();
}
@Override
protected Image doInBackground() throws Exception {
RenderedImage sourceImage = FileLoadDescriptor.create(imageFilePath, null, true, null);
int width = sourceImage.getWidth();
int height = sourceImage.getHeight();
float scale = (float) (targetDimension.getWidth() / width);
scale = (float) Math.min(scale, targetDimension.getHeight() / height);
if (scale > 1) {
scale = 1.0f;
}
Interpolation interpolation = Interpolation.getInstance(Interpolation.INTERP_NEAREST);
RenderedImage scaledImage = ScaleDescriptor.create(sourceImage,
scale, scale,
0.0f, 0.0f,
interpolation, null);
PlanarImage planarImage = PlanarImage.wrapRenderedImage(scaledImage);
BufferedImage bufferedImage = planarImage.getAsBufferedImage();
planarImage.dispose();
return bufferedImage;
}
@Override
protected void done() {
try {
imageLabel.setIcon(new ImageIcon(get()));
imageLabel.setText(null);
} catch (InterruptedException e) {
e.printStackTrace();
imageLabel.setText("Could not create preview");
} catch (ExecutionException e) {
e.printStackTrace();
imageLabel.setText("Could not create preview");
}
}
}
}
}