/*
* 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 org.esa.snap.core.datamodel.GeoCoding;
import org.esa.snap.core.datamodel.GeoPos;
import org.esa.snap.core.datamodel.PixelPos;
import org.esa.snap.core.datamodel.Product;
import org.esa.snap.core.datamodel.ProductNode;
import org.esa.snap.core.util.SystemUtils;
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.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionRegistration;
import org.openide.util.ContextAwareAction;
import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.openide.util.WeakListeners;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFileChooser;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import static org.esa.snap.rcp.SnapApp.SelectionSourceHint.*;
/**
* This actions exports ground control points of the selected product in a ENVI format.
*/
@ActionID(
category = "File",
id = "org.esa.snap.rcp.actions.file.export.ExportEnviGcpFileAction"
)
@ActionRegistration(
displayName = "#CTL_ExportEnviGcpFileAction_MenuText",
popupText = "#CTL_ExportEnviGcpFileAction_PopupText",
lazy = false
)
@ActionReference(path = "Menu/File/Export/Other", position = 30)
@NbBundle.Messages({
"CTL_ExportEnviGcpFileAction_MenuText=Geo-Coding as ENVI GCP File",
"CTL_ExportEnviGcpFileAction_PopupText=Export Geo-Coding as ENVI GCP File",
"CTL_ExportEnviGcpFileAction_DialogTitle=Export ENVI Ground Control Points",
"CTL_ExportEnviGcpFileAction_ShortDescription=Export an ENVI GCP (ground control points) file for image registration"
})
public class ExportEnviGcpFileAction extends AbstractAction implements LookupListener, ContextAwareAction, HelpCtx.Provider {
private static final String GCP_FILE_DESCRIPTION = "ENVI Ground Control Points";
private static final String GCP_FILE_EXTENSION = ".pts";
private static final String GCP_LINE_SEPARATOR = System.getProperty("line.separator");
private static final String GCP_EXPORT_DIR_PREFERENCES_KEY = "user.gcp.export.dir";
private static final String HELP_ID = "exportEnviGcpFile";
private final Lookup.Result<ProductNode> result;
public ExportEnviGcpFileAction() {
this(Utilities.actionsGlobalContext());
}
public ExportEnviGcpFileAction(Lookup lookup) {
super(Bundle.CTL_ExportEnviGcpFileAction_MenuText());
result = lookup.lookupResult(ProductNode.class);
result.addLookupListener(WeakListeners.create(LookupListener.class, this, result));
setEnabled(false);
}
@Override
public Action createContextAwareInstance(Lookup lookup) {
return new ExportEnviGcpFileAction(lookup);
}
@Override
public void actionPerformed(ActionEvent e) {
exportGroundControlPoints();
}
@Override
public void resultChanged(LookupEvent lookupEvent) {
setEnabled(SnapApp.getDefault().getSelectedProduct(AUTO) != null);
}
@Override
public HelpCtx getHelpCtx() {
return new HelpCtx(HELP_ID);
}
private void exportGroundControlPoints() {
SnapApp snapApp = SnapApp.getDefault();
final Product product = snapApp.getSelectedProduct(AUTO);
if (product == null) {
return;
}
JFileChooser fileChooser = createFileChooser();
int result = fileChooser.showSaveDialog(snapApp.getMainFrame());
if (result != JFileChooser.APPROVE_OPTION) {
return;
}
File file = fileChooser.getSelectedFile();
if (file == null || file.getName().equals("")) {
return;
}
final File absoluteFile = FileUtils.ensureExtension(file.getAbsoluteFile(), GCP_FILE_EXTENSION);
String lastDirPath = absoluteFile.getParent();
Config.instance().load().preferences().put(GCP_EXPORT_DIR_PREFERENCES_KEY, lastDirPath);
final GeoCoding geoCoding = product.getSceneGeoCoding();
if (geoCoding == null) {
return;
}
final Boolean decision = Dialogs.requestOverwriteDecision(Bundle.CTL_ExportEnviGcpFileAction_DialogTitle(), absoluteFile);
if (decision == null || !decision) {
return;
}
if (absoluteFile.exists()) {
absoluteFile.delete();
}
try (FileWriter writer = new FileWriter(absoluteFile)) {
writer.write(createLineString("; ENVI Registration GCP File"));
final int width = product.getSceneRasterWidth();
final int height = product.getSceneRasterHeight();
final int resolution = Config.instance().load().preferences().getInt("gcp.resolution", 112);
final int gcpWidth = Math.max(width / resolution + 1, 2); //2 minimum
final int gcpHeight = Math.max(height / resolution + 1, 2);//2 minimum
final float xMultiplier = 1f * (width - 1) / (gcpWidth - 1);
final float yMultiplier = 1f * (height - 1) / (gcpHeight - 1);
final PixelPos pixelPos = new PixelPos();
final GeoPos geoPos = new GeoPos();
for (int y = 0; y < gcpHeight; y++) {
for (int x = 0; x < gcpWidth; x++) {
final float imageX = xMultiplier * x;
final float imageY = yMultiplier * y;
pixelPos.x = imageX + 0.5f;
pixelPos.y = imageY + 0.5f;
geoCoding.getGeoPos(pixelPos, geoPos);
final double mapX = geoPos.lon; //longitude
final double mapY = geoPos.lat; //latitude
writer.write(createLineString(mapX, mapY,
pixelPos.x + 1,
// + 1 because ENVI uses a one-based pixel co-ordinate system
pixelPos.y + 1));
}
}
} catch (IOException e) {
Dialogs.showInformation(Bundle.CTL_ExportEnviGcpFileAction_DialogTitle(),
"An I/O error occurred:\n" + e.getMessage());
}
}
private static String createLineString(final String str) {
return str.concat(GCP_LINE_SEPARATOR);
}
private static String createLineString(final double mapX, final double mapY, final double imageX, final double imageY) {
return "" + mapX + "\t" + mapY + "\t" + imageX + "\t" + imageY + GCP_LINE_SEPARATOR;
}
private JFileChooser createFileChooser() {
String lastDirPath = Config.instance().load().preferences().get(GCP_EXPORT_DIR_PREFERENCES_KEY,
SystemUtils.getUserHomeDir().getPath());
SnapFileChooser fileChooser = new SnapFileChooser();
HelpCtx.setHelpIDString(fileChooser, getHelpCtx().getHelpID());
fileChooser.setAcceptAllFileFilterUsed(false);
fileChooser.setCurrentDirectory(new File(lastDirPath));
fileChooser.setFileFilter(
new SnapFileFilter(GCP_FILE_DESCRIPTION, GCP_FILE_EXTENSION, GCP_FILE_DESCRIPTION));
fileChooser.setDialogTitle(Bundle.CTL_ExportEnviGcpFileAction_DialogTitle());
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
return fileChooser;
}
}