Coordinate.java

package org.cugos.wkg;

/**
 * A Coordinate that can contain X, Y, Z, and M values
 * @author Jared Erickson
 */
public final class Coordinate {

    /**
     * The X value
     */
    private final double x;

    /**
     * The Y value
     */
    private final double y;

    /**
     * The Z value
     */
    private final double z;

    /**
     * The M value
     */
    private final double m;

    /**
     * Creae a new Coordinate
     * @param x The x value
     * @param y The y value
     * @param z The z value
     * @param m The m value
     */
    public Coordinate(double x, double y, double z, double m) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.m = m;
    }

    /**
     * Create a Coordinate with only XY values
     * @param x The x value
     * @param y The y value
     */
    public Coordinate(double x, double y) {
        this(x, y, Double.NaN, Double.NaN);
    }

    /**
     * Create an empty Coordinate
     * @return An empty Coordinate
     */
    public static Coordinate createEmpty() {
        return new Coordinate(Double.NaN, Double.NaN, Double.NaN, Double.NaN);
    }

    /**
     * Create a 2D Coordinate
     * @param x The x value
     * @param y The y value
     * @return A 2D Coordinate
     */
    public static Coordinate create2D(double x, double y) {
        return new Coordinate(x, y, Double.NaN, Double.NaN);
    }

    /**
     * Create a 3D Coordinate
     * @param x The x value
     * @param y The y value
     * @param z The z value
     * @return A 3D Coordinate
     */
    public static Coordinate create3D(double x, double y, double z) {
        return new Coordinate(x, y, z, Double.NaN);
    }

    /**
     * Create a 2DM Coordinate
     * @param x The x value
     * @param y The y value
     * @param m The m value
     * @return A 2DM Coordinate
     */
    public static Coordinate create2DM(double x, double y, double m) {
        return new Coordinate(x, y, Double.NaN, m);
    }

    /**
     * Create a 3DM Coordinate
     * @param x The x value
     * @param y The y value
     * @param z The z value
     * @param m The m value
     * @return A 3DM Coordinate
     */
    public static Coordinate create3DM(double x, double y, double z, double m) {
        return new Coordinate(x, y, z, m);
    }

    /**
     * Get the x value
     * @return The x value
     */
    public double getX() {
        return x;
    }

    /**
     * Get the y value
     * @return The y value
     */
    public double getY() {
        return y;
    }

    /**
     * Get the z value
     * @return The z value
     */
    public double getZ() {
        return z;
    }

    /**
     * Get the m vaue
     * @return The m value
     */
    public double getM() {
        return m;
    }

    /**
     * Get the Dimension
     * @return The Dimension
     */
    public Dimension getDimension() {
        if (!Double.isNaN(m) && !Double.isNaN(z)) {
            return Dimension.ThreeMeasured;
        } else if (!Double.isNaN(z)) {
            return Dimension.Three;
        } else if (!Double.isNaN(m)) {
            return Dimension.TwoMeasured;
        } else {
            return Dimension.Two;
        }
    }

    /**
     * Determine if the Coordinate is empty (x and y values of NaN)
     * @return Whether this Coordinate is empty
     */
    public boolean isEmpty() {
        return (Double.isNaN(x) && Double.isNaN(y));
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Coordinate that = (Coordinate) o;

        if (Double.compare(that.m, m) != 0) return false;
        if (Double.compare(that.x, x) != 0) return false;
        if (Double.compare(that.y, y) != 0) return false;
        if (Double.compare(that.z, z) != 0) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        temp = Double.doubleToLongBits(x);
        result = (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(y);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(z);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(m);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    @Override
    public String toString() {
        String str = "Coordinate { x = " + x + " y = " + y;
        if (!Double.isNaN(z)) {
            str = str + " z = " + z;
        }
        if (!Double.isNaN(m)) {
            str = str + " m = " + m;
        }
        str = str + " }";
        return str;
    }

    /**
     * A Builder for constructing immutable Coordinates
     */
    public static class Builder {

        private double x = Double.NaN;

        private double y = Double.NaN;

        private double z = Double.NaN;

        private double m = Double.NaN;

        public Builder setX(double x) {
            this.x = x;
            return this;
        }

        public Builder setY(double y) {
            this.y = y;
            return this;
        }

        public Builder setM(double m) {
            this.m = m;
            return this;
        }

        public Builder setZ(double z) {
            this.z = z;
            return this;
        }

        public Coordinate build() {
            Coordinate coordinate;
            // Three Measured
            if (!Double.isNaN(x) && !Double.isNaN(y) && !Double.isNaN(z) && !Double.isNaN(m)) {
                coordinate = new Coordinate(x, y, z, m);
            }
            // Three
            else if (!Double.isNaN(x) && !Double.isNaN(y) && !Double.isNaN(z) && Double.isNaN(m)) {
                coordinate = new Coordinate(x, y, z, Double.NaN);
            }
            // Two Measured
            else if (!Double.isNaN(x) && !Double.isNaN(y) && Double.isNaN(z) && !Double.isNaN(m)) {
                coordinate = new Coordinate(x, y, Double.NaN, m);
            }
            // Two
            else if (!Double.isNaN(x) && !Double.isNaN(y) && Double.isNaN(z) && Double.isNaN(m)) {
                coordinate = new Coordinate(x, y, Double.NaN, Double.NaN);
            }
            // Empty
            else {
                coordinate = new Coordinate(Double.NaN, Double.NaN, Double.NaN, Double.NaN);
            }
            reset();
            return coordinate;
        }

        private void reset() {
            this.x = Double.NaN;
            this.y = Double.NaN;
            this.z = Double.NaN;
            this.m = Double.NaN;
        }

    }

}