Package org.geotools.geometry.jts

Source Code of org.geotools.geometry.jts.ReferencedEnvelope3D

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2005-2008, Open Source Geospatial Foundation (OSGeo)
*
*    This library is free software; you can redistribute it and/or
*    modify it under the terms of the GNU Lesser General Public
*    License as published by the Free Software Foundation;
*    version 2.1 of the License.
*
*    This library 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
*    Lesser General Public License for more details.
*/
package org.geotools.geometry.jts;

import org.geotools.geometry.DirectPosition3D;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Errors;
import org.opengis.geometry.BoundingBox;
import org.opengis.geometry.BoundingBox3D;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.CoordinateOperation;
import org.opengis.referencing.operation.CoordinateOperationFactory;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;

/**
* A 3D envelope associated with a
* {@linkplain CoordinateReferenceSystem coordinate reference system}. In
* addition, this JTS envelope also implements the GeoAPI
* {@linkplain org.opengis.geometry.Envelope envelope} interface
* for interoperability with GeoAPI.
*
*
* @source $URL$
* @version $Id$
* @author Niels Charlier
*
*/
public class ReferencedEnvelope3D extends ReferencedEnvelope implements BoundingBox3D {
 
    /**
     * Serial number for compatibility with different versions.
     */
    private static final long serialVersionUID = -3188702602373537163L;
 
  /**
   * Test the point q to see whether it intersects the Envelope defined by
   * p1-p2
   *
   * @param p1
   *            one extremal point of the envelope
   * @param p2
   *            another extremal point of the envelope
   * @param q
   *            the point to test for intersection
   * @return <code>true</code> if q intersects the envelope p1-p2
   */
  public static boolean intersects(Coordinate p1, Coordinate p2, Coordinate q) {
    // OptimizeIt shows that Math#min and Math#max here are a bottleneck.
    // Replace with direct comparisons. [Jon Aquino]
    if (((q.x >= (p1.x < p2.x ? p1.x : p2.x)) && (q.x <= (p1.x > p2.x ? p1.x
        : p2.x)))
        && ((q.y >= (p1.y < p2.y ? p1.y : p2.y)) && (q.y <= (p1.y > p2.y ? p1.y
            : p2.y)))) {
      return true;
    }
    return false;
  }

  /**
   * Test the envelope defined by p1-p2 for intersection with the envelope
   * defined by q1-q2
   *
   * @param p1
   *            one extremal point of the envelope P
   * @param p2
   *            another extremal point of the envelope P
   * @param q1
   *            one extremal point of the envelope Q
   * @param q2
   *            another extremal point of the envelope Q
   * @return <code>true</code> if Q intersects P
   */
  public static boolean intersects(Coordinate p1, Coordinate p2,
      Coordinate q1, Coordinate q2) {
    double minq = Math.min(q1.x, q2.x);
    double maxq = Math.max(q1.x, q2.x);
    double minp = Math.min(p1.x, p2.x);
    double maxp = Math.max(p1.x, p2.x);

    if (minp > maxq)
      return false;
    if (maxp < minq)
      return false;

    minq = Math.min(q1.y, q2.y);
    maxq = Math.max(q1.y, q2.y);
    minp = Math.min(p1.y, p2.y);
    maxp = Math.max(p1.y, p2.y);

    if (minp > maxq)
      return false;
    if (maxp < minq)
      return false;
    return true;
  }

  /**
   * the minimum z-coordinate
   */
  private double minz;

  /**
   * the maximum z-coordinate
   */
  private double maxz;

 
  /**
   * Initialize to a null <code>Envelope</code>.
   */
  public void init() {
    setToNull();
  }

  /**
   * Initialize an <code>Envelope</code> for a region defined by maximum and
   * minimum values.
   *
   * @param x1
   *            the first x-value
   * @param x2
   *            the second x-value
   * @param y1
   *            the first y-value
   * @param y2
   *            the second y-value
   * @param z1
   *            the first z-value
   * @param z2
   *            the second z-value
   */
  public void init(double x1, double x2, double y1, double y2, double z1,
      double z2) {
    init(x1, x2, y1, y2);
    if (z1 < z2) {
      minz = z1;
      maxz = z2;
    } else {
      minz = z2;
      maxz = z1;
    }
  }

  /**
   * Initialize an <code>Envelope</code> to a region defined by two
   * Coordinates.
   *
   * @param p1
   *            the first Coordinate
   * @param p2
   *            the second Coordinate
   */
  public void init(Coordinate p1, Coordinate p2) {
    init(p1.x, p2.x, p1.y, p2.y, p1.z, p2.z);
  }

