001 //$HeadURL: $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 This file is part of deegree. 004 Copyright (C) 2001-2008 by: 005 Department of Geography, University of Bonn 006 http://www.giub.uni-bonn.de/deegree/ 007 lat/lon GmbH 008 http://www.lat-lon.de 009 010 This library is free software; you can redistribute it and/or 011 modify it under the terms of the GNU Lesser General Public 012 License as published by the Free Software Foundation; either 013 version 2.1 of the License, or (at your option) any later version. 014 This library is distributed in the hope that it will be useful, 015 but WITHOUT ANY WARRANTY; without even the implied warranty of 016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 017 Lesser General Public License for more details. 018 You should have received a copy of the GNU Lesser General Public 019 License along with this library; if not, write to the Free Software 020 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 021 Contact: 022 023 Andreas Poth 024 lat/lon GmbH 025 Aennchenstr. 19 026 53177 Bonn 027 Germany 028 E-Mail: poth@lat-lon.de 029 030 Prof. Dr. Klaus Greve 031 Department of Geography 032 University of Bonn 033 Meckenheimer Allee 166 034 53115 Bonn 035 Germany 036 E-Mail: greve@giub.uni-bonn.de 037 ---------------------------------------------------------------------------*/ 038 039 package org.deegree.crs.transformations; 040 041 import java.util.ArrayList; 042 import java.util.List; 043 044 import javax.vecmath.Point3d; 045 046 import org.deegree.crs.components.Ellipsoid; 047 import org.deegree.crs.components.Unit; 048 import org.deegree.crs.coordinatesystems.GeocentricCRS; 049 import org.deegree.crs.coordinatesystems.GeographicCRS; 050 import org.deegree.crs.projections.ProjectionUtils; 051 052 /** 053 * The <code>GeocentricTransform</code> class is used to create a transformation between a geocentric CRS (having 054 * lat-lon coordinates) and it's geodetic CRS (having x-y-z) coordinates and vice versa. 055 * 056 * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a> 057 * 058 * @author last edited by: $Author:$ 059 * 060 * @version $Revision:$, $Date:$ 061 * 062 */ 063 064 public class GeocentricTransform extends CRSTransformation { 065 066 /** 067 * Cosine of 67.5 degrees. 068 */ 069 private static final double COS_67P5 = 0.38268343236508977; 070 071 /** 072 * Toms region 1 constant, which will allow for a difference between ellipsoid of 2000 Kilometers. <quote>Under this 073 * policy the maximum error is less than 42 centimeters for altitudes less then 10.000.000 Kilometers</quote> 074 */ 075 private static final double AD_C = 1.0026; 076 077 /** 078 * Semi-major axis of ellipsoid in meters. 079 */ 080 private final double semiMajorAxis; 081 082 /** 083 * Semi-minor axis of ellipsoid in meters. 084 */ 085 private final double semiMinorAxis; 086 087 /** 088 * Square of semi-major axis {@link #semiMajorAxis}. 089 */ 090 private final double squaredSemiMajorAxis; 091 092 /** 093 * Square of semi-minor axis {@link #semiMinorAxis}. 094 */ 095 private final double squaredSemiMinorAxis; 096 097 /** 098 * Eccentricity squared. 099 */ 100 private final double squaredEccentricity; 101 102 /** 103 * 2nd eccentricity squared. 104 */ 105 private final double ep2; 106 107 /** 108 * true if the given points will use heights (e.g. have a z/height-value). 109 */ 110 private boolean hasHeight; 111 112 /** 113 * @param source 114 * the geographic crs. 115 * @param target 116 * the geocentric crs. 117 * @param identifier 118 * @param name 119 * @param version 120 * @param description 121 * @param areaOfUse 122 */ 123 public GeocentricTransform( GeographicCRS source, GeocentricCRS target, String identifier, String version, 124 String description, String areaOfUse ) { 125 super( source, target, identifier, "Geocentric-Transform", version, description, areaOfUse ); 126 this.hasHeight = source.getDimension() == 3; 127 Ellipsoid ellipsoid = source.getGeodeticDatum().getEllipsoid(); 128 semiMajorAxis = Unit.METRE.convert( ellipsoid.getSemiMajorAxis(), ellipsoid.getUnits() ); 129 semiMinorAxis = Unit.METRE.convert( ellipsoid.getSemiMinorAxis(), ellipsoid.getUnits() ); 130 squaredSemiMajorAxis = semiMajorAxis * semiMajorAxis; 131 squaredSemiMinorAxis = semiMinorAxis * semiMinorAxis; 132 squaredEccentricity = ellipsoid.getSquaredEccentricity(); 133 // e2 = ( a2 - b2 ) / a2; 134 ep2 = ( squaredSemiMajorAxis - squaredSemiMinorAxis ) / squaredSemiMinorAxis; 135 } 136 137 /** 138 * @param source 139 * the geographic crs. 140 * @param target 141 * the geocentric crs. 142 * @param identifier 143 */ 144 public GeocentricTransform( GeographicCRS source, GeocentricCRS target, String identifier ) { 145 this( source, target, identifier, null, null, null ); 146 } 147 148 /** 149 * This constructor creates an identifier with following form: <code> 150 * FROM_id1_TO_id2.</code> The name will be 151 * generated the same way (with getName() instead). 152 * 153 * @param source 154 * the geographic crs. 155 * @param target 156 * the geocentric crs. 157 */ 158 public GeocentricTransform( GeographicCRS source, GeocentricCRS target ) { 159 this( source, target, new StringBuilder( "FROM_" ).append( source.getIdentifier() ) 160 .append( "_TO_" ) 161 .append( target.getIdentifier() ) 162 .toString() ); 163 } 164 165 @Override 166 public List<Point3d> doTransform( List<Point3d> srcPts ) { 167 List<Point3d> result = new ArrayList<Point3d>( srcPts ); 168 if ( isInverseTransform() ) { 169 // System.out.println( "An inverse geocentric transform with incoming points: " + srcPts ); 170 toGeographic( result ); 171 } else { 172 // System.out.println( "A geocentric transform with incoming points: x: " + Math.toDegrees( srcPts.get(0).x) 173 // + ", y: " + Math.toDegrees( srcPts.get(0).y ) ); 174 toGeoCentric( result ); 175 } 176 return result; 177 } 178 179 /** 180 * Converts geocentric coordinates (x, y, z) to geodetic coordinates (longitude, latitude, height), according to the 181 * current ellipsoid parameters. The method used here is derived from "An Improved Algorithm for Geocentric to 182 * Geodetic Coordinate Conversion", by Ralph Toms, Feb 1996 UCRL-JC-123138. 183 * 184 * @param srcPts 185 * the points which must be transformed. 186 */ 187 protected void toGeographic( List<Point3d> srcPts ) { 188 for ( Point3d p : srcPts ) { 189 // Note: Variable names follow the notation used in Toms, Feb 1996 190 // final double W2 = x * x + y * y; // square of distance from Z axis 191 192 final double T0 = p.z * AD_C; // initial estimate of vertical component 193 final double W = ProjectionUtils.length( p.x, p.y );// Math.sqrt( W2 ); // distance from Z axis 194 final double S0 = ProjectionUtils.length( T0, W );// Math.sqrt( T0 * T0 + W*W ); // initial estimate of 195 // horizontal 196 // component 197 198 final double sin_B0 = T0 / S0; // sin(B0), B0 is estimate of Bowring variable 199 final double cos_B0 = W / S0; // cos(B0) 200 final double sin3_B0 = sin_B0 * sin_B0 * sin_B0; // cube of sin(B0) 201 final double T1 = p.z + semiMinorAxis * ep2 * sin3_B0; // corrected estimate of vertical component 202 203 // numerator of cos(phi1) 204 final double sum = W - semiMajorAxis * squaredEccentricity * ( cos_B0 * cos_B0 * cos_B0 ); 205 206 // corrected estimate of horizontal component 207 final double S1 = ProjectionUtils.length( T1, sum );// Math.sqrt( T1 * T1 + sum * sum ); 208 209 // sin(phi), phi is estimated latitude 210 final double sinPhi = T1 / S1; 211 final double cosPhi = sum / S1; // cos(phi) 212 213 //Lambda in tom. 214 p.x = Math.atan2( p.y, p.x );// longitude; 215 p.y = Math.atan( sinPhi / cosPhi );// latitude; 216 if ( hasHeight ) { 217 double height = 1; 218 //rn = radius of curvature of the prime vertical, of the ellipsoid at location 219 final double rn = semiMajorAxis / Math.sqrt( 1 - squaredEccentricity * ( sinPhi * sinPhi ) ); 220 221 if ( cosPhi >= +COS_67P5 ) { 222 height = W / +cosPhi - rn; 223 } else if ( cosPhi <= -COS_67P5 ) { 224 height = W / -cosPhi - rn; 225 } else { 226 height = p.z / sinPhi + rn * ( squaredEccentricity - 1.0 ); 227 } 228 p.z = height; 229 } 230 } 231 } 232 233 protected void toGeoCentric( List<Point3d> srcPts ) { 234 for ( Point3d p : srcPts ) { 235 final double lambda = p.x; // Longitude 236 final double phi = p.y; // Latitude 237 final double h = hasHeight ? p.z : 0; // Height above the ellipsoid (metres). 238 239 final double cosPhi = Math.cos( phi ); 240 final double sinPhi = Math.sin( phi ); 241 final double rn = semiMajorAxis / Math.sqrt( 1 - squaredEccentricity * ( sinPhi * sinPhi ) ); 242 243 p.x = ( rn + h ) * cosPhi * Math.cos( lambda ); 244 p.y = ( rn + h ) * cosPhi * Math.sin( lambda ); 245 p.z = ( rn * ( 1 - squaredEccentricity ) + h ) * sinPhi; 246 } 247 } 248 249 @Override 250 public boolean isIdentity() { 251 return false; 252 } 253 254 /** 255 * @return the semiMajorAxis. 256 */ 257 public final double getSemiMajorAxis() { 258 return semiMajorAxis; 259 } 260 261 /** 262 * @return the semiMinorAxis. 263 */ 264 public final double getSemiMinorAxis() { 265 return semiMinorAxis; 266 } 267 268 }