package org.mbari.seepsearch.grid;

import java.awt.geom.Rectangle2D;

/**
 * <p>Efficient storage of a Raster data grid. Grids are assumed to be rectangular.
 * Data is stored internally as floats. The x and y axis are stored as 1-D arrays
 * while the z data is stored as a 2-D array. To check if a grid contains a point,
 * use <code>getRectangle2D()</code> to retrive a reference to the rectangle and use it's
 * <code>contains()</code> method</p>
 *
 * <p>The Seep Search project is a sub-project of the Continental
 * Margin Fluids and Gases project (200010). It involves the analysis of
 * archived ROV data to obtain understanding of seep distritution in Monterey
 * Bay.</p>
 *
 * <p>The Purpose of this Java package is to intercompare ROV Navigation files,
 * VIMS data on seep community observations and the EM-300 bathymetry. We are
 * looking for locations where the ROV navigation files and the EM-300 data
 * have similar depths between the 2 data sets, which implies that the ROV is
 * observing the sea floor.</p><hr><br>
 *
 * <p>MBARI provides this documentation and code &quot;as is&quot;, with no
 * warranty, express or implied, of its quality or consistency. It is provided
 * without support and without obligation on the part of the Monterey Bay
 * Aquarium Research Institute to assist in its use, correction, modification,
 * or enhancement. This information should not be published or distributed to
 * third parties without specific written permission from MBARI.</p><br>
 *
 * Copyright 2002 MBARI.<br>
 * MBARI Proprietary Information. All rights reserved.<br><hr><br>
 *
 * $Log: RasterGrid.java,v $
 * Revision 1.8  2002/02/25 16:40:40  brian
 * Modified tools to ignore eastings and northings columns.
 *
 * Revision 1.7  2002/02/09 00:07:19  brian
 * Working tools to calculate ROV altitude. There's a glitch that occurs for a few files. Need to look into this
 *
 * Revision 1.6  2002/01/30 00:23:56  brian
 * Added comments to all classes and methods. Tested the accuracy of AsciiRasterGridReader and AsciiRasterGridWriter. Also checked the the GridUtil was populating the spatial grids correctly and accurately. The data files generated matched up perfectly with the orginal text files. Also implemented an RovAltitudeFilter class to encapsulate different filtering methods.
 *<br><hr><br>
 *
 * @author  : $Author: brian $
 * @version : $Revision: 1.8 $
 */
public class RasterGrid implements Cloneable {

    /**
     * @param x X-coordinates (i.e East-West) for each column of z
     * @param y Y-coordinates (i.e. North-South) for each row of z
     * @param z data values whos positions are defined by the x and y arrays
     * @throws GridException thrown if the x.length != z[0].length and y.length != z.length
     */
    public RasterGrid( double[] x, double[] y, float[][] z) throws GridException {

        if ((x.length == z[0].length) && (y.length == z.length)) {
            this.x = x;
            this.y = y;
            this.z = z;
            setBounds();
        } else {
            throw new GridException("Dimensions of z do not match the " +
                    "length of the x and y axes!");
        }

    }


    /**
     * @return The x values defining the location coordinates of the columns in z
     */
    public double[] getX(){
        return x;
    }

    /**
     * @param x sets the x coordinates
     * @throws GridException Thrown if x is the incorrect size for the z array
     */
    public void setX(double[] x) throws GridException {
        if (this.x.length == x.length) {
            this.x = x;
            setBounds();
        } else {
            throw new GridException("X is the wrong size. Input is a float[" + x.length +
                    "], it must be a float[" + this.x.length + "].");
        }
    }

    /**
     * @return The y values defining the location coordinates of the columns in z
     */
    public double[] getY() {
        return y;
    }

    /**
     * @param y sets the y coordinates
     * @throws GridException Thrown if y is the incorrect size for the z array
     */
    public void setY(double[] y) throws GridException {
        if (this.y.length == y.length) {
            this.y = y;
            setBounds();
        } else {
            throw new GridException("Y is the wrong size. Input is a float[" + y.length +
                    "], it must be a float[" + this.y.length + "].");
        }
    }

    /**
     * @return The z data whos geographic coordinates are defined by x and y.
     * Column cooridnates are defined by x, rows by y.
     */
    public float[][] getZ() {
        return z;
    }

    /**
     * @param z sets the z coordinates
     * @throws GridException Thrown if z is a different size from the existing
     * z array.
     */
    public void setZ(float[][] z) throws GridException {
        if ((this.z.length == z.length) && (this.z[0].length == z[0].length)) {
            this.z = z;
        } else {
            throw new GridException("Z is the wrong size. Input is a float[" + z.length +
                    "][" + z[0].length + "], it must be a float[" + this.z.length +
                    "][" + this.z[0].length + "].");
        }
        this.z = z;
    }

    /**
     * @param z sets a z coordinate
     * @param xIndex The column index of the z value to change
     * @param yIndex The row index of the z value to change
     */
    public void setZ(float zValue, int xIndex, int yIndex) {

        this.z[xIndex][yIndex] = zValue;
    }

    /**
     * Tests if a specified coordinate is inside the boundary of this
     * <code>RasterGrid</code>.
     * @param x,&nbsp;y the coordinates to test
     * @return <code>true</code> if the specified coordinates are
     * inside the boundary of this <code>RasterGrid</code>;
     * <code>false</code> otherwise.
     * @since 1.2
     * @deprecated 11 Feb 2002, Use getRectangle2D() and then use its contains
     * methods instead
     */
    public boolean contains(double x, double y) {
        return rectangle2D.contains(x, y);
    }



    /**
     * Used to define the boundaries of the grid. Required for contains method.
     */
    private void setBounds() {
        double xMin = 0;
        double xMax = 0;
        double width = 0;
        double yMin = 0;
        double yMax = 0;
        double height = 0;

        if (x[0] < x[x.length - 1]) {
            xMin = x[0];
            xMax = x[x.length - 1];
        } else {
            xMin = x[x.length - 1];
            xMax = x[0];
        }
        width = xMax - xMin;

        if (y[0] < y[y.length - 1]) {
            yMin = y[0];
            yMax = y[y.length - 1];
        } else {
            yMin = y[y.length - 1];
            yMax = y[0];
        }
        height = yMax = yMin;

        rectangle2D = new Rectangle2D.Double(xMin, yMin, width, height);
    }

    public Object clone() {
        Object o = null;
        try {
            o = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return o;
    }

    /**
     * A rectangle is maintained that specifies the boundaries of the RasterGrid.
     * @return Rectangle specifying the boundaries of the RasterGrid. Useful
     * for checking for intersections.
     */
    public Rectangle2D getRectangle2D() {
        return rectangle2D;
    }

    /**
     * Defines the eastings or longitude of the grid
     */
    private double[] x;

    /**
     * Defines the northings or latitude of the grid.
     */
    private double[] y;

    /**
     * Defines the height or other z data.
     */
    private float[][] z;

    /**
     * Used for specifiying the boundaries fo the grid. Useful for checking to
     * see if a point is with in the grid.
     */
    private Rectangle2D rectangle2D ;

}