  /**
   * Initialize an <code>Envelope</code> to a region defined by a single
   * Coordinate.
   *
   * @param p
   *            the coordinate
   */
  public void init(Coordinate p) {
    init(p.x, p.x, p.y, p.y, p.z, p.z);
  }
  @Override
  public void init(Envelope env) {
      super.init(env);
      if( env instanceof BoundingBox3D ){
          this.minz = ((BoundingBox3D)env).getMinZ();
          this.maxz = ((BoundingBox3D)env).getMaxZ();
      }
  }
 
  /**
   * Initialize an <code>Envelope</code> from an existing 3D Envelope.
   *
   * @param env
   *            the 3D Envelope to initialize from
   */
  public void init(ReferencedEnvelope3D env) {
    super.init( (Envelope) env);
    this.minz = env.minz;
    this.maxz = env.maxz;
  }

  /**
   * Makes this <code>Envelope</code> a "null" envelope, that is, the envelope
   * of the empty geometry.
   */
  public void setToNull() {
    super.setToNull();
    minz = 0;
    maxz = -1;
  }

  /**
   * Returns the difference between the maximum and minimum z values.
   *
   * @return max z - min z, or 0 if this is a null <code>Envelope</code>
   */
  public double getDepth() {
    if (isNull()) {
      return 0;
    }
    return maxz - minz;
  }

  /**
   * Returns the <code>Envelope</code>s minimum z-value. min z > max z
   * indicates that this is a null <code>Envelope</code>.
   *
   * @return the minimum z-coordinate
   */
  public double getMinZ() {
    return minz;
  }

  /**
   * Returns the <code>Envelope</code>s maximum z-value. min z > max z
   * indicates that this is a null <code>Envelope</code>.
   *
   * @return the maximum z-coordinate
   */
  public double getMaxZ() {
    return maxz;
  }

  /**
   * Gets the volume of this envelope.
   *
   * @return the volume of the envelope
   * @return 0.0 if the envelope is null
   */
  public double getVolume() {
    return getWidth() * getHeight() * getDepth();
  }

  /**
   * Gets the minimum extent of this envelope across all three dimensions.
   *
   * @return the minimum extent of this envelope
   */
  public double minExtent() {
    if (isNull())
      return 0.0;
    return Math.min(getWidth(), Math.min(getHeight(), getDepth()));
  }

  /**
   * Gets the maximum extent of this envelope across both dimensions.
   *
   * @return the maximum extent of this envelope
   */
  public double maxExtent() {
    if (isNull())
      return 0.0;
    return Math.max(getWidth(), Math.max(getHeight(), getDepth()));
  }

  /**
   * Enlarges this <code>Envelope</code> so that it contains the given
   * {@link Coordinate}. Has no effect if the point is already on or within
   * the envelope.
   *
   * @param p
   *            the Coordinate to expand to include
   */
  public void expandToInclude(Coordinate p) {
    expandToInclude(p.x, p.y, p.z);
  }

  /**
   * Expands this envelope by a given distance in all directions. Both
   * positive and negative distances are supported.
   *
   * @param distance
   *            the distance to expand the envelope
   */
  public void expandBy(double distance) {
    expandBy(distance, distance, distance);
  }

  /**
   * Expands this envelope by a given distance in all directions. Both
   * positive and negative distances are supported.
   *
   * @param deltaX
   *            the distance to expand the envelope along the the X axis
   * @param deltaY
   *            the distance to expand the envelope along the the Y axis
   */
  public void expandBy(double deltaX, double deltaY, double deltaZ) {
    if (isNull())
      return;

    minz -= deltaZ;
    maxz += deltaZ;
    expandBy(deltaX, deltaY);

    // check for envelope disappearing
    if (minz > maxz)
      setToNull();
  }

  /**
   * Enlarges this <code>Envelope</code> so that it contains the given point.
   * Has no effect if the point is already on or within the envelope.
   *
   * @param x
   *            the value to lower the minimum x to or to raise the maximum x
   *            to
   * @param y
   *            the value to lower the minimum y to or to raise the maximum y
   *            to
   * @param z
   *            the value to lower the minimum z to or to raise the maximum z
   *            to
   */
  public void expandToInclude(double x, double y, double z) {
    if (isNull()) {     
      expandToInclude(x,y);
      minz = z;
      maxz = z;
    } else {
      expandToInclude(x,y);
      if (z < minz) {
        minz = z;
      }
      if (z > maxz) {
        maxz = z;
      }
    }
  }
  @Override
        public void expandToInclude(DirectPosition pt ){
            double x = pt.getOrdinate(0);
            double y = pt.getOrdinate(1);
            double z = pt.getDimension()>=3 ? pt.getOrdinate(2) : Double.NaN;
            expandToInclude(x,y,z);
        }
  /**
   * Translates this envelope by given amounts in the X and Y direction.
   *
   * @param transX
   *            the amount to translate along the X axis
   * @param transY
   *            the amount to translate along the Y axis
   * @param transZ
   *            the amount to translate along the Z axis  
   */
  public void translate(double transX, double transY, double transZ) {
    if (isNull()) {
      return;
    }
    init(getMinX() + transX, getMaxX() + transX, getMinY() + transY,
        getMaxY() + transY, getMinZ() + transZ,  getMaxZ() + transZ);
  }

