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 }