/* * 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.geometry.iso.coordinate; import java.io.Serializable; import java.util.Arrays; import org.geotools.geometry.iso.util.DoubleOperation; import org.geotools.geometry.iso.util.algorithmND.AlgoPointND; import org.geotools.referencing.CRS; import org.opengis.util.Cloneable; import org.opengis.geometry.DirectPosition; import org.opengis.geometry.MismatchedDimensionException; import org.opengis.geometry.coordinate.Position; import org.opengis.referencing.crs.CoordinateReferenceSystem; /** * @author Jackson Roehrig & Sanjay Jena * * * @source $URL$ */ public class DirectPositionImpl implements DirectPosition, Cloneable, Serializable { private static final long serialVersionUID = 2327211794986364062L; /** * The attribute "coordinate" is a sequence of Numbers that hold the * coordinate of this position in the specified reference system. * DirectPosition2D::coordinate : Sequence<Number> * * In order that this system will be based on an euclidic 2D system, the * coordinates will be stored in explicit variables for each X- and * Y-Coordinate */ private double[] coordinate; /** * Coordinate Reference System used to determine meaning of above coordinates */ private CoordinateReferenceSystem crs; public DirectPositionImpl( CoordinateReferenceSystem crs ){ final int N = crs.getCoordinateSystem().getDimension(); this.crs = crs; this.coordinate = new double[N]; for (int i = 0; i < N; ++i){ this.coordinate[i] = Double.NaN; } } /** * Creates a direct Position by using coordinates of another direct Position * @param crs * @param coord */ public DirectPositionImpl(CoordinateReferenceSystem crs, double[] coord) { this.crs = crs; assert (coord.length == crs.getCoordinateSystem().getDimension()); this.coordinate = coord; } public DirectPositionImpl( Position position ){ this( position.getPosition() ); } /** * Creates a direct Position by using coordinates of another direct Position * * @param position */ public DirectPositionImpl(final DirectPosition position) { this.crs = position.getCoordinateReferenceSystem(); // Comment by Sanjay // VORSICHT: Die folgende Codezeile verursachte, dass das selbe Objekt // (double Array) verwendet wurde; folglich wurde z.B. beim // Envelope Min und Max Position auf die selben Koordinaten zugegriffen. // this.coordinate=p.getCoordinate(); // Bitte um kenntnisnahme und berücksichtigung in sourcen: Arrays müssen // explizit kopiert werden, nur elementare Datentypen werden automatisch // von Java neu erzeugt, alles andere sind nur Referenzen // TODO Das Klonen sollte in die Factory verlagert werden // the above seems to say that the array must be explicitly copied // but the direct position javadocs say that getCoordinate produces // a copy ... so we should be good without the clone. this.coordinate = position.getCoordinates(); //.clone() } /** * @param crs * @param x * @param y * @param z */ public DirectPositionImpl(CoordinateReferenceSystem crs, double x, double y, double z) { this.crs = crs; assert (3 == crs.getCoordinateSystem().getDimension()); this.coordinate = new double[] { x, y, z }; } /** * @param crs * @param x * @param y * @param z * @param m */ public DirectPositionImpl( CoordinateReferenceSystem crs, double x, double y, double z, double m) { this.crs = crs; assert (5 == crs.getCoordinateSystem().getDimension()); this.coordinate = new double[] { x, y, z, m }; } /* * (non-Javadoc) * * @see org.opengis.geometry.coordinate.DirectPosition#getDimension() */ public int getDimension() { return crs.getCoordinateSystem().getDimension(); } /* * (non-Javadoc) * * @see org.opengis.geometry.coordinate.DirectPosition#getCoordinate() */ public double[] getCoordinate() { return (double[]) this.coordinate.clone(); // JG: modified to return clone each time } @Deprecated public double[] getCoordinates() { return getCoordinate(); } /* * (non-Javadoc) * * @see org.opengis.geometry.coordinate.DirectPosition#getOrdinate(int) */ public double getOrdinate(int dimension) throws IndexOutOfBoundsException { return this.coordinate[dimension]; } /* * (non-Javadoc) * * @see org.opengis.geometry.coordinate.DirectPosition#setOrdinate(int, * double) */ public void setOrdinate(int dimension, double value) throws IndexOutOfBoundsException { // TODO semantic JR // TODO documentation if (dimension >= this.coordinate.length || dimension < 0){ throw new IndexOutOfBoundsException("Index "+dimension+" out of coordinate range (max "+ coordinate.length+")"); //$NON-NLS-1$ } this.coordinate[dimension] = value; } /* * (non-Javadoc) * * @see org.opengis.geometry.coordinate.DirectPosition#getCoordinateReferenceSystem() */ public CoordinateReferenceSystem getCoordinateReferenceSystem() { // TODO semantic JR // TODO implementation Is the CRS correct/existent? // TODO test // TODO documentation return crs; } /* * (non-Javadoc) * * @see org.opengis.geometry.coordinate.DirectPosition#clone() */ public DirectPositionImpl clone() { // Cloning the double array (in parameter) is important! // Return new DirectPosition by cloning the Coordiante array of double which define the position return new DirectPositionImpl( crs, coordinate.clone() ); } /** * @param coord */ public void setCoordinate(double[] coord) { if (coord.length != this.getDimension()){ throw new IllegalArgumentException("Index "+coord.length+" out of coordinate range (expected "+getDimension()+")"); //$NON-NLS-1$ } this.coordinate = coord; } /** * Returns the x value of the coordinate represented by this DirectPosition * @return x */ public double getX() { return this.coordinate[0]; } /** * Returns the y value of the coordinate represented by this DirectPosition * @return y */ public double getY() { return this.coordinate[1]; } /** * Returns the z value of the coordinate represented by this DirectPosition * @return z */ public double getZ() { return (this.getDimension() > 2) ? this.coordinate[2] : Double.NaN; } /** * Sets the x value of the coordinate represented by this DirectPosition * * @param x */ public void setX(double x) { this.coordinate[0] = x; } /** * Sets the y value of the coordinate represented by this DirectPosition * * @param y */ public void setY(double y) { this.coordinate[1] = y; } /** * Sets the z value of the coordinate represented by this DirectPosition * * @param z */ public void setZ(double z) { if (this.getDimension() > 2) this.coordinate[2] = z; } /** * Compares coodinates of Direct Positions and allows a tolerance value in * the comparison * * @param position * Direct Position to compare with * @param tol Epsilon tolerance value * @return TRUE, if coordinates accord concording to the tolerance value, FALSE if they dont. */ public boolean equals(DirectPosition position, double tol) { int D = position.getCoordinateReferenceSystem().getCoordinateSystem().getDimension(); if( D != crs.getCoordinateSystem().getDimension() ) return false; // use CRS.equalsIgnoreMetadata for effeciency and to avoid various issues with comparing // CRS such as coordinate order. if ( !CRS.equalsIgnoreMetadata(getCoordinateReferenceSystem(), position.getCoordinateReferenceSystem()) ) { return false; } // comparing a NaN ordinate to a non-NaN ordinate should return false, but two // ordinates that are both NaN should considered equal. for (int i = 0; i < D; ++i) { if (Double.isNaN(position.getOrdinate(i)) && Double.isNaN(this.coordinate[i])) continue; if (Math.abs(DoubleOperation.subtract(position.getOrdinate(i), this.coordinate[i])) > tol) return false; } return true; } /** * Compares coodinates of DirectPosition Implementation Note: Parameter has * to be of Type DirectPosition (not DirectPositionImpl), so that the equals * method is found for DirectPosition´s and DirectPositionImpl´s * * @param p * DirectPosition * @return TRUE, if the two DirectPositions describe the same point in the * Euclidian Space */ // Sanjay: The method was replaced by the equals(Object) method below, // because it was not recognized in all cases // public boolean equals(DirectPosition p) { // return this.equals(p, 0); // } // TODO JR: nach zur kenntnisnahme und zustimmung bitte obiges kommentar loeschen public boolean equals(Object o) { if (o instanceof DirectPosition) return this.equals((DirectPosition) o, 0); else if (o instanceof Position) return ((Position)o).equals(this); else return false; } public int hashCode() { final int PRIME = 31; int result = 1; result = PRIME * result + Arrays.hashCode(coordinate); result = PRIME * result + ((crs == null) ? 0 : crs.hashCode()); return result; } public String toString() { double coord[] = this.getCoordinates(); String str = "(" + Double.toString(coord[0]); for (int i = 1; i < coord.length; ++i) { str += " " + Double.toString(coord[i]); } return str + ")"; } /** * Calculates the distance to another direct position * * @param p * direct Position * @return Distance */ public double distance(DirectPosition p) { return AlgoPointND.getDistance(this.coordinate, p.getCoordinates()); } /** * Calculates the square of the distance to another direct position * * @param p another direct Position * @return Distance */ public double distanceSquare(DirectPosition p) { return AlgoPointND.getDistanceSquare(this.coordinate, p .getCoordinates()); } // Auskommentiert, da ungenutzt und nicht getestet // /** // * Adds a DirectPosition to the position // * // * @param p // * DirectPosition to add // * @return new Position // */ // public DirectPositionImpl add(DirectPosition p) { // return new DirectPositionImpl(this.factory, AlgoPointND.add( // this.coordinate, p.getCoordinate())); // } /** * Adds certain value to each ordinate of this direct position. * * @param values Array of doubles. values[0] will be added to the X ordinate, values[1] to the Y value and an optional values[2] to the Z value. */ public void add(double[] values) { if (this.coordinate.length != values.length) throw new MismatchedDimensionException(); for (int i=0; i < this.coordinate.length; i++) { if (Double.compare(this.coordinate[i], Double.NaN) == 0) { this.coordinate[i] = values[i]; } else { this.coordinate[i] = DoubleOperation.add(this.coordinate[i], values[i]); } } } /** * Adds the ordinates of another direct position to the ordinates of this direct position. * * @param otherDP DirectPosition which ordinates shall be added to this DirectPosition */ public void add(DirectPositionImpl otherDP) { if (this.coordinate.length != otherDP.getDimension()) throw new MismatchedDimensionException(); for (int i=0; i < this.coordinate.length; i++) { if (Double.compare(this.coordinate[i], Double.NaN) == 0) { this.coordinate[i] = otherDP.getOrdinate(i); } else { this.coordinate[i] = DoubleOperation.add(this.coordinate[i], otherDP.getOrdinate(i)); } } } /** * Scales the ordinates of the DirectPosition by a factor: * newOrdinate = oldOrdinate * factor * * @param factor Scaling factor */ public void scale(double factor) { for (int i=0; i < this.coordinate.length; i++) { this.coordinate[i] = DoubleOperation.mult(this.coordinate[i], factor); } } /** * Divides the ordinates of the DirectPosition by a factor: * newOrdinate = oldOrdinate / factor * * @param factor Value of divisor */ public void divideBy(double factor) { for (int i=0; i < this.coordinate.length; i++) { this.coordinate[i] = DoubleOperation.div(this.coordinate[i], factor); } } // TODO This method exists because of the extend of Position // Is this extend really correct??? Check interfaces!!! /* (non-Javadoc) * @see org.opengis.geometry.coordinate.Position#getPosition() */ @Deprecated public DirectPosition getPosition() { return this; } /* (non-Javadoc) * @see org.opengis.geometry.coordinate.Position#getDirectPosition() */ public DirectPosition getDirectPosition() { return this; } // Auskommentiert, da ungenutzt und nicht getestet // /** // * Subtracts a direct position from the position // * // * @param p // * @return new Position // */ // public DirectPositionImpl subtract(DirectPositionImpl p) { // return new DirectPositionImpl(this.factory, AlgoPointND.subtract( // this.coordinate, p.coordinate)); // } // Auskommentiert, da die Methode auf einer nicht-robusten Methode scale-Methode von AlgoPointND basiert. // Die Methode liefert daher teilweise falsche Ergebnisse. S. JUNIT Test (SJ) // TODO 1) benoetigen wir die methode. wenn ja: // 2) algorithmus als robuste version implementieren oder sind interne rundungsfehler ok? // /** // * @param factor // * @return DirectPositionImpl // */ // public DirectPositionImpl scale(double factor) { // return new DirectPositionImpl(this.factory, AlgoPointND.scale( // this.coordinate, factor)); // } // Auch diese Methode beruht auf einem Algorithmus, welcher wahrscheinlich nicht robust ist. // TODO 1) benoetigen wir die methode. wenn ja: // 2) algorithmus als robuste version implementieren oder sind interne rundungsfehler ok? // /** // * @return GM_DirectPosition // */ // public DirectPositionImpl normalize() { // return new DirectPositionImpl(this.factory, AlgoPointND // .normalize(this.coordinate.clone())); // } // Diese methode hat meiner meinung nach nichts in dieser klasse zu suchen (SJ) // das interpolieren von einer straight line gehoert nicht zur aufgabe einer directPosition // eher zu lineSegment. ich habe die vorhandene methode dort (in LineSegment) deswegen entsprechend angepasst. // // /** // * // * @param p0 // * @param p1 // * @param r // * @return DirectPositionImpl // */ // public static DirectPositionImpl evaluate(DirectPositionImpl p0, // DirectPositionImpl p1, double r) { // // TODO Documentation // // TODO Test // return new DirectPositionImpl(p0.factory, AlgoPointND.evaluate( // p0.coordinate, p1.coordinate, r)); // } // Diese methode hat meiner meinung nach nichts in dieser klasse zu suchen (SJ) // das interpolieren von einer straight line gehoert nicht zur aufgabe einer directPosition // eher zu lineSegment. ich habe die vorhandene methode dort (in LineSegment) deswegen entsprechend angepasst. // /** // * @param p0 // * @param p1 // * @param eval // * @return DirectPositionImpl // */ // public static DirectPositionImpl evaluate(DirectPositionImpl p0, // DirectPositionImpl p1, DirectPositionImpl eval) { // return new DirectPositionImpl(p0.factory, AlgoPointND.evaluate( // p0.coordinate, p1.coordinate, eval.coordinate)); // } // Nicht genutzt, nicht getestet // /** // * Returns the length (Distance between origin and position) // * // * @return Length // */ // public double length() { // return AlgoPointND.getDistanceToOrigin(this.coordinate); // } // Nicht genutzt, nicht getestet // /** // * @return double // */ // public double lengthSquare() { // return AlgoPointND.getDistanceToOriginSquare(this.coordinate); // } // TODO JR: Wenn du nichts dagegen hast, den folgenden teil bitte rausloeschen. solche methoden werden in robuster form in jts angeboten. // public double cross2D(DirectPositionImpl dp){ // // corresponds to the 2*area of two vectors // return this.getX() * dp.getY() - this.getY() * dp.getX(); // } // // public boolean intersectWithHorizontalLineFromRight2D(DirectPositionImpl // p0, DirectPositionImpl p1){ // // returns true when a horizontal line passing at ME: // // 1) intersects the line with origin p0 and and p1 and // // 2) when ME is on the right side of the line // double x0 = p0.getX(); // line endpoint 2D coords // double y0 = p0.getY(); // line endpoint 2D coords // double x1 = p1.getX(); // line endpoint 2D coords // double y1 = p1.getY(); // line endpoint 2D coords // double xa = x0; // swap coordinates // double ya = y0; // swap coordinates // double xb = x1; // swap coordinates // double yb = y1; // swap coordinates // double max_x = Math.max(x0, x1); // maximum x coordinate // double min_x = Math.min(x0, x1); // minimum x coordinate // double max_y = Math.max(y0, y1); // maximum y coordinate // double min_y = Math.min(y0, y1); // minimum y coordinate // // // the horizontal line does not intersect the line to the // // left of location pt if: // // [1] if line is horizontal // if ( y0 == y1 ) return false; // // (2) if the y coordinate of point is outside the range // // max_y and min_y (but not including min_y) // if ( ((this.getY() < min_y) || (this.getY() >= max_y)) ) return false; // // (3) if given line is vertical and y coordinate of point is // // smaller than that of line // if ( ((x0 == x1) && (this.getX() < x0)) ) return false; // // (4) if inclined line is located to the right of given point // // (first reduce the problem to a case where yb >ya, always) // if ( (x0 != x1) ) { // if ( !(((x1 > x0) && (y1 > y0)) || ((x1 < x0) && (y1 > y0))) ) { // xa = x1; // ya = y1; // xb = x0; // yb = y0; // } // if ( (((this.getY() - ya) * (xb - xa)) > ((this.getX() - xa) * (yb - // ya))) ) { // return false; // } // } // // if we get here that is because the horizontal line passing // // at the location this intersects the given line to the left of the pt // return true; // } // // public double getAngle2D(DirectPositionImpl p1){ // // * p1 // // / // // / // // / // // *------>* // // (0,0) this // double angle = Math.atan2(p1.getY(), p1.getX()) - Math.atan2(this.getY(), // this.getX()); // if ( angle < 0.0 ) angle = angle + 2 * Math.PI; // if ( angle > (2 * Math.PI) ) angle = angle - 2 * Math.PI; // return angle; // } // // public double minAngle2D(DirectPositionImpl p1, DirectPositionImpl p2){ // double ang0 = ((DirectPositionImpl) // p1.subtract(this)).getAngle2D((DirectPositionImpl)p2.subtract(this)); // double ang1 = // ((DirectPositionImpl)p2.subtract(p1)).getAngle2D((DirectPositionImpl)this.subtract(p1)); // return Math.min(Math.min(ang0, ang1), Math.min(ang0, Math.PI - ang0 - // ang1)); // } // // public String toString() { // String str = Double.toString(this.coordinate[0]); // for (int i=1; i<this.getDimension(); ++i) { // str += "| " + Double.toString(this.coordinate[i]); // } // return "[DirectPosition: " + str + "]"; // } // // /** // * Builds the scalar product // * @param p // * @return Scalar product // */ // public double scalar(DirectPositionImpl p) { // double result = 0.0; // double pCoord[] = p.getCoordinate(); // for (int i=0; i<this.getDimension(); ++i) { // result += this.coordinate[i] * pCoord[i]; // } // return result; // } // // public static Object cross(DirectPositionImpl p0, DirectPositionImpl p1){ // int n = Math.min(p0.getDimension(),p1.getDimension()); // if (n==2) { // // corresponds to the 2*area of two vectors // double p0Coord[] = p0.getCoordinate(); // double p1Coord[] = p1.getCoordinate(); // return (Double)p0Coord[0] * p1Coord[1] - p0Coord[1] * p1Coord[0]; // } else if (n==3) { // // TODO // assert false; // DirectPositionImpl result = null; // return null; // } else { // assert false; // return null; // } // } }