  /**
   * Computes the coordinate of the centre of this envelope (as long as it is
   * non-null
   *
   * @return the centre coordinate of this envelope <code>null</code> if the
   *         envelope is null
   */
  public Coordinate centre() {
    if (isNull())
      return null;
    return new Coordinate((getMinX() + getMaxX()) / 2.0,
        (getMinY() + getMaxY()) / 2.0, (getMinZ() + getMaxZ()) / 2.0);
  }

  /**
   * Check if the region defined by <code>other</code> overlaps (intersects)
   * the region of this <code>Envelope</code>.
   *
   * @param other
   *            the <code>Envelope</code> which this <code>Envelope</code> is
   *            being checked for overlapping
   * @return <code>true</code> if the <code>Envelope</code>s overlap
   */
  public boolean intersects(ReferencedEnvelope3D other) {
    if (isNull() || other.isNull()) {
      return false;
    }
    return super.intersects((Envelope) other) && !(other.minz > maxz || other.maxz < minz);
  }

  /**
   * @deprecated Use #intersects instead. In the future, #overlaps may be
   *             changed to be a true overlap check; that is, whether the
   *             intersection is two-dimensional.
   */
  public boolean overlaps(ReferencedEnvelope3D other) {
    return intersects(other);
  }

  /**
   * Check if the point <code>p</code> overlaps (lies inside) the region of
   * this <code>Envelope</code>.
   *
   * @param p
   *            the <code>Coordinate</code> to be tested
   * @return <code>true</code> if the point overlaps this
   *         <code>Envelope</code>
   */
  public boolean intersects(Coordinate p) {
    return intersects(p.x, p.y, p.z);
  }

  /**
   * @deprecated Use #intersects instead.
   */
  public boolean overlaps(Coordinate p) {
    return intersects(p);
  }

  /**
   * Check if the point <code>(x, y)</code> overlaps (lies inside) the region
   * of this <code>Envelope</code>.
   *
   * @param x
   *            the x-ordinate of the point
   * @param y
   *            the y-ordinate of the point
   * @param z
   *            the z-ordinate of the point
   * @return <code>true</code> if the point overlaps this
   *         <code>Envelope</code>
   */
  public boolean intersects(double x, double y, double z) {
    if (isNull())
      return false;
    return intersects(x,y) && !(z > maxz || z > maxz);
  }

  /**
   * @deprecated Use #intersects instead.
   */
  public boolean overlaps(double x, double y, double z) {
    return intersects(x, y, z);
  }

  /**
   * Tests if the given point lies in or on the envelope.
   * <p>
   * Note that this is <b>not</b> the same definition as the SFS
   * <tt>contains</tt>, which would exclude the envelope boundary.
   *
   * @param p
   *            the point which this <code>Envelope</code> is being checked
   *            for containing
   * @return <code>true</code> if the point lies in the interior or on the
   *         boundary of this <code>Envelope</code>.
   *
   * @see #covers(Coordinate)
   */
  public boolean contains(Coordinate p) {
    return covers(p);
  }

  /**
   * Tests if the given point lies in or on the envelope.
   * <p>
   * Note that this is <b>not</b> the same definition as the SFS
   * <tt>contains</tt>, which would exclude the envelope boundary.
   *
   * @param x
   *            the x-coordinate of the point which this <code>Envelope</code>
   *            is being checked for containing
   * @param y
   *            the y-coordinate of the point which this <code>Envelope</code>
   *            is being checked for containing
   * @return <code>true</code> if <code>(x, y)</code> lies in the interior or
   *         on the boundary of this <code>Envelope</code>.
   *
   * @see #covers(double, double)
   */
  public boolean contains(double x, double y, double z) {
    return covers(x, y, z);
  }

