org.geotoolkit.geometry.AbstractEnvelope Maven / Gradle / Ivy
Show all versions of geotk-referencing Show documentation
/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2004-2011, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2011, Geomatys
*
* 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.geotoolkit.geometry;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.geotoolkit.util.Utilities;
import org.geotoolkit.resources.Errors;
import org.geotoolkit.referencing.CRS;
import static org.geotoolkit.util.ArgumentChecks.ensureNonNull;
import static org.geotoolkit.util.Strings.trimFractionalPart;
/**
* Base class for {@linkplain Envelope envelope} implementations. This base class
* provides default implementations for {@link #toString()}, {@link #equals(Object)}
* and {@link #hashCode()} methods.
*
* This class do not holds any state. The decision to implement {@link java.io.Serializable}
* or {@link org.geotoolkit.util.Cloneable} interfaces is left to implementors.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 3.10
*
* @since 2.4
* @module
*/
public abstract class AbstractEnvelope implements Envelope {
/**
* Constructs an envelope.
*/
protected AbstractEnvelope() {
}
/**
* Returns {@code true} if at least one of the specified CRS is null, or both CRS are equals.
* This special processing for {@code null} values is different from the usual contract of an
* {@code equals} method, but allow to handle the case where the CRS is unknown.
*/
static boolean equalsIgnoreMetadata(final CoordinateReferenceSystem crs1,
final CoordinateReferenceSystem crs2)
{
return (crs1 == null) || (crs2 == null) || CRS.equalsIgnoreMetadata(crs1, crs2);
}
/**
* Returns the common CRS of specified points.
*
* @param minDP The first position.
* @param maxDP The second position.
* @return Their common CRS, or {@code null} if none.
* @throws MismatchedReferenceSystemException if the two positions don't use the same CRS.
*/
static CoordinateReferenceSystem getCoordinateReferenceSystem(final DirectPosition minDP,
final DirectPosition maxDP)
{
final CoordinateReferenceSystem crs1 = minDP.getCoordinateReferenceSystem();
final CoordinateReferenceSystem crs2 = maxDP.getCoordinateReferenceSystem();
if (crs1 == null) {
return crs2;
} else {
if (crs2 != null && !crs1.equals(crs2)) {
throw new IllegalArgumentException(
Errors.format(Errors.Keys.MISMATCHED_COORDINATE_REFERENCE_SYSTEM));
}
return crs1;
}
}
/**
* A coordinate position consisting of all the {@linkplain #getMinimum minimal ordinates}.
* The default implementation returns a direct position backed by this envelope, so changes
* in this envelope will be immediately reflected in the direct position.
*
* @return The lower corner.
*/
@Override
public DirectPosition getLowerCorner() {
return new LowerCorner();
}
/**
* A coordinate position consisting of all the {@linkplain #getMaximum maximal ordinates}.
* The default implementation returns a direct position backed by this envelope, so changes
* in this envelope will be immediately reflected in the direct position.
*
* @return The upper corner.
*/
@Override
public DirectPosition getUpperCorner() {
return new UpperCorner();
}
/**
* Formats this envelope in the Well Known Text (WKT) format. The output is like
* below, where n is the {@linkplain #getDimension() number of dimensions}:
*
*
{@code BOX}n{@code D(}{@linkplain #getLowerCorner() lower corner}{@code ,}
* {@linkplain #getUpperCorner() upper corner}{@code )}
*
* The output of this method can be {@linkplain GeneralEnvelope#GeneralEnvelope(String) parsed}
* by the {@link GeneralEnvelope} constructor.
*/
@Override
public String toString() {
return toString(this);
}
/**
* Formats a {@code BOX} element from an envelope. This method formats the given envelope in
* the Well Known Text (WKT) format. The output is like below, where n
* is the {@linkplain Envelope#getDimension() number of dimensions}:
*
* {@code BOX}n{@code D(}{@linkplain #getLowerCorner() lower corner}{@code ,}
* {@linkplain #getUpperCorner() upper corner}{@code )}
*
* The output of this method can be {@linkplain GeneralEnvelope#GeneralEnvelope(String) parsed}
* by the {@link GeneralEnvelope} constructor.
*
* @param envelope The envelope to format.
* @return The envelope as a {@code BOX2D} or {@code BOX3D} in WKT format.
*
* @see GeneralEnvelope#GeneralEnvelope(String)
* @see org.geotoolkit.measure.CoordinateFormat
* @see org.geotoolkit.io.wkt
*
* @since 3.09
*
* @deprecated Moved to {@link Envelopes#toWKT(Envelope)}.
*/
// After deprecation cycle, don't remove this method. Just make it package-privated.
@Deprecated
public static String toString(final Envelope envelope) {
final int dimension = envelope.getDimension();
final StringBuilder buffer = new StringBuilder("BOX").append(dimension).append("D(");
for (int i=0; iWell Known Text (WKT) format. This is provided as an
* alternative to the {@code BOX} element formatted by {@link #toString(Envelope)}, because
* the {@code BOX} element is usually not considered a geometry while {@code POLYGON} is.
*
* The output of this method can be {@linkplain GeneralEnvelope#GeneralEnvelope(String) parsed}
* by the {@link GeneralEnvelope} constructor.
*
* @param envelope The envelope to format.
* @return The envelope as a {@code POLYGON} in WKT format.
*
* @see org.geotoolkit.io.wkt
*
* @since 3.09
*
* @deprecated Moved to {@link Envelopes#toPolygonWKT(Envelope)}.
*/
@Deprecated
public static String toPolygonString(final Envelope envelope) {
return Envelopes.toPolygonWKT(envelope);
}
/**
* Returns a hash value for this envelope.
*/
@Override
public int hashCode() {
final int dimension = getDimension();
int code = 1;
boolean p = true;
do {
for (int i=0; i>> 32));
}
} while ((p = !p) == false);
final CoordinateReferenceSystem crs = getCoordinateReferenceSystem();
if (crs != null) {
code += crs.hashCode();
}
return code;
}
/**
* Returns {@code true} if the specified object is an envelope of the same class
* with equals coordinates and {@linkplain #getCoordinateReferenceSystem CRS}.
*
* {@note This implementation requires that the provided object
argument
* is of the same class than this envelope. We do not relax this rule since not every
* implementations in the Geotk code base follow the same contract.}
*
* @param object The object to compare with this envelope.
* @return {@code true} if the given object is equal to this envelope.
*/
@Override
public boolean equals(final Object object) {
if (object != null && object.getClass() == getClass()) {
final Envelope that = (Envelope) object;
final int dimension = getDimension();
if (dimension == that.getDimension()) {
for (int i=0; i
* If {@code epsIsRelative} is set to {@code true}, the actual tolerance value for a given
* dimension i is {@code eps}×{@code span} where {@code span} is the
* maximum of {@linkplain #getSpan this envelope span} and the specified envelope length
* along dimension i.
*
* If {@code epsIsRelative} is set to {@code false}, the actual tolerance value for a
* given dimension i is {@code eps}.
*
* Relative tolerance value (as opposed to absolute tolerance value) help to workaround the
* fact that tolerance value are CRS dependent. For example the tolerance value need to be
* smaller for geographic CRS than for UTM projections, because the former typically has a
* range of -180 to 180° while the later can have a range of thousands of meters.
*
* {@note This method assumes that the specified envelope uses the same CRS than this envelope.
* For performance reason, it will no be verified unless Java assertions are enabled.}
*
* @param envelope The envelope to compare with.
* @param eps The tolerance value to use for numerical comparisons.
* @param epsIsRelative {@code true} if the tolerance value should be relative to
* axis length, or {@code false} if it is an absolute value.
* @return {@code true} if the given object is equal to this envelope up to the given
* tolerance value.
*
* @see GeneralEnvelope#contains(Envelope, boolean)
* @see GeneralEnvelope#intersects(Envelope, boolean)
*
* @since 2.4
*/
public boolean equals(final Envelope envelope, final double eps, final boolean epsIsRelative) {
ensureNonNull("envelope", envelope);
final int dimension = getDimension();
if (envelope.getDimension() != dimension) {
return false;
}
assert equalsIgnoreMetadata(getCoordinateReferenceSystem(), envelope.getCoordinateReferenceSystem()) : envelope;
for (int i=0; i 0 && epsilon < Double.POSITIVE_INFINITY) ? epsilon*eps : eps;
} else {
epsilon = eps;
}
// Comparison below uses '!' in order to catch NaN values.
if (!(Math.abs(getMinimum(i) - envelope.getMinimum(i)) <= epsilon &&
Math.abs(getMaximum(i) - envelope.getMaximum(i)) <= epsilon))
{
return false;
}
}
return true;
}
/**
* Base class for direct position from an envelope.
* This class delegates its work to the enclosing envelope.
*/
private abstract class Corner extends AbstractDirectPosition {
/** The coordinate reference system in which the coordinate is given. */
@Override public CoordinateReferenceSystem getCoordinateReferenceSystem() {
return AbstractEnvelope.this.getCoordinateReferenceSystem();
}
/** The length of coordinate sequence (the number of entries). */
@Override public int getDimension() {
return AbstractEnvelope.this.getDimension();
}
/** Sets the ordinate value along the specified dimension. */
@Override public void setOrdinate(int dimension, double value) {
throw new UnsupportedOperationException();
}
}
/**
* The corner returned by {@link AbstractEnvelope#getLowerCorner}.
*/
private final class LowerCorner extends Corner {
@Override public double getOrdinate(final int dimension) throws IndexOutOfBoundsException {
return getMinimum(dimension);
}
}
/**
* The corner returned by {@link AbstractEnvelope#getUpperCorner}.
*/
private final class UpperCorner extends Corner {
@Override public double getOrdinate(final int dimension) throws IndexOutOfBoundsException {
return getMaximum(dimension);
}
}
}