plugins.ImageRectificationPanel.java Source code

Java tutorial

Introduction

Here is the source code for plugins.ImageRectificationPanel.java

Source

/*
 * Copyright (C) 2013 Dr. John Lindsay <jlindsay@uoguelph.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 plugins;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.event.ChangeListener;
import java.awt.Dimension;
import java.text.DecimalFormat;
import java.util.Date;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.TableModelEvent;
import javax.swing.table.*;
import javax.swing.event.TableModelListener;
import org.apache.commons.math3.linear.*;
import whitebox.geospatialfiles.ShapeFile;
import whitebox.geospatialfiles.WhiteboxRaster;
import whitebox.geospatialfiles.shapefile.*;
import static whitebox.geospatialfiles.shapefile.ShapeType.POINT;
import static whitebox.geospatialfiles.shapefile.ShapeType.POINTM;
import static whitebox.geospatialfiles.shapefile.ShapeType.POINTZ;
import whitebox.interfaces.WhiteboxPluginHost;
import whitebox.structures.XYPoint;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ResourceBundle;

/**
 * Can't find
 * @author Dr. John Lindsay email: jlindsay@uoguelph.ca
 */
public class ImageRectificationPanel extends JPanel
        implements ActionListener, ChangeListener, TableModelListener, PropertyChangeListener, MouseListener {
    // global variables

    private String inputImageFile;
    private String imageGCPFile;
    private String mapGCPFile;
    private String outputImageFile;
    private int polyOrder = 1;
    private double[] forwardRegressCoeffX;
    private double[] forwardRegressCoeffY;
    private double[] backRegressCoeffX;
    private double[] backRegressCoeffY;
    private int numCoefficients;
    private double[] imageGCPsXCoords;
    private double[] imageGCPsYCoords;
    private double[] mapGCPsXCoords;
    private double[] mapGCPsYCoords;
    private double[] residualsXY;
    private boolean[] useGCP;
    private double imageXMin;
    private double imageYMin;
    private double mapXMin;
    private double mapYMin;
    private WhiteboxPluginHost myHost;
    private JSpinner polyOrderSpinner;
    private JTable dataTable;
    private JProgressBar progressBar;
    private Task task;
    private JLabel cancel;
    private ResourceBundle bundle;
    private ResourceBundle messages;

    // constructors
    public ImageRectificationPanel() {
        // no-args constructor
    }

    public ImageRectificationPanel(String inputImageFile, String imageGCPFile, String mapGCPFile,
            String outputImageFile, WhiteboxPluginHost host) {
        this.imageGCPFile = imageGCPFile;
        this.inputImageFile = inputImageFile;
        this.mapGCPFile = mapGCPFile;
        this.outputImageFile = outputImageFile;
        this.myHost = host;
        this.bundle = host.getGuiLabelsBundle();
        this.messages = host.getMessageBundle();

        readFiles();

        createGui();
    }

    // properties
    public String getInputImageFile() {
        return inputImageFile;
    }

    public void setInputImageFile(String inputImageFile) {
        this.inputImageFile = inputImageFile;
    }

    public String getImageGCPFile() {
        return imageGCPFile;
    }

    public void setImageGCPFile(String imageGCPFile) {
        this.imageGCPFile = imageGCPFile;
    }

    public String getMapGCPFile() {
        return mapGCPFile;
    }

    public void setMapGCPFile(String mapGCPFile) {
        this.mapGCPFile = mapGCPFile;
    }

    public String getOutputImageFile() {
        return outputImageFile;
    }

    public void setOutputImageFile(String outputImageFile) {
        this.outputImageFile = outputImageFile;
    }

    public int getPolyOrder() {
        return polyOrder;
    }

    public void setPolyOrder(int polyOrder) {
        this.polyOrder = polyOrder;
    }

    // methods
    public final void createGui() {
        this.removeAll();

        if (imageGCPsXCoords == null) {
            return;
        }
        int i;
        int newN = 0;
        for (i = 0; i < imageGCPsXCoords.length; i++) {
            if (useGCP[i]) {
                newN++;
            }
        }
        double[] X1 = new double[newN];
        double[] Y1 = new double[newN];
        double[] X2 = new double[newN];
        double[] Y2 = new double[newN];

        int j = 0;
        for (i = 0; i < imageGCPsXCoords.length; i++) {
            if (useGCP[i]) {
                X1[j] = imageGCPsXCoords[i];
                Y1[j] = imageGCPsYCoords[i];
                X2[j] = mapGCPsXCoords[i];
                Y2[j] = mapGCPsYCoords[i];
                j++;
            }
        }

        calculateEquations(X1, Y1, X2, Y2);

        // gui stuff
        this.setLayout(new BorderLayout());

        DecimalFormat df = new DecimalFormat("###,###,##0.000");

        JPanel buttonPane = new JPanel();
        buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS));
        JButton btnOK = createButton(bundle.getString("OK"), bundle.getString("OK"), "ok");
        JButton btnExit = createButton(bundle.getString("Close"), bundle.getString("Close"), "close");
        //JButton btnRefresh = createButton("Cancel", "Cancel");

        buttonPane.add(Box.createHorizontalStrut(10));
        buttonPane.add(btnOK);
        buttonPane.add(Box.createHorizontalStrut(5));
        //buttonPane.add(btnRefresh);
        buttonPane.add(Box.createHorizontalStrut(5));
        buttonPane.add(btnExit);
        buttonPane.add(Box.createHorizontalGlue());

        progressBar = new JProgressBar(0, 100);
        buttonPane.add(progressBar);
        buttonPane.add(Box.createHorizontalStrut(5));
        cancel = new JLabel(bundle.getString("Cancel"));
        cancel.setForeground(Color.BLUE.darker());
        cancel.addMouseListener(this);
        buttonPane.add(cancel);
        buttonPane.add(Box.createHorizontalStrut(10));

        this.add(buttonPane, BorderLayout.SOUTH);

        Box mainBox = Box.createVerticalBox();
        mainBox.add(Box.createVerticalStrut(10));

        Box box1 = Box.createHorizontalBox();
        box1.add(Box.createHorizontalStrut(10));
        box1.add(new JLabel(bundle.getString("PolynomialOrder") + ": "));
        SpinnerModel model = new SpinnerNumberModel(polyOrder, //initial value
                1, //min
                5, //max
                1); //step

        polyOrderSpinner = new JSpinner(model);
        polyOrderSpinner.setPreferredSize(new Dimension(15, polyOrderSpinner.getPreferredSize().height));
        polyOrderSpinner.addChangeListener(this);

        JSpinner.DefaultEditor editor = (JSpinner.DefaultEditor) polyOrderSpinner.getEditor();
        editor.getTextField().setEnabled(true);
        editor.getTextField().setEditable(false);

        box1.add(polyOrderSpinner);
        box1.add(Box.createHorizontalGlue());
        JLabel label = new JLabel("RMSE: " + df.format(overallRMSE));
        box1.add(label);
        box1.add(Box.createHorizontalStrut(10));
        mainBox.add(box1);

        mainBox.add(Box.createVerticalStrut(10));

        // Create columns names
        int numPoints = imageGCPsXCoords.length;
        Object dataValues[][] = new Object[numPoints][7];
        j = 0;
        for (i = 0; i < numPoints; i++) {
            dataValues[i][0] = i + 1;
            dataValues[i][1] = df.format(imageGCPsXCoords[i]);
            dataValues[i][2] = df.format(imageGCPsYCoords[i]);
            dataValues[i][3] = df.format(mapGCPsXCoords[i]);
            dataValues[i][4] = df.format(mapGCPsYCoords[i]);
            if (useGCP[i]) {
                dataValues[i][5] = df.format(residualsXY[j]);
                j++;
            } else {
                dataValues[i][5] = null;
            }
            dataValues[i][6] = useGCP[i];
        }

        String columnNames[] = { "GCP", bundle.getString("Image") + " X", bundle.getString("Image") + " Y",
                bundle.getString("Map") + " X", bundle.getString("Map") + " Y", messages.getString("Error"),
                "Use" };

        DefaultTableModel tableModel = new DefaultTableModel(dataValues, columnNames);

        dataTable = new JTable(tableModel) {
            private static final long serialVersionUID = 1L;

            @Override
            public Class getColumnClass(int column) {
                switch (column) {
                case 0:
                    return Integer.class;
                case 1:
                    return String.class; //Double.class;
                case 2:
                    return String.class; //Double.class;
                case 3:
                    return String.class; //Double.class;
                case 4:
                    return String.class; //Double.class;
                case 5:
                    return String.class; //Double.class;
                case 6:
                    return Boolean.class;
                default:
                    return String.class; //Double.class;
                }
            }

            @Override
            public Component prepareRenderer(TableCellRenderer renderer, int index_row, int index_col) {
                Component comp = super.prepareRenderer(renderer, index_row, index_col);
                //even index, selected or not selected

                if (index_row % 2 == 0) {
                    comp.setBackground(Color.WHITE);
                    comp.setForeground(Color.BLACK);
                } else {
                    comp.setBackground(new Color(225, 245, 255)); //new Color(210, 230, 255));
                    comp.setForeground(Color.BLACK);
                }
                if (isCellSelected(index_row, index_col)) {
                    comp.setForeground(Color.RED);
                }
                return comp;
            }
        };

        tableModel.addTableModelListener(this);

        TableCellRenderer rend = dataTable.getTableHeader().getDefaultRenderer();
        TableColumnModel tcm = dataTable.getColumnModel();
        //for (int j = 0; j < tcm.getColumnCount(); j += 1) {
        TableColumn tc = tcm.getColumn(0);
        TableCellRenderer rendCol = tc.getHeaderRenderer(); // likely null  
        if (rendCol == null) {
            rendCol = rend;
        }
        Component c = rendCol.getTableCellRendererComponent(dataTable, tc.getHeaderValue(), false, false, 0, 0);
        tc.setPreferredWidth(35);

        tc = tcm.getColumn(6);
        rendCol = tc.getHeaderRenderer(); // likely null  
        if (rendCol == null) {
            rendCol = rend;
        }
        c = rendCol.getTableCellRendererComponent(dataTable, tc.getHeaderValue(), false, false, 0, 6);
        tc.setPreferredWidth(35);

        JScrollPane scroll = new JScrollPane(dataTable);
        mainBox.add(scroll);

        this.add(mainBox, BorderLayout.CENTER);

        this.validate();
    }

    private JButton createButton(String buttonLabel, String toolTip, String actionCommand) {
        JButton btn = new JButton(buttonLabel);
        btn.addActionListener(this);
        btn.setActionCommand(actionCommand);
        btn.setToolTipText(toolTip);
        return btn;
    }

    private void readFiles() {
        try {

            if (imageGCPFile == null || mapGCPFile == null) {
                return;
            }

            int i;

            ShapeFile imageGCPs = new ShapeFile(imageGCPFile);
            ShapeFile mapGCPs = new ShapeFile(mapGCPFile);

            int n = imageGCPs.getNumberOfRecords();
            if (n != mapGCPs.getNumberOfRecords()) {
                showFeedback("Shapefiles must have the same number of GCPs.");
                return;
            }
            if (imageGCPs.getShapeType().getBaseType() != ShapeType.POINT
                    || mapGCPs.getShapeType().getBaseType() != ShapeType.POINT) {
                showFeedback("Shapefiles must be of Point ShapeType. \n" + "The operation will not continue.");
                return;
            }

            // Read the GCP data 
            imageGCPsXCoords = new double[n];
            imageGCPsYCoords = new double[n];
            mapGCPsXCoords = new double[n];
            mapGCPsYCoords = new double[n];

            i = 0;
            for (ShapeFileRecord record : imageGCPs.records) {
                double[][] vertices = new double[1][1];
                ShapeType shapeType = record.getShapeType();
                switch (shapeType) {
                case POINT:
                    whitebox.geospatialfiles.shapefile.Point recPoint = (whitebox.geospatialfiles.shapefile.Point) (record
                            .getGeometry());
                    vertices = recPoint.getPoints();
                    break;
                case POINTZ:
                    PointZ recPointZ = (PointZ) (record.getGeometry());
                    vertices = recPointZ.getPoints();
                    break;
                case POINTM:
                    PointM recPointM = (PointM) (record.getGeometry());
                    vertices = recPointM.getPoints();
                    break;
                default:
                    showFeedback("Shapefiles must be of Point ShapeType. \n" + "The operation will not continue.");
                    return;
                }

                imageGCPsXCoords[i] = vertices[0][0];// - imageXMin;
                imageGCPsYCoords[i] = vertices[0][1];// - imageYMin;

                i++;
            }

            i = 0;
            for (ShapeFileRecord record : mapGCPs.records) {
                double[][] vertices = new double[1][1];
                ShapeType shapeType = record.getShapeType();
                switch (shapeType) {
                case POINT:
                    whitebox.geospatialfiles.shapefile.Point recPoint = (whitebox.geospatialfiles.shapefile.Point) (record
                            .getGeometry());
                    vertices = recPoint.getPoints();
                    break;
                case POINTZ:
                    PointZ recPointZ = (PointZ) (record.getGeometry());
                    vertices = recPointZ.getPoints();
                    break;
                case POINTM:
                    PointM recPointM = (PointM) (record.getGeometry());
                    vertices = recPointM.getPoints();
                    break;
                default:
                    showFeedback("Shapefiles must be of Point ShapeType. \n" + "The operation will not continue.");
                    return;
                }

                mapGCPsXCoords[i] = vertices[0][0];// - mapXMin;
                mapGCPsYCoords[i] = vertices[0][1];// - mapYMin;

                i++;
            }

            useGCP = new boolean[n];
            for (i = 0; i < n; i++) {
                useGCP[i] = true;
            }

        } catch (OutOfMemoryError oe) {
            myHost.showFeedback("An out-of-memory error has occurred during operation.");
        } catch (Exception e) {
            myHost.showFeedback("An error has occurred during operation. See log file for details.");
            myHost.logException("Error in ImageRectification", e);
        }
    }

    double overallRMSE = 0.0;

    public void calculateEquations(double[] imageX, double[] imageY, double[] mapX, double[] mapY) {
        try {
            int m, i, j, k;

            int n = mapX.length;

            // How many coefficients are there?
            numCoefficients = 0;

            for (j = 0; j <= polyOrder; j++) {
                for (k = 0; k <= (polyOrder - j); k++) {
                    numCoefficients++;
                }
            }

            for (i = 0; i < n; i++) {
                imageX[i] -= imageXMin;
                imageY[i] -= imageYMin;
                mapX[i] -= mapXMin;
                mapY[i] -= mapYMin;
            }

            // Solve the forward transformation equations
            double[][] forwardCoefficientMatrix = new double[n][numCoefficients];
            for (i = 0; i < n; i++) {
                m = 0;
                for (j = 0; j <= polyOrder; j++) {
                    for (k = 0; k <= (polyOrder - j); k++) {
                        forwardCoefficientMatrix[i][m] = Math.pow(imageX[i], j) * Math.pow(imageY[i], k);
                        m++;
                    }
                }
            }

            RealMatrix coefficients = new Array2DRowRealMatrix(forwardCoefficientMatrix, false);
            //DecompositionSolver solver = new SingularValueDecomposition(coefficients).getSolver();
            DecompositionSolver solver = new QRDecomposition(coefficients).getSolver();

            // do the x-coordinate first
            RealVector constants = new ArrayRealVector(mapX, false);
            RealVector solution = solver.solve(constants);
            forwardRegressCoeffX = new double[n];
            for (int a = 0; a < numCoefficients; a++) {
                forwardRegressCoeffX[a] = solution.getEntry(a);
            }

            double[] residualsX = new double[n];
            double SSresidX = 0;
            for (i = 0; i < n; i++) {
                double yHat = 0.0;
                for (j = 0; j < numCoefficients; j++) {
                    yHat += forwardCoefficientMatrix[i][j] * forwardRegressCoeffX[j];
                }
                residualsX[i] = mapX[i] - yHat;
                SSresidX += residualsX[i] * residualsX[i];
            }

            double sumX = 0;
            double SSx = 0;
            for (i = 0; i < n; i++) {
                SSx += mapX[i] * mapX[i];
                sumX += mapX[i];
            }
            double varianceX = (SSx - (sumX * sumX) / n) / n;
            double SStotalX = (n - 1) * varianceX;
            double rsqX = 1 - SSresidX / SStotalX;

            //System.out.println("x-coordinate r-square: " + rsqX);

            // now the y-coordinate 
            constants = new ArrayRealVector(mapY, false);
            solution = solver.solve(constants);
            forwardRegressCoeffY = new double[numCoefficients];
            for (int a = 0; a < numCoefficients; a++) {
                forwardRegressCoeffY[a] = solution.getEntry(a);
            }

            double[] residualsY = new double[n];
            residualsXY = new double[n];
            double SSresidY = 0;
            for (i = 0; i < n; i++) {
                double yHat = 0.0;
                for (j = 0; j < numCoefficients; j++) {
                    yHat += forwardCoefficientMatrix[i][j] * forwardRegressCoeffY[j];
                }
                residualsY[i] = mapY[i] - yHat;
                SSresidY += residualsY[i] * residualsY[i];
                residualsXY[i] = Math.sqrt(residualsX[i] * residualsX[i] + residualsY[i] * residualsY[i]);
            }

            double sumY = 0;
            double sumR = 0;
            double SSy = 0;
            double SSr = 0;
            for (i = 0; i < n; i++) {
                SSy += mapY[i] * mapY[i];
                SSr += residualsXY[i] * residualsXY[i];
                sumY += mapY[i];
                sumR += residualsXY[i];
            }
            double varianceY = (SSy - (sumY * sumY) / n) / n;
            double varianceResiduals = (SSr - (sumR * sumR) / n) / n;
            double SStotalY = (n - 1) * varianceY;
            double rsqY = 1 - SSresidY / SStotalY;
            overallRMSE = Math.sqrt(varianceResiduals);

            //System.out.println("y-coordinate r-square: " + rsqY);

            //            // Print the residuals.
            //            System.out.println("\nResiduals:");
            //            for (i = 0; i < n; i++) {
            //                System.out.println("Point " + (i + 1) + "\t" + residualsX[i]
            //                        + "\t" + residualsY[i] + "\t" + residualsXY[i]);
            //            }

            // Solve the backward transformation equations
            double[][] backCoefficientMatrix = new double[n][numCoefficients];
            for (i = 0; i < n; i++) {
                m = 0;
                for (j = 0; j <= polyOrder; j++) {
                    for (k = 0; k <= (polyOrder - j); k++) {
                        backCoefficientMatrix[i][m] = Math.pow(mapX[i], j) * Math.pow(mapY[i], k);
                        m++;
                    }
                }
            }

            coefficients = new Array2DRowRealMatrix(backCoefficientMatrix, false);
            //DecompositionSolver solver = new SingularValueDecomposition(coefficients).getSolver();
            solver = new QRDecomposition(coefficients).getSolver();

            // do the x-coordinate first
            constants = new ArrayRealVector(imageX, false);
            solution = solver.solve(constants);
            backRegressCoeffX = new double[numCoefficients];
            for (int a = 0; a < numCoefficients; a++) {
                backRegressCoeffX[a] = solution.getEntry(a);
            }

            // now the y-coordinate 
            constants = new ArrayRealVector(imageY, false);
            solution = solver.solve(constants);
            backRegressCoeffY = new double[n];
            for (int a = 0; a < numCoefficients; a++) {
                backRegressCoeffY[a] = solution.getEntry(a);
            }
        } catch (OutOfMemoryError oe) {
            myHost.showFeedback("An out-of-memory error has occurred during operation.");
        } catch (Exception e) {
            myHost.showFeedback("An error has occurred during operation. See log file for details.");
            myHost.logException("Error in ImageRectification", e);
        }
    }

    private XYPoint getForwardCoordinates(double x, double y) {
        XYPoint ret;
        int j, k, m;
        double x_transformed = 0; //mapXMin;
        double y_transformed = 0; //mapYMin;
        double term;
        m = 0;
        for (j = 0; j <= polyOrder; j++) {
            for (k = 0; k <= (polyOrder - j); k++) {
                term = Math.pow(x, j) * Math.pow(y, k);
                x_transformed += term * forwardRegressCoeffX[m];
                y_transformed += term * forwardRegressCoeffY[m];
                m++;
            }
        }

        ret = new XYPoint(x_transformed, y_transformed);

        return ret;
    }

    private XYPoint getBackwardCoordinates(double x, double y) {
        XYPoint ret;
        int j, k, m;
        double x_transformed = 0; //imageXMin;
        double y_transformed = 0; //imageYMin;
        double term;
        m = 0;
        for (j = 0; j <= polyOrder; j++) {
            for (k = 0; k <= (polyOrder - j); k++) {
                term = Math.pow(x, j) * Math.pow(y, k);
                x_transformed += term * backRegressCoeffX[m];
                y_transformed += term * backRegressCoeffY[m];
                m++;
            }
        }

        ret = new XYPoint(x_transformed, y_transformed);

        return ret;
    }

    /**
     * Used to communicate feedback pop-up messages between a plugin tool and
     * the main Whitebox user-interface.
     *
     * @param feedback String containing the text to display.
     */
    private void showFeedback(String feedback) {
        if (myHost != null) {
            myHost.showFeedback(feedback);
        } else {
            System.out.println(feedback);
        }
    }

    /**
     * Used to communicate a return object from a plugin tool to the main
     * Whitebox user-interface.
     *
     * @return Object, such as an output WhiteboxRaster.
     */
    private void returnData(Object ret) {
        if (myHost != null) {
            myHost.returnData(ret);
        }
    }

    /**
     * Used to communicate a progress update between a plugin tool and the main
     * Whitebox user interface.
     *
     * @param progressLabel A String to use for the progress label.
     * @param progress Float containing the progress value (between 0 and 100).
     */
    private void updateProgress(String progressLabel, int progress) {
        if (myHost != null) {
            myHost.updateProgress(progressLabel, progress);
        } else {
            System.out.println(progressLabel + " " + progress + "%");
        }
    }

    /**
     * Used to communicate a progress update between a plugin tool and the main
     * Whitebox user interface.
     *
     * @param progress Float containing the progress value (between 0 and 100).
     */
    private void updateProgress(int progressVal) {
        progressBar.setValue(progressVal);
        //        if (myHost != null) {
        //            myHost.updateProgress(progress);
        //        } else {
        //            System.out.println("Progress: " + progress + "%");
        //        }
    }

    private boolean cancelOp = false;

    /**
     * Used to communicate a cancel operation from the Whitebox GUI.
     *
     * @param cancel Set to true if the plugin should be canceled.
     */
    public void setCancelOp(boolean cancel) {
        cancelOp = cancel;
    }

    private void cancelOperation() {
        showFeedback("Operation cancelled.");
        //updateProgress("Progress: ", 0);
    }

    //This method is only used during testing.
    public static void main(String[] args) {
        try {
            //            int polyOrder = 4;
            ////            String inputGCPFile1 = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/tiepoints 15-16 image 15.shp";
            ////            String inputRasterFile1 = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/A19411_15_Blue.dep";
            ////            String inputGCPFile2 = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/tiepoints 15-16 image 16.shp";
            ////            String inputRasterFile2 = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/A19411_16_Blue.dep";
            ////            String outputRasterFile = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/16 registered.dep";
            //
            ////            String inputGCPFile1 = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/tiepoints final image 15-16.shp";
            ////            String inputRasterFile1 = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/tmp6.dep";
            ////            String inputGCPFile2 = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/tiepoints final image 17.shp";
            ////            String inputRasterFile2 = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/17 adjusted.dep";
            ////            String outputRasterFile = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/17 registered.dep";
            //
            ////            String inputGCPFile1 = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/image 15 GCPs map.shp";
            ////            String inputRasterFile1 = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/A19411_15_Blue.dep";
            ////            String inputGCPFile2 = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/image 15 GCPs.shp";
            ////            String outputRasterFile = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/15 registered to map1.dep";
            //
            //            String inputGCPFile1 = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/image 16 GCPs map.shp";
            //            String inputRasterFile1 = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/A19411_16_Blue.dep";
            //            String inputGCPFile2 = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/image 16 GCPs.shp";
            //            String outputRasterFile = "/Users/johnlindsay/Documents/Data/Guelph Photomosaic/16 registered to map1.dep";
            //
            //
            //            args = new String[5];
            //            args[0] = inputGCPFile1;
            //            args[1] = inputRasterFile1;
            //            args[2] = inputGCPFile2;
            //            args[3] = outputRasterFile;
            //            args[4] = String.valueOf(polyOrder);
            //
            //            TiePointTransformation tpt = new TiePointTransformation();
            //            tpt.setArgs(args);
            //            tpt.run();
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }

    boolean isRunning = false;

    @Override
    public void actionPerformed(ActionEvent e) {
        String ac = e.getActionCommand().toLowerCase();
        switch (ac) {
        case "close":
            JDialog d = (JDialog) SwingUtilities.getAncestorOfClass(JDialog.class, this);
            d.dispose();
            cancelOp = true;
            break;
        case "ok":
            if (!isRunning) { // you only want one of these threads running at a time.
                cancelOp = false;
                task = new Task();
                task.addPropertyChangeListener(this);
                task.execute();
            }
            break;
        case "cancel":
            cancelOp = true;
            break;
        }
    }

    @Override
    public void stateChanged(ChangeEvent e) {
        SpinnerModel model = polyOrderSpinner.getModel();
        if (model instanceof SpinnerNumberModel) {
            polyOrder = (int) (((SpinnerNumberModel) model).getValue());
            createGui();
        }
    }

    @Override
    public void tableChanged(TableModelEvent e) {
        if (e.getType() == TableModelEvent.UPDATE) {
            int column = e.getColumn();
            if (column == 6) {
                int row = e.getFirstRow();
                useGCP[row] = (boolean) (dataTable.getValueAt(row, column));
                createGui();
            }
        }
    }

    /**
     * Invoked when task's progress property changes.
     */
    @Override
    public void propertyChange(PropertyChangeEvent evt) {

        if (evt.getPropertyName().equals("progress")) {
            int progress = (Integer) evt.getNewValue();
            progressBar.setValue(progress);
        }
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
        cancel.setForeground(Color.RED.darker());
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        cancel.setForeground(Color.BLUE.darker());
        cancelOp = true;
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }

    class Task extends SwingWorker<Void, Void> {
        /*
         * Main task. Executed in background thread.
         */

        @Override
        public Void doInBackground() {
            try {
                WhiteboxRaster inputImage = new WhiteboxRaster(inputImageFile, "r");

                double image2North = inputImage.getNorth();
                double image2South = inputImage.getSouth();
                double image2West = inputImage.getWest();
                double image2East = inputImage.getEast();
                XYPoint topLeftCorner = getForwardCoordinates(image2West, image2North);
                XYPoint topRightCorner = getForwardCoordinates(image2East, image2North);
                XYPoint bottomLeftCorner = getForwardCoordinates(image2West, image2South);
                XYPoint bottomRightCorner = getForwardCoordinates(image2East, image2South);

                // figure out the grid resolution
                double vertCornerDist = Math
                        .sqrt((topLeftCorner.x - bottomLeftCorner.x) * (topLeftCorner.x - bottomLeftCorner.x)
                                + (topLeftCorner.y - bottomLeftCorner.y) * (topLeftCorner.y - bottomLeftCorner.y));

                double horizCornerDist = Math
                        .sqrt((topLeftCorner.x - topRightCorner.x) * (topLeftCorner.x - topRightCorner.x)
                                + (topLeftCorner.y - topRightCorner.y) * (topLeftCorner.y - topRightCorner.y));

                double avgGridRes = (vertCornerDist / inputImage.getNumberRows()
                        + horizCornerDist / inputImage.getNumberColumns()) / 2.0;

                double outputNorth = Double.NEGATIVE_INFINITY;
                double outputSouth = Double.POSITIVE_INFINITY;
                double outputEast = Double.NEGATIVE_INFINITY;
                double outputWest = Double.POSITIVE_INFINITY;

                if (topLeftCorner.y > outputNorth) {
                    outputNorth = topLeftCorner.y;
                }
                if (topLeftCorner.y < outputSouth) {
                    outputSouth = topLeftCorner.y;
                }
                if (topLeftCorner.x > outputEast) {
                    outputEast = topLeftCorner.x;
                }
                if (topLeftCorner.x < outputWest) {
                    outputWest = topLeftCorner.x;
                }

                if (topRightCorner.y > outputNorth) {
                    outputNorth = topRightCorner.y;
                }
                if (topRightCorner.y < outputSouth) {
                    outputSouth = topRightCorner.y;
                }
                if (topRightCorner.x > outputEast) {
                    outputEast = topRightCorner.x;
                }
                if (topRightCorner.x < outputWest) {
                    outputWest = topRightCorner.x;
                }

                if (bottomLeftCorner.y > outputNorth) {
                    outputNorth = bottomLeftCorner.y;
                }
                if (bottomLeftCorner.y < outputSouth) {
                    outputSouth = bottomLeftCorner.y;
                }
                if (bottomLeftCorner.x > outputEast) {
                    outputEast = bottomLeftCorner.x;
                }
                if (bottomLeftCorner.x < outputWest) {
                    outputWest = bottomLeftCorner.x;
                }

                if (bottomRightCorner.y > outputNorth) {
                    outputNorth = bottomRightCorner.y;
                }
                if (bottomRightCorner.y < outputSouth) {
                    outputSouth = bottomRightCorner.y;
                }
                if (bottomRightCorner.x > outputEast) {
                    outputEast = bottomRightCorner.x;
                }
                if (bottomRightCorner.x < outputWest) {
                    outputWest = bottomRightCorner.x;
                }

                double nsRange = outputNorth - outputSouth;
                double ewRange = outputEast - outputWest;

                int nRows = (int) (nsRange / avgGridRes);
                int nCols = (int) (ewRange / avgGridRes);

                WhiteboxRaster output = new WhiteboxRaster(outputImageFile, outputNorth, outputSouth, outputEast,
                        outputWest, nRows, nCols, inputImage.getDataScale(), inputImage.getDataType(),
                        inputImage.getNoDataValue(), inputImage.getNoDataValue());

                double outputX, outputY;
                double inputX, inputY;
                int inputCol, inputRow;
                XYPoint point;
                double z;
                int oldProgress = -1;
                int progress;
                for (int row = 0; row < nRows; row++) {
                    for (int col = 0; col < nCols; col++) {
                        outputX = output.getXCoordinateFromColumn(col);
                        outputY = output.getYCoordinateFromRow(row);

                        // back transform them into image 2 coordinates.
                        point = getBackwardCoordinates(outputX, outputY);

                        inputX = point.x;
                        inputY = point.y;

                        inputCol = inputImage.getColumnFromXCoordinate(inputX);
                        inputRow = inputImage.getRowFromYCoordinate(inputY);

                        z = inputImage.getValue(inputRow, inputCol);

                        output.setValue(row, col, z);
                    }
                    if (cancelOp) {
                        cancelOperation();
                        return null;
                    }
                    progress = (int) (100f * row / (nRows - 1));
                    if (progress != oldProgress) {
                        setProgress(progress);
                    }
                }

                output.addMetadataEntry("Created by the " + "ImageRectification tool.");
                output.addMetadataEntry("Created on " + new Date());

                output.close();

                returnData(outputImageFile);

                return null;
            } catch (Exception e) {
                return null;
            } finally {
                isRunning = false;
            }
        }

        /*
         * Executed in event dispatching thread
         */
        @Override
        public void done() {
            setProgress(0);
        }
    }
}