  /**
   * Tests if the given point lies in or on the envelope.
   *
   * @param x
   *            the x-coordinate of the point which this <code>Envelope</code>
   *            is being checked for containing
   * @param y
   *            the y-coordinate of the point which this <code>Envelope</code>
   *            is being checked for containing
   * @return <code>true</code> if <code>(x, y)</code> lies in the interior or
   *         on the boundary of this <code>Envelope</code>.
   */
  public boolean covers(double x, double y, double z) {
    if (isNull())
      return false;
    return covers(x,y) && z >= minz && z <= maxz;
  }

  /**
   * Tests if the given point lies in or on the envelope.
   *
   * @param p
   *            the point which this <code>Envelope</code> is being checked
   *            for containing
   * @return <code>true</code> if the point lies in the interior or on the
   *         boundary of this <code>Envelope</code>.
   */
  public boolean covers(Coordinate p) {
    return covers(p.x, p.y, p.z);
  }

  /**
   * Tests if the <code>Envelope other</code> lies wholely inside this
   * <code>Envelope</code> (inclusive of the boundary).
   *
   * @param other
   *            the <code>Envelope</code> to check
   * @return true if this <code>Envelope</code> covers the <code>other</code>
   */
  public boolean covers(ReferencedEnvelope3D other) {
    if (isNull() || other.isNull()) {
      return false;
    }
    return super.covers(other)
        && other.getMinZ() >= minz && other.getMaxZ() <= maxz;
  }

  /**
   * Computes the distance between this and another <code>Envelope</code>. The
   * distance between overlapping Envelopes is 0. Otherwise, the distance is
   * the Euclidean distance between the closest points.
   */
  public double distance(ReferencedEnvelope3D env) {
    if (intersects(env))
      return 0;

    double dx = 0.0;
    if (getMaxX() < env.getMinX())
      dx = env.getMinX() - getMaxX();
    else if (getMinX() > env.getMaxX())
      dx = getMinX() - env.getMaxX();

    double dy = 0.0;
    if (getMaxY() < env.getMinY())
      dy = env.getMinY() - getMaxY();
    else if (getMinY() > env.getMaxY())
      dy = getMinY() - env.getMaxY();
   
    double dz = 0.0;
    if (maxz < env.minz)
      dz = env.minz - maxz;
    else if (minz > env.maxz)
      dy = minz - env.maxz;

    // if either is zero, the envelopes overlap either vertically or
    // horizontally
    if (dx == 0.0 && dz == 0)
      return dy;
    if (dy == 0.0 && dz ==0)
      return dx;
    if (dx == 0 && dy == 0)
      return dz;
    return Math.sqrt(dx * dx + dy * dy + dz * dz);
  }
 
  //---------------------------------------------------------------------------------------------------------------
   
    /** A ReferencedEnvelope containing "everything" */
    public static ReferencedEnvelope3D EVERYTHING = new ReferencedEnvelope3D(Double.NEGATIVE_INFINITY,
            Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
            Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, null) {
        private static final long serialVersionUID = -3188702602373537164L;

        public boolean contains(BoundingBox bbox) {
            return true;
        }

        public boolean contains(Coordinate p) {
            return true;
        }

        public boolean contains(DirectPosition pos) {
            return true;
        }

        public boolean contains(double x, double y, double z) {
            return true;
        }

        public boolean isEmpty() {
            return false;
        }

        public boolean isNull() {
            return true;
        }
       
        public double getArea() {
            //return super.getArea();
            return Double.POSITIVE_INFINITY;
        }
       
        public double getVolume() {
            //return super.getArea();
            return Double.POSITIVE_INFINITY;
        }
       
        public void setBounds(BoundingBox3D arg0) {
            throw new IllegalStateException("Cannot modify ReferencedEnvelope.EVERYTHING");
        }
        public Coordinate centre() {
            return new Coordinate();
        }
        public void setToNull() {
            // um ignore this as we are already "null"
        }
        public boolean equals(Object obj) {
            if( obj == EVERYTHING ){
                return true;
            }
            if( obj instanceof ReferencedEnvelope3D ){
                ReferencedEnvelope3D other = (ReferencedEnvelope3D) obj;
                if( other.crs != EVERYTHING.crs ) return false;
                if( other.getMinX() != EVERYTHING.getMinX() ) return false;
                if( other.getMinY() != EVERYTHING.getMinY() ) return false;
                if( other.getMinZ() != EVERYTHING.getMinZ() ) return false;
                if( other.getMaxX() != EVERYTHING.getMaxX() ) return false;
                if( other.getMaxY() != EVERYTHING.getMaxY() ) return false;
                if( other.getMaxZ() != EVERYTHING.getMaxZ() ) return false;
               
                return true;
            }
            return super.equals(obj);
        }
       
        public String toString() {
            return "ReferencedEnvelope.EVERYTHING";
        }
    };

