001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/csct/ct/LambertConformalProjection.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001 by: 006 EXSE, Department of Geography, University of Bonn 007 http://www.giub.uni-bonn.de/exse/ 008 lat/lon GmbH 009 http://www.lat-lon.de 010 011 It has been implemented within SEAGIS - An OpenSource implementation of OpenGIS specification 012 (C) 2001, Institut de Recherche pour le D�veloppement (http://sourceforge.net/projects/seagis/) 013 SEAGIS Contacts: Surveillance de l'Environnement Assist�e par Satellite 014 Institut de Recherche pour le D�veloppement / US-Espace 015 mailto:seasnet@teledetection.fr 016 017 018 This library is free software; you can redistribute it and/or 019 modify it under the terms of the GNU Lesser General Public 020 License as published by the Free Software Foundation; either 021 version 2.1 of the License, or (at your option) any later version. 022 023 This library is distributed in the hope that it will be useful, 024 but WITHOUT ANY WARRANTY; without even the implied warranty of 025 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 026 Lesser General Public License for more details. 027 028 You should have received a copy of the GNU Lesser General Public 029 License along with this library; if not, write to the Free Software 030 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 031 032 Contact: 033 034 Andreas Poth 035 lat/lon GmbH 036 Aennchenstr. 19 037 53115 Bonn 038 Germany 039 E-Mail: poth@lat-lon.de 040 041 Klaus Greve 042 Department of Geography 043 University of Bonn 044 Meckenheimer Allee 166 045 53115 Bonn 046 Germany 047 E-Mail: klaus.greve@uni-bonn.de 048 049 050 ---------------------------------------------------------------------------*/ 051 package org.deegree.model.csct.ct; 052 053 // OpenGIS (SEAS) dependencies 054 import java.awt.geom.Point2D; 055 056 import org.deegree.model.csct.cs.Projection; 057 import org.deegree.model.csct.pt.Latitude; 058 import org.deegree.model.csct.resources.css.ResourceKeys; 059 import org.deegree.model.csct.resources.css.Resources; 060 061 /** 062 * Projection conique conforme de Lambert. Les aires et les formes sont d�form�es 063 * � mesure que l'on s'�loigne de parall�les standards. Les angles sont vrais dans 064 * une r�gion limit�e. Cette projection est utilis�e pour les cartes de l'Am�rique 065 * du Nord. Elle utilise par d�faut une latitude centrale de 40�N. 066 * <br><br> 067 * 068 * R�f�rence: John P. Snyder (Map Projections - A Working Manual, 069 * U.S. Geological Survey Professional Paper 1395, 1987) 070 * 071 * @version 1.0 072 * @author Andr� Gosselin 073 * @author Martin Desruisseaux 074 */ 075 final class LambertConformalProjection extends ConicProjection { 076 /** 077 * Standards parallels in radians, for 078 * {@link #toString} implementation. 079 */ 080 private final double phi1, phi2; 081 082 /** 083 * Variables internes 084 * pour les calculs. 085 */ 086 private final double n, F, rho0; 087 088 /** 089 * Construct a new map projection from the suplied parameters. 090 * 091 * @param parameters The parameter values in standard units. 092 * @throws MissingParameterException if a mandatory parameter is missing. 093 */ 094 protected LambertConformalProjection( final Projection parameters ) 095 throws MissingParameterException { 096 ////////////////////////// 097 // Fetch parameters // 098 ////////////////////////// 099 super( parameters ); 100 phi1 = latitudeToRadians( parameters.getValue( "standard_parallel1", 30 ), true ); 101 phi2 = latitudeToRadians( parameters.getValue( "standard_parallel2", 45 ), true ); 102 103 ////////////////////////// 104 // Compute constants // 105 ////////////////////////// 106 if ( Math.abs( phi1 + phi2 ) < EPS ) { 107 throw new IllegalArgumentException( 108 Resources.format( 109 ResourceKeys.ERROR_ANTIPODE_LATITUDES_$2, 110 new Latitude( 111 Math.toDegrees( phi1 ) ), 112 new Latitude( 113 Math.toDegrees( phi2 ) ) ) ); 114 } 115 final double cosphi = Math.cos( phi1 ); 116 final double sinphi = Math.sin( phi1 ); 117 final boolean secant = Math.abs( phi1 - phi2 ) > EPS; 118 if ( isSpherical ) { 119 if ( secant ) { 120 n = Math.log( cosphi / Math.cos( phi2 ) ) 121 / Math.log( Math.tan( ( Math.PI / 4 ) + 0.5 * phi2 ) 122 / Math.tan( ( Math.PI / 4 ) + 0.5 * phi1 ) ); 123 } else 124 n = sinphi; 125 F = cosphi * Math.pow( Math.tan( ( Math.PI / 4 ) + 0.5 * phi1 ), n ) / n; 126 if ( Math.abs( Math.abs( centralLatitude ) - ( Math.PI / 2 ) ) >= EPS ) { 127 rho0 = F * Math.pow( Math.tan( ( Math.PI / 4 ) + 0.5 * centralLatitude ), -n ); 128 } else 129 rho0 = 0.0; 130 } else { 131 final double m1 = msfn( sinphi, cosphi ); 132 final double t1 = tsfn( phi1, sinphi ); 133 if ( secant ) { 134 final double sinphi2 = Math.sin( phi2 ); 135 final double m2 = msfn( sinphi2, Math.cos( phi2 ) ); 136 final double t2 = tsfn( phi2, sinphi2 ); 137 n = Math.log( m1 / m2 ) / Math.log( t1 / t2 ); 138 } else 139 n = sinphi; 140 F = m1 * Math.pow( t1, -n ) / n; 141 if ( Math.abs( Math.abs( centralLatitude ) - ( Math.PI / 2 ) ) >= EPS ) { 142 rho0 = F * Math.pow( tsfn( centralLatitude, Math.sin( centralLatitude ) ), n ); 143 } else 144 rho0 = 0.0; 145 } 146 } 147 148 /** 149 * Returns a human readable name localized for the specified locale. 150 */ 151 public String getName() { 152 return Resources.getResources( null ).getString( 153 ResourceKeys.LAMBERT_CONFORMAL_PROJECTION ); 154 } 155 156 /** 157 * Transforms the specified (<var>x</var>,<var>y</var>) coordinate 158 * and stores the result in <code>ptDst</code>. 159 */ 160 protected Point2D transform( double x, double y, final Point2D ptDst ) 161 throws TransformException { 162 x -= centralMeridian; 163 double rho; 164 if ( Math.abs( Math.abs( y ) - ( Math.PI / 2 ) ) < EPS ) { 165 if ( y * n <= 0 ) { 166 throw new TransformException( Resources.format( ResourceKeys.ERROR_POLE_PROJECTION_$1, 167 new Latitude( Math.toDegrees( y ) ) ) ); 168 } 169 rho = 0; 170 } else { 171 if ( isSpherical ) 172 rho = Math.pow( Math.tan( ( Math.PI / 4d ) + 0.5 * y ), -n ); 173 else 174 rho = Math.pow( tsfn( y, Math.sin( y ) ), n ); 175 rho *= F; 176 } 177 178 x = n * x; 179 y = a * ( rho0 - rho * Math.cos( x ) ) + false_northing; 180 x = a * ( rho * Math.sin( x ) ) + false_easting; 181 182 if ( ptDst != null ) { 183 ptDst.setLocation( x, y ); 184 return ptDst; 185 } 186 return new Point2D.Double( x, y ); 187 } 188 189 /** 190 * Transforms the specified (<var>x</var>,<var>y</var>) coordinate 191 * and stores the result in <code>ptDst</code>. 192 */ 193 protected Point2D inverseTransform( double x, double y, final Point2D ptDst ) 194 throws TransformException { 195 x -= false_easting; 196 y -= false_northing; 197 x /= a; 198 y = rho0 - y / a; 199 double rho = Math.sqrt( x * x + y * y ); 200 if ( rho > EPS ) { 201 if ( n < 0 ) { 202 rho = -rho; 203 x = -x; 204 y = -y; 205 } 206 x = centralMeridian + Math.atan2( x, y ) / n; 207 if ( isSpherical ) { 208 y = 2.0 * Math.atan( Math.pow( F / rho, 1.0 / n ) ) - ( Math.PI / 2 ); 209 } else { 210 y = cphi2( Math.pow( rho / F, 1.0 / n ) ); 211 } 212 } else { 213 x = centralMeridian; 214 y = n < 0 ? -( Math.PI / 2 ) : ( Math.PI / 2 ); 215 } 216 if ( ptDst != null ) { 217 ptDst.setLocation( x, y ); 218 return ptDst; 219 } 220 return new Point2D.Double( x, y ); 221 } 222 223 /** 224 * Returns a hash value for this projection. 225 */ 226 public int hashCode() { 227 final long code = Double.doubleToLongBits( F ); 228 return ( (int) code ^ (int) ( code >>> 32 ) ) + 37 * super.hashCode(); 229 } 230 231 /** 232 * Compares the specified object with 233 * this map projection for equality. 234 */ 235 public boolean equals( final Object object ) { 236 if ( object == this ) 237 return true; // Slight optimization 238 if ( super.equals( object ) ) { 239 final LambertConformalProjection that = (LambertConformalProjection) object; 240 return Double.doubleToLongBits( this.n ) == Double.doubleToLongBits( that.n ) 241 && Double.doubleToLongBits( this.F ) == Double.doubleToLongBits( that.F ) 242 && Double.doubleToLongBits( this.rho0 ) == Double.doubleToLongBits( that.rho0 ); 243 } 244 return false; 245 } 246 247 /** 248 * Impl�mentation de la partie entre crochets 249 * de la cha�ne retourn�e par {@link #toString()}. 250 */ 251 void toString( final StringBuffer buffer ) { 252 super.toString( buffer ); 253 addParameter( buffer, "standard_parallel1", Math.toDegrees( phi1 ) ); 254 addParameter( buffer, "standard_parallel2", Math.toDegrees( phi2 ) ); 255 } 256 257 /** 258 * Informations about a {@link LambertConformalProjection}. 259 * 260 * @version 1.0 261 * @author Martin Desruisseaux 262 */ 263 static final class Provider extends MapProjection.Provider { 264 /** 265 * Construct a new provider. 266 */ 267 public Provider() { 268 super( "Lambert_Conformal_Conic_2SP", ResourceKeys.LAMBERT_CONFORMAL_PROJECTION ); 269 put( "standard_parallel1", 30.0, LATITUDE_RANGE ); 270 put( "standard_parallel2", 45.0, LATITUDE_RANGE ); 271 } 272 273 /** 274 * Create a new map projection. 275 */ 276 protected Object create( final Projection parameters ) { 277 return new LambertConformalProjection( parameters ); 278 } 279 } 280 }