001: /*
002: * Geotools2 - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2006, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.referencing.operation.builder;
017:
018: // J2SE dependencies
019: import java.io.Serializable;
020:
021: // OpenGIS dependencies
022: import org.opengis.referencing.operation.MathTransform;
023: import org.opengis.referencing.operation.TransformException;
024: import org.opengis.geometry.MismatchedDimensionException;
025: import org.opengis.geometry.DirectPosition;
026:
027: // Geotools dependencies
028: import org.geotools.io.TableWriter;
029: import org.geotools.geometry.DirectPosition2D;
030: import org.geotools.geometry.GeneralDirectPosition;
031: import org.geotools.resources.i18n.VocabularyKeys;
032: import org.geotools.resources.i18n.Vocabulary;
033: import org.geotools.resources.i18n.ErrorKeys;
034: import org.geotools.resources.i18n.Errors;
035: import org.geotools.resources.Utilities;
036:
037: /**
038: * An association between a {@linkplain #getSource source} and {@linkplain #getTarget target}
039: * direct positions. Accuracy information and comments can optionnaly be attached.
040: *
041: * @since 2.4
042: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/operation/builder/MappedPosition.java $
043: * @version $Id: MappedPosition.java 25262 2007-04-23 21:11:16Z desruisseaux $
044: * @author Jan Jezek
045: * @author Martin Desruisseaux
046: */
047: public class MappedPosition implements Serializable {
048: /**
049: * For cross-version compatibility.
050: */
051: private static final long serialVersionUID = 3262172371858749543L;
052:
053: /**
054: * The source position.
055: */
056: private final DirectPosition source;
057:
058: /**
059: * The target position.
060: */
061: private final DirectPosition target;
062:
063: /**
064: * An estimation of mapping accuracy in units of target CRS axis,
065: * or {@link Double#NaN} if unknow.
066: */
067: private double accuracy = Double.NaN;
068:
069: /**
070: * Optionnal comments attached to this mapping, or {@code null} if none.
071: */
072: private String comments;
073:
074: /**
075: * Creates a mapped position with {@linkplain #getSource source} and
076: * {@linkplain #getTarget target} position of the specified dimension.
077: * The initial coordinate values are 0.
078: */
079: public MappedPosition(final int dimension) {
080: if (dimension == 2) {
081: source = new DirectPosition2D();
082: target = new DirectPosition2D();
083: } else {
084: source = new GeneralDirectPosition(dimension);
085: target = new GeneralDirectPosition(dimension);
086: }
087: }
088:
089: /**
090: * Creates a mapped position from the specified direct positions.
091: *
092: * @param source The original direct position.
093: * @param target The associated direct position.
094: */
095: public MappedPosition(final DirectPosition source,
096: final DirectPosition target) {
097: ensureNonNull("source", source);
098: ensureNonNull("target", target);
099: this .source = source;
100: this .target = target;
101: }
102:
103: /**
104: * Makes sure an argument is non-null.
105: *
106: * @param name Argument name.
107: * @param object User argument.
108: * @throws InvalidParameterValueException if {@code object} is null.
109: */
110: private static void ensureNonNull(final String name,
111: final Object object) throws IllegalArgumentException {
112: if (object == null) {
113: throw new IllegalArgumentException(Errors.format(
114: ErrorKeys.NULL_ARGUMENT_$1, name));
115: }
116: }
117:
118: /**
119: * Returns the source direct position. For performance reasons, the current
120: * implementation returns a reference to the internal object. However users
121: * should avoid to modify directly the returned position and use
122: * {@link #setSource} instead.
123: */
124: public DirectPosition getSource() {
125: return source;
126: }
127:
128: /**
129: * Set the source direct position to the specified value.
130: */
131: public void setSource(final DirectPosition point) {
132: if (source instanceof DirectPosition2D) {
133: ((DirectPosition2D) source).setLocation(point);
134: } else {
135: ((GeneralDirectPosition) source).setLocation(point);
136: }
137: }
138:
139: /**
140: * Returns the target direct position. For performance reasons, the current
141: * implementation returns a reference to the internal object. However users
142: * should avoid to modify directly the returned position and use
143: * {@link #setTarget} instead.
144: */
145: public DirectPosition getTarget() {
146: return target;
147: }
148:
149: /**
150: * Set the target direct position to the specified value.
151: */
152: public void setTarget(final DirectPosition point) {
153: if (source instanceof DirectPosition2D) {
154: ((DirectPosition2D) target).setLocation(point);
155: } else {
156: ((GeneralDirectPosition) target).setLocation(point);
157: }
158: }
159:
160: /**
161: * Returns the comments attached to this mapping, or {@code null} if none.
162: */
163: public String getComments() {
164: return comments;
165: }
166:
167: /**
168: * Set the comments attached to this mapping. May be {@code null} if none.
169: */
170: public void setComments(final String comments) {
171: this .comments = comments;
172: }
173:
174: /**
175: * Returns an estimation of mapping accuracy in units of target CRS axis,
176: * or {@link Double#NaN} if unknow.
177: */
178: public double getAccuracy() {
179: return accuracy;
180: }
181:
182: /**
183: * Set the accuracy.
184: */
185: public void setAccuracy(final double accuracy) {
186: this .accuracy = accuracy;
187: }
188:
189: /**
190: * Computes the distance between the {@linkplain #getSource source point} transformed
191: * by the supplied math transform, and the {@linkplain #getTarget target point}.
192: *
193: * @param transform The transform to use for computing the error.
194: * @param buffer An optionnaly pre-computed direct position to use as a buffer,
195: * or {@code null} if none. The content of this buffer will be overwritten.
196: * @return The distance in units of the target CRS axis.
197: */
198: final double getError(final MathTransform transform,
199: final DirectPosition buffer) throws TransformException {
200: return distance(transform.transform(source, buffer), target);
201: }
202:
203: /**
204: * Returns the distance between the specified points.
205: */
206: private static double distance(final DirectPosition source,
207: final DirectPosition target) {
208: final int otherDim = source.getDimension();
209: final int dimension = target.getDimension();
210: if (otherDim != dimension) {
211: throw new MismatchedDimensionException(Errors.format(
212: ErrorKeys.MISMATCHED_DIMENSION_$2, new Integer(
213: otherDim), new Integer(dimension)));
214: }
215: double sum = 0;
216: for (int i = 0; i < dimension; i++) {
217: final double delta = source.getOrdinate(i)
218: - target.getOrdinate(i);
219: sum += delta * delta;
220: }
221: return Math.sqrt(sum / dimension);
222: }
223:
224: /**
225: * Returns a hash code value for this mapped position.
226: */
227: public int hashCode() {
228: return source.hashCode() + 37 * target.hashCode();
229: }
230:
231: /**
232: * Compares this mapped position with the specified object for equality.
233: */
234: public boolean equals(final Object object) {
235: if (object != null && object.getClass().equals(getClass())) {
236: final MappedPosition that = (MappedPosition) object;
237: return Utilities.equals(this .source, that.source)
238: && Utilities.equals(this .target, that.target)
239: && Utilities.equals(this .comments, that.comments)
240: && Double.doubleToLongBits(this .accuracy) == Double
241: .doubleToLongBits(that.accuracy);
242: }
243: return false;
244: }
245:
246: /**
247: * Returns a string representation of this mapped position.
248: *
249: * @todo Consider using a {@link java.text.NumberFormat} instance.
250: */
251: public String toString() {
252: final TableWriter table = new TableWriter(null, " ");
253: table.write(Vocabulary.format(VocabularyKeys.SOURCE_POINT));
254: table.write(':');
255: int dimension = source.getDimension();
256: for (int i = 0; i < dimension; i++) {
257: table.nextColumn();
258: table.write(String.valueOf(source.getOrdinate(i)));
259: }
260: table.nextLine();
261: table.write(Vocabulary.format(VocabularyKeys.TARGET_POINT));
262: table.write(':');
263: dimension = target.getDimension();
264: for (int i = 0; i < dimension; i++) {
265: table.nextColumn();
266: table.write(String.valueOf(target.getOrdinate(i)));
267: }
268: return table.toString();
269: }
270: }
|