    /**
     * Creates a null envelope with a null coordinate reference system.
     */
    public ReferencedEnvelope3D() {
        this((CoordinateReferenceSystem) null);
    }

    /**
     * Creates a null envelope with the specified coordinate reference system.
     *
     * @param crs The coordinate reference system.
     * @throws MismatchedDimensionException if the CRS dimension is not valid.
     */
    public ReferencedEnvelope3D(CoordinateReferenceSystem crs)
        throws MismatchedDimensionException {
        this.crs = crs;
        checkCoordinateReferenceSystemDimension();
    }

    /**
     * Creates an envelope for a region defined by maximum and minimum values.
     *
     * @param x1  The first x-value.
     * @param x2  The second x-value.
     * @param y1  The first y-value.
     * @param y2  The second y-value.
     * @param z1  The first y-value.
     * @param z2  The second y-value.
     * @param crs The coordinate reference system.
     *
     * @throws MismatchedDimensionException if the CRS dimension is not valid.
     */
    public ReferencedEnvelope3D(final double x1, final double x2, final double y1, final double y2,
        final double z1, final double z2, final CoordinateReferenceSystem crs) throws MismatchedDimensionException {
        init(x1, x2, y1, y2, z1, z2);
        this.crs = crs;
        checkCoordinateReferenceSystemDimension();
    }

    /**
     * Creates a new envelope from an existing envelope.
     *
     * @param envelope The envelope to initialize from
     * @throws MismatchedDimensionException if the CRS dimension is not valid.
     *
     */
    public ReferencedEnvelope3D(final ReferencedEnvelope3D envelope)
        throws MismatchedDimensionException {
        init(envelope);
        crs = envelope.getCoordinateReferenceSystem();
        checkCoordinateReferenceSystemDimension();
    }

    /**
     * Creates a new envelope from an existing bounding box.
     *
     * @param bbox The bounding box to initialize from.
     * @throws MismatchedDimensionException if the CRS dimension is not valid.
     *
     */
    public ReferencedEnvelope3D(final BoundingBox3D bbox) throws MismatchedDimensionException {
        this(bbox.getMinX(), bbox.getMaxX(), bbox.getMinY(), bbox.getMaxY(), bbox.getMinZ(), bbox.getMaxZ(),
            bbox.getCoordinateReferenceSystem());
    }

    /**
     * Creates a new envelope from an existing JTS envelope.
     *
     * @param envelope The envelope to initialize from.
     * @param crs The coordinate reference system.
     * @throws MismatchedDimensionExceptionif the CRS dimension is not valid.
     */
    public ReferencedEnvelope3D(final Envelope envelope, final CoordinateReferenceSystem crs)
        throws MismatchedDimensionException {
        super(envelope, crs );
        if( envelope instanceof ReferencedEnvelope3D ){
            this.minz = ((ReferencedEnvelope3D)envelope).getMinZ();
            this.maxz = ((ReferencedEnvelope3D)envelope).getMaxZ();
        }
    }
   
    /**
     * Creates a new envelope from an existing OGC envelope.
     *
     * @param envelope The envelope to initialize from.
     * @throws MismatchedDimensionException if the CRS dimension is not valid.
     *
     */
    public ReferencedEnvelope3D(final org.opengis.geometry.Envelope envelope)
        throws MismatchedDimensionException {
        init(envelope.getMinimum(0), envelope.getMaximum(0), envelope.getMinimum(1),
            envelope.getMaximum(1), envelope.getMinimum(2),
            envelope.getMaximum(2));
        this.crs = envelope.getCoordinateReferenceSystem();
        checkCoordinateReferenceSystemDimension();
    }

    /**
     * Creates a new envelope from an existing JTS envelope.
     *
     * @param envelope The envelope to initialize from.
     * @param crs The coordinate reference system.
     * @throws MismatchedDimensionExceptionif the CRS dimension is not valid.
     */
    public ReferencedEnvelope3D(final ReferencedEnvelope3D envelope, final CoordinateReferenceSystem crs)
        throws MismatchedDimensionException {
        init(envelope);
        this.crs = crs;
        checkCoordinateReferenceSystemDimension();
    }

    /**
     * Sets this envelope to the specified bounding box.
     */
    public void init(BoundingBox bounds) {
        init(bounds.getMinimum(0), bounds.getMaximum(0), bounds.getMinimum(1),
            bounds.getMaximum(1), bounds.getMinimum(2),
            bounds.getMaximum(2));
        this.crs = bounds.getCoordinateReferenceSystem();
    }

