001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004-2007, GeoTools Project Managment Committee (PMC)
005: * (C) 2004, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation;
010: * version 2.1 of the License.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.geometry;
018:
019: // OpenGIS dependencies
020: import org.opengis.geometry.DirectPosition;
021: import org.opengis.geometry.Envelope;
022: import org.opengis.geometry.MismatchedReferenceSystemException;
023: import org.opengis.referencing.crs.CoordinateReferenceSystem;
024:
025: // Geotools dependencies
026: import org.geotools.resources.Utilities;
027: import org.geotools.resources.i18n.Errors;
028: import org.geotools.resources.i18n.ErrorKeys;
029:
030: /**
031: * Base class for {@linkplain Envelope envelope} implementations. This base class
032: * provides default implementations for {@link #toString}, {@link #equals} and
033: * {@link #hashCode} methods.
034: * <p>
035: * This class do not holds any state. The decision to implement {@link java.io.Serializable}
036: * or {@link org.geotools.util.Cloneable} interfaces is left to implementors.
037: *
038: * @since 2.4
039: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/geometry/AbstractEnvelope.java $
040: * @version $Id: AbstractEnvelope.java 26137 2007-07-03 17:59:44Z desruisseaux $
041: * @author Martin Desruisseaux
042: */
043: public abstract class AbstractEnvelope implements Envelope {
044: /**
045: * Constructs an envelope.
046: */
047: protected AbstractEnvelope() {
048: }
049:
050: /**
051: * Returns the common CRS of specified points.
052: *
053: * @param minDP The first position.
054: * @param maxDP The second position.
055: * @return Their common CRS, or {@code null} if none.
056: * @throws MismatchedReferenceSystemException if the two positions don't use the same CRS.
057: */
058: static CoordinateReferenceSystem getCoordinateReferenceSystem(
059: final DirectPosition minDP, final DirectPosition maxDP)
060: throws MismatchedReferenceSystemException {
061: final CoordinateReferenceSystem crs1 = minDP
062: .getCoordinateReferenceSystem();
063: final CoordinateReferenceSystem crs2 = maxDP
064: .getCoordinateReferenceSystem();
065: if (crs1 == null) {
066: return crs2;
067: } else {
068: if (crs2 != null && !crs1.equals(crs2)) {
069: throw new MismatchedReferenceSystemException(
070: Errors
071: .format(ErrorKeys.MISMATCHED_COORDINATE_REFERENCE_SYSTEM));
072: }
073: return crs1;
074: }
075: }
076:
077: /**
078: * A coordinate position consisting of all the {@linkplain #getMinimum minimal ordinates}.
079: * The default implementation returns a direct position backed by this envelope, so changes
080: * in this envelope will be immediately reflected in the direct position.
081: *
082: * @return The lower corner.
083: */
084: public DirectPosition getLowerCorner() {
085: return new LowerCorner();
086: }
087:
088: /**
089: * A coordinate position consisting of all the {@linkplain #getMaximum maximal ordinates}.
090: * The default implementation returns a direct position backed by this envelope, so changes
091: * in this envelope will be immediately reflected in the direct position.
092: *
093: * @return The upper corner.
094: */
095: public DirectPosition getUpperCorner() {
096: return new UpperCorner();
097: }
098:
099: /**
100: * Returns a string representation of this envelope. The default implementation is okay
101: * for occasional formatting (for example for debugging purpose). But if there is a lot
102: * of envelopes to format, users will get more control by using their own instance of
103: * {@link org.geotools.measure.CoordinateFormat}.
104: */
105: public String toString() {
106: return toString(this );
107: }
108:
109: /**
110: * Formats the specified envelope.
111: */
112: static String toString(final Envelope envelope) {
113: final StringBuffer buffer = new StringBuffer(Utilities
114: .getShortClassName(envelope)).append('[');
115: final int dimension = envelope.getDimension();
116: for (int i = 0; i < dimension; i++) {
117: if (i != 0) {
118: buffer.append(", ");
119: }
120: buffer.append(envelope.getMinimum(i)).append(" : ").append(
121: envelope.getMaximum(i));
122: }
123: return buffer.append(']').toString();
124: }
125:
126: /**
127: * Returns a hash value for this envelope.
128: */
129: public int hashCode() {
130: final int dimension = getDimension();
131: int code = 1;
132: boolean p = true;
133: do {
134: for (int i = 0; i < dimension; i++) {
135: final long bits = Double
136: .doubleToLongBits(p ? getMinimum(i)
137: : getMaximum(i));
138: code = 31 * code + ((int) (bits) ^ (int) (bits >>> 32));
139: }
140: } while ((p = !p) == false);
141: final CoordinateReferenceSystem crs = getCoordinateReferenceSystem();
142: if (crs != null) {
143: code += crs.hashCode();
144: }
145: return code;
146: }
147:
148: /**
149: * Returns {@code true} if the specified object is also an {@linkplain Envelope envelope}
150: * with equals coordinates and {@linkplain #getCoordinateReferenceSystem CRS}.
151: *
152: * @todo Current implementation requires that {@code object} is of the same class.
153: * We can not relax this rule before we ensure that every implementations in
154: * the Geotools code base follow the same contract.
155: */
156: public boolean equals(final Object object) {
157: if (object != null && object.getClass().equals(getClass())) {
158: final Envelope that = (Envelope) object;
159: final int dimension = getDimension();
160: if (dimension == that.getDimension()) {
161: for (int i = 0; i < dimension; i++) {
162: if (Double.doubleToLongBits(this .getMinimum(i)) != Double
163: .doubleToLongBits(that.getMinimum(i))
164: || Double.doubleToLongBits(this
165: .getMaximum(i)) != Double
166: .doubleToLongBits(that
167: .getMaximum(i))) {
168: return false;
169: }
170: }
171: if (Utilities.equals(this
172: .getCoordinateReferenceSystem(), that
173: .getCoordinateReferenceSystem())) {
174: assert hashCode() == that.hashCode() : this ;
175: return true;
176: }
177: }
178: }
179: return false;
180: }
181:
182: /**
183: * Base class for direct position from an envelope.
184: * This class delegates its work to the enclosing envelope.
185: */
186: private abstract class Corner extends AbstractDirectPosition {
187: /** The coordinate reference system in which the coordinate is given. */
188: public CoordinateReferenceSystem getCoordinateReferenceSystem() {
189: return AbstractEnvelope.this .getCoordinateReferenceSystem();
190: }
191:
192: /** The length of coordinate sequence (the number of entries). */
193: public int getDimension() {
194: return AbstractEnvelope.this .getDimension();
195: }
196:
197: /** Sets the ordinate value along the specified dimension. */
198: public void setOrdinate(int dimension, double value) {
199: throw new UnsupportedOperationException();
200: }
201: }
202:
203: /**
204: * The corner returned by {@link AbstractEnvelope#getLowerCorner}.
205: */
206: private final class LowerCorner extends Corner {
207: public double getOrdinate(final int dimension)
208: throws IndexOutOfBoundsException {
209: return getMinimum(dimension);
210: }
211: }
212:
213: /**
214: * The corner returned by {@link AbstractEnvelope#getUpperCorner}.
215: */
216: private final class UpperCorner extends Corner {
217: public double getOrdinate(final int dimension)
218: throws IndexOutOfBoundsException {
219: return getMaximum(dimension);
220: }
221: }
222: }
|