/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2006-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.referencing.operation.builder; import java.io.Serializable; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; import org.opengis.geometry.MismatchedDimensionException; import org.opengis.geometry.DirectPosition; import org.geotools.io.TableWriter; import org.geotools.geometry.DirectPosition2D; import org.geotools.geometry.GeneralDirectPosition; import org.geotools.resources.i18n.VocabularyKeys; import org.geotools.resources.i18n.Vocabulary; import org.geotools.resources.i18n.ErrorKeys; import org.geotools.resources.i18n.Errors; import org.geotools.util.Utilities; /** * An association between a {@linkplain #getSource source} and {@linkplain #getTarget target} * direct positions. Accuracy information and comments can optionnaly be attached. * * @since 2.4 * * @source $URL$ * @version $Id$ * @author Jan Jezek * @author Martin Desruisseaux */ public class MappedPosition implements Serializable { /** * For cross-version compatibility. */ private static final long serialVersionUID = 3262172371858749543L; /** * The source position. */ private final DirectPosition source; /** * The target position. */ private final DirectPosition target; /** * An estimation of mapping accuracy in units of target CRS axis, * or {@link Double#NaN} if unknow. */ private double accuracy = Double.NaN; /** * Optionnal comments attached to this mapping, or {@code null} if none. */ private String comments; /** * Creates a mapped position with {@linkplain #getSource source} and * {@linkplain #getTarget target} position of the specified dimension. * The initial coordinate values are 0. */ public MappedPosition(final int dimension) { if (dimension == 2) { source = new DirectPosition2D(); target = new DirectPosition2D(); } else { source = new GeneralDirectPosition(dimension); target = new GeneralDirectPosition(dimension); } } /** * Creates a mapped position from the specified direct positions. * * @param source The original direct position. * @param target The associated direct position. */ public MappedPosition(final DirectPosition source, final DirectPosition target) { ensureNonNull("source", source); ensureNonNull("target", target); this.source = source; this.target = target; } /** * Makes sure an argument is non-null. * * @param name Argument name. * @param object User argument. * @throws InvalidParameterValueException if {@code object} is null. */ private static void ensureNonNull(final String name, final Object object) throws IllegalArgumentException { if (object == null) { throw new IllegalArgumentException(Errors.format(ErrorKeys.NULL_ARGUMENT_$1, name)); } } /** * Returns the source direct position. For performance reasons, the current * implementation returns a reference to the internal object. However users * should avoid to modify directly the returned position and use * {@link #setSource} instead. */ public DirectPosition getSource() { return source; } /** * Set the source direct position to the specified value. */ public void setSource(final DirectPosition point) { if (source instanceof DirectPosition2D) { ((DirectPosition2D) source).setLocation(point); } else { ((GeneralDirectPosition) source).setLocation(point); } } /** * Returns the target direct position. For performance reasons, the current * implementation returns a reference to the internal object. However users * should avoid to modify directly the returned position and use * {@link #setTarget} instead. */ public DirectPosition getTarget() { return target; } /** * Set the target direct position to the specified value. */ public void setTarget(final DirectPosition point) { if (source instanceof DirectPosition2D) { ((DirectPosition2D) target).setLocation(point); } else { ((GeneralDirectPosition) target).setLocation(point); } } /** * Returns the comments attached to this mapping, or {@code null} if none. */ public String getComments() { return comments; } /** * Set the comments attached to this mapping. May be {@code null} if none. */ public void setComments(final String comments) { this.comments = comments; } /** * Returns an estimation of mapping accuracy in units of target CRS axis, * or {@link Double#NaN} if unknow. */ public double getAccuracy() { return accuracy; } /** * Set the accuracy. */ public void setAccuracy(final double accuracy) { this.accuracy = accuracy; } /** * Computes the distance between the {@linkplain #getSource source point} transformed * by the supplied math transform, and the {@linkplain #getTarget target point}. * * @param transform The transform to use for computing the error. * @param buffer An optionnaly pre-computed direct position to use as a buffer, * or {@code null} if none. The content of this buffer will be overwritten. * @return The distance in units of the target CRS axis. */ final double getError(final MathTransform transform, final DirectPosition buffer) throws TransformException { return distance(transform.transform(source, buffer), target); } /** * Returns the distance between the specified points. */ private static double distance(final DirectPosition source, final DirectPosition target) { final int otherDim = source.getDimension(); final int dimension = target.getDimension(); if (otherDim != dimension) { throw new MismatchedDimensionException(Errors.format(ErrorKeys.MISMATCHED_DIMENSION_$2, otherDim, dimension)); } double sum = 0; for (int i=0; i<dimension; i++) { final double delta = source.getOrdinate(i) - target.getOrdinate(i); sum += delta*delta; } return Math.sqrt(sum / dimension); } /** * Returns a hash code value for this mapped position. */ public int hashCode() { return source.hashCode() + 37*target.hashCode(); } /** * Compares this mapped position with the specified object for equality. */ public boolean equals(final Object object) { if (object!=null && object.getClass().equals(getClass())) { final MappedPosition that = (MappedPosition) object; return Utilities.equals(this.source, that.source) && Utilities.equals(this.target, that.target) && Utilities.equals(this.comments, that.comments) && Utilities.equals(this.accuracy, that.accuracy); } return false; } /** * Returns a string representation of this mapped position. * * @todo Consider using a {@link java.text.NumberFormat} instance. */ public String toString() { final TableWriter table = new TableWriter(null, " "); table.write(Vocabulary.format(VocabularyKeys.SOURCE_POINT)); table.write(':'); int dimension = source.getDimension(); for (int i=0; i<dimension; i++) { table.nextColumn(); table.write(String.valueOf(source.getOrdinate(i))); } table.nextLine(); table.write(Vocabulary.format(VocabularyKeys.TARGET_POINT)); table.write(':'); dimension = target.getDimension(); for (int i=0; i<dimension; i++) { table.nextColumn(); table.write(String.valueOf(target.getOrdinate(i))); } return table.toString(); } }