    /**
     * Returns the specified bounding box as a JTS envelope.
     */
    private static ReferencedEnvelope3D getJTSEnvelope(final BoundingBox3D bbox) {
        if( bbox == null ){
            throw new NullPointerException("Provided bbox envelope was null");
        }
        if (bbox instanceof ReferencedEnvelope3D) {
            return (ReferencedEnvelope3D) bbox;
        }       
        return new ReferencedEnvelope3D(bbox);
    }
  
    /**
     * Returns the number of dimensions.
     */
    public int getDimension() {
        return 3;
    }

    /**
     * Returns the minimal ordinate along the specified dimension.
     */
    public double getMinimum(final int dimension) {
        switch (dimension) {
        case 0:
            return getMinX();

        case 1:
            return getMinY();
           
        case 2:
            return getMinZ();

        default:
            throw new IndexOutOfBoundsException(String.valueOf(dimension));
        }
    }

    /**
     * Returns the maximal ordinate along the specified dimension.
     */
    public double getMaximum(final int dimension) {
        switch (dimension) {
        case 0:
            return getMaxX();

        case 1:
            return getMaxY();
           
           
        case 2:
            return getMaxZ();

        default:
            throw new IndexOutOfBoundsException(String.valueOf(dimension));
        }
    }

    /**
     * Returns the center ordinate along the specified dimension.
     */
    public double getMedian(final int dimension) {
        switch (dimension) {
        case 0:
            return 0.5 * (getMinX() + getMaxX());

        case 1:
            return 0.5 * (getMinY() + getMaxY());

        case 2:
            return 0.5 * (getMinZ() + getMaxZ());
           
        default:
            throw new IndexOutOfBoundsException(String.valueOf(dimension));
        }
    }


    /**
     * Returns the envelope length along the specified dimension. This length is
     * equals to the maximum ordinate minus the minimal ordinate.
     */
    public double getSpan(final int dimension) {
        switch (dimension) {
        case 0:
            return getWidth();

        case 1:
            return getHeight();
           
        case 2:
            return getDepth();

        default:
            throw new IndexOutOfBoundsException(String.valueOf(dimension));
        }
    }

    /**
     * A coordinate position consisting of all the minimal ordinates for each
     * dimension for all points within the {@code Envelope}.
     */
    public DirectPosition getLowerCorner() {
        return new DirectPosition3D(crs, getMinX(), getMinY(), getMinZ());
    }

    /**
     * A coordinate position consisting of all the maximal ordinates for each
     * dimension for all points within the {@code Envelope}.
     */
    public DirectPosition getUpperCorner() {
        return new DirectPosition3D(crs, getMaxX(), getMaxY(), getMinZ() );
    }

    /**
     * Returns {@code true} if lengths along all dimension are zero.
     *
     */
    public boolean isEmpty() {
        return super.isNull();
    }

    /**
     * Returns {@code true} if the provided location is contained by this bounding box.
     *
     */
    public boolean contains(DirectPosition pos) {
        ensureCompatibleReferenceSystem( pos );
        return contains(pos.getOrdinate(0), pos.getOrdinate(1), pos.getOrdinate(2));
    }

    /**
     * Returns {@code true} if the provided bounds are contained by this bounding box.
     *
     */
    public boolean contains(final BoundingBox3D bbox) {
        ensureCompatibleReferenceSystem(bbox);

        return covers(getJTSEnvelope(bbox));
    }

    /**
     * Check if this bounding box intersects the provided bounds.
     *
     */
    public boolean intersects(final BoundingBox3D bbox) {
        ensureCompatibleReferenceSystem(bbox);

        return intersects(getJTSEnvelope(bbox));
    }
   
    /**
   * Computes the intersection of two {@link Envelope}s.
   *
   * @param env
   *            the envelope to intersect with
   * @return a new Envelope representing the intersection of the envelopes
   *         (this will be the null envelope if either argument is null, or
   *         they do not intersect
   */
  public ReferencedEnvelope3D intersection(ReferencedEnvelope3D env) {
    ensureCompatibleReferenceSystem( env );
   
    if (isNull() || env.isNull() || !intersects( env))
      return new ReferencedEnvelope3D();

    double intMinX = getMinX() > env.getMinX() ? getMinX() : env.getMinX();
    double intMinY = getMinY() > env.getMinY() ? getMinY() : env.getMinY();
    double intMinZ = minz > env.minz ? minz : env.minz;
    double intMaxX = getMaxX() < env.getMaxX() ? getMaxX() : env.getMaxX();
    double intMaxY = getMaxY() < env.getMaxY() ? getMaxY() : env.getMaxY();
    double intMaxZ = maxz < env.maxz ? maxz : env.maxz;

    return new ReferencedEnvelope3D(intMinX, intMaxX, intMinY, intMaxY, intMinZ, intMaxZ, env.getCoordinateReferenceSystem());
  }
    
    /**
     * Include the provided bounding box, expanding as necessary.
     *
     */
    public void include(final BoundingBox3D bbox) {
        if( crs == null ){
            this.crs = bbox.getCoordinateReferenceSystem();
        }
        expandToInclude(getJTSEnvelope(bbox));       
    }
   
    /**
   * Enlarges this <code>Envelope</code> so that it contains the
   * <code>other</code> Envelope. Has no effect if <code>other</code> is
   * wholly on or within the envelope.
   *
   * @param other
   *            the <code>Envelope</code> to expand to include
   */
  public void expandToInclude(ReferencedEnvelope3D other) {
    ensureCompatibleReferenceSystem( other );
   
    if (other.isNull()) {
      return;
    }
    if (isNull()) {
      super.expandToInclude(other);
      minz = other.getMinZ();
      maxz = other.getMaxZ();
    } else {
      super.expandToInclude(other);
      if (other.minz < minz) {
        minz = other.minz;
      }
      if (other.maxz > maxz) {
        maxz = other.maxz;
      }
    }
  }
   
    /**
     * Include the provided coordinates, expanding as necessary.
     *
     */
    public void include(double x, double y, double z) {
        expandToInclude(x, y, z);
    }

    /**
     * Initialize the bounding box with another bounding box.
     *
     * @since 2.4
     */
    public void setBounds(final BoundingBox3D bbox) {
        ensureCompatibleReferenceSystem(bbox);
        init(getJTSEnvelope(bbox));
    }

    /**
     * Returns a new bounding box which contains the transformed shape of this bounding box.
     * This is a convenience method that delegate its work to the {@link #transform transform}
     * method.
     *
     */
    public BoundingBox toBounds(final CoordinateReferenceSystem targetCRS)
        throws TransformException {
        try {
            return transform(targetCRS, true);
        } catch (FactoryException e) {
            throw new TransformException(e.getLocalizedMessage(), e);
        }
    }

    /**
     * Transforms the referenced envelope to the specified coordinate reference system.
     * <p>
     * This method can handle the case where the envelope contains the North or South pole,
     * or when it cross the &plusmn;180� longitude.
     *
     * @param targetCRS The target coordinate reference system.
     * @param lenient   {@code true} if datum shift should be applied even if there is
     *                  insuffisient information. Otherwise (if {@code false}), an
     *                  exception is thrown in such case.
     * @return The transformed envelope.
     * @throws FactoryException if the math transform can't be determined.
     * @throws TransformException if at least one coordinate can't be transformed.
     *
     * @see CRS#transform(CoordinateOperation, org.opengis.geometry.Envelope)
     */
    public ReferencedEnvelope transform(CoordinateReferenceSystem targetCRS, boolean lenient)
        throws TransformException, FactoryException {
        return transform(targetCRS, lenient, 5);
    }

    /**
     * Transforms the referenced envelope to the specified coordinate reference system
     * using the specified amount of points.
     * <p>
     * This method can handle the case where the envelope contains the North or South pole,
     * or when it cross the &plusmn;180� longitude.
     *
     * @param targetCRS The target coordinate reference system.
     * @param lenient   {@code true} if datum shift should be applied even if there is
     *                  insuffisient information. Otherwise (if {@code false}), an
     *                  exception is thrown in such case.
     * @param numPointsForTransformation The number of points to use for sampling the envelope.
     * @return The transformed envelope.
     * @throws FactoryException if the math transform can't be determined.
     * @throws TransformException if at least one coordinate can't be transformed.
     *
     * @see CRS#transform(CoordinateOperation, org.opengis.geometry.Envelope)
     *
     */
    public ReferencedEnvelope transform(final CoordinateReferenceSystem targetCRS,
        final boolean lenient, final int numPointsForTransformation)
        throws TransformException, FactoryException {
      //TODO: implement 3D behaviour for this method
      //falls back on 2D behaviour (3rd coordinate is preserved!)
     
        if( crs == null ){
            if( isEmpty() ){
                // We don't have a CRS yet because we are still empty, being empty is
                // something we can represent in the targetCRS
                return new ReferencedEnvelope3D(targetCRS);
            }
            else {
                // really this is a the code that created this ReferencedEnvelope
                throw new NullPointerException("Unable to transform referenced envelope, crs has not yet been provided.");
            }
        }
        if( getDimension() != targetCRS.getCoordinateSystem().getDimension()){
            if( lenient ){
                return JTS.transformTo2D(this, targetCRS, lenient, numPointsForTransformation );
            }
            else {
                throw new MismatchedDimensionException(Errors.format(
                        ErrorKeys.MISMATCHED_DIMENSION_$3, crs.getName().getCode(),
                        new Integer(getDimension()), new Integer(targetCRS.getCoordinateSystem().getDimension())));
            }
        }
        // Gets a first estimation using an algorithm capable to take singularity in account
        // (North pole, South pole, 180� longitude). We will expand this initial box later.
       
        CoordinateOperationFactory coordinateOperationFactory = CRS.getCoordinateOperationFactory(lenient);

        final CoordinateOperation operation = coordinateOperationFactory.createOperation(crs, targetCRS);
        final GeneralEnvelope transformed = CRS.transform(operation, this);
        transformed.setCoordinateReferenceSystem(targetCRS);

       // Now expands the box using the usual utility methods.
       
        final ReferencedEnvelope3D target = new ReferencedEnvelope3D(transformed);
        final MathTransform transform = operation.getMathTransform();
        JTS.transform(this, target, transform, numPointsForTransformation);
        //smuggle back third coordinate
        target.expandToInclude(0,0,this.minz);
        target.expandToInclude(0,0,this.maxz);
       
        return target;
    }
   
   /**
     * Returns a hash value for this envelope. This value need not remain
     * consistent between different implementations of the same class.
     */
    @Override
    public int hashCode() {
      // Algorithm from Effective Java by Joshua Bloch [Jon Aquino]
      int result = super.hashCode();
      result = 37 * result + Coordinate.hashCode(minz);
      result = 37 * result + Coordinate.hashCode(maxz);
           
        int code = result ^ (int) serialVersionUID;
        if (crs != null) {
            code ^= crs.hashCode();
        }
        return code;
    }

    /**
     * Compares the specified object with this envelope for equality.
     */
    @Override
    public boolean equals(final Object other) {
     
      if (!(other instanceof ReferencedEnvelope3D)) {
      return false;
    }
    ReferencedEnvelope3D otherEnvelope = (ReferencedEnvelope3D) other;
    if (isNull()) {
      return otherEnvelope.isNull();
    }
    if (super.equals(other)
        && minz == otherEnvelope.getMinZ()
        && minz == otherEnvelope.getMinZ()) {
            final CoordinateReferenceSystem otherCRS = (other instanceof ReferencedEnvelope3D)
                ? ((ReferencedEnvelope3D) other).crs : null;

            return CRS.equalsIgnoreMetadata(crs, otherCRS);
        }
        return false;
    }

    /**
     * Compare the bounds of this envelope with those of another.
     * <p>
     * Note: in this test:
     * <ul>
     * <li> the coordinate reference systems of the envelopes are not examined
     * <li> only the first three dimensions of the envelopes are compared
     * <li> it is assumed that each dimension equates to the same axis for both envelopes
     * </ul>
     *
     * @param other other envelope
     * @param eps a small tolerance factor (e.g. 1.0e-6d) which will be scaled
     *        relative to this envlope's width and height
     *
     * @return true if all bounding coordinates are equal within the set tolerance;
     *         false otherwise
     */

    public boolean boundsEquals3D(final org.opengis.geometry.Envelope other, double eps) {
        eps *= 0.5*(getWidth() + getHeight());

        double[] delta = new double[4];
        delta[0] = getMinimum(0) - other.getMinimum(0);
        delta[1] = getMaximum(0) - other.getMaximum(0);
        delta[2] = getMinimum(1) - other.getMinimum(1);
        delta[3] = getMaximum(1) - other.getMaximum(1);
        delta[4] = getMinimum(2) - other.getMinimum(2);
        delta[5] = getMaximum(2) - other.getMaximum(2);

        for (int i = 0; i < delta.length; i++) {
            /*
             * As per Envelope2D#boundsEquals we use ! here to
             * catch any NaN values
             */
            if (!(Math.abs(delta[i]) <= eps)) {
                return false;
            }
        }
        return true;
    }
   
  @Override
  public void include(double x, double y) {
     super.expandToInclude(x, y);
   
  }
}
TOP

Related Classes of org.geotools.geometry.jts.ReferencedEnvelope3D

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.