/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001 by:
EXSE, Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/exse/
lat/lon GmbH
http://www.lat-lon.de
It has been implemented within SEAGIS - An OpenSource implementation of OpenGIS specification
(C) 2001, Institut de Recherche pour le D�veloppement (http://sourceforge.net/projects/seagis/)
SEAGIS Contacts: Surveillance de l'Environnement Assist�e par Satellite
Institut de Recherche pour le D�veloppement / US-Espace
mailto:seasnet@teledetection.fr
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; either
version 2.1 of the License, or (at your option) any later version.
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.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
Andreas Poth
lat/lon GmbH
Aennchenstr. 19
53115 Bonn
Germany
E-Mail: poth@lat-lon.de
Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: klaus.greve@uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.model.csct.ct;
// OpenGIS (SEAS) dependencies
import java.awt.geom.Point2D;
import java.util.Locale;
import org.deegree.model.csct.cs.Projection;
import org.deegree.model.csct.pt.Latitude;
import org.deegree.model.csct.resources.css.ResourceKeys;
import org.deegree.model.csct.resources.css.Resources;
/**
* Projection conique conforme de Lambert. Les aires et les formes sont d�form�es
* � mesure que l'on s'�loigne de parall�les standards. Les angles sont vrais dans
* une r�gion limit�e. Cette projection est utilis�e pour les cartes de l'Am�rique
* du Nord. Elle utilise par d�faut une latitude centrale de 40�N.
* <br><br>
*
* R�f�rence: John P. Snyder (Map Projections - A Working Manual,
* U.S. Geological Survey Professional Paper 1395, 1987)
*
* @version 1.0
* @author Andr� Gosselin
* @author Martin Desruisseaux
*/
final class LambertConformalProjection extends ConicProjection {
/**
* Standards parallels in radians, for
* {@link #toString} implementation.
*/
private final double phi1, phi2;
/**
* Variables internes
* pour les calculs.
*/
private final double n, F, rho0;
/**
* Construct a new map projection from the suplied parameters.
*
* @param parameters The parameter values in standard units.
* @throws MissingParameterException if a mandatory parameter is missing.
*/
protected LambertConformalProjection( final Projection parameters )
throws MissingParameterException {
//////////////////////////
// Fetch parameters //
//////////////////////////
super( parameters );
phi1 = latitudeToRadians( parameters.getValue( "standard_parallel1", 30 ), true );
phi2 = latitudeToRadians( parameters.getValue( "standard_parallel2", 45 ), true );
//////////////////////////
// Compute constants //
//////////////////////////
if ( Math.abs( phi1 + phi2 ) < EPS ) {
throw new IllegalArgumentException(
Resources.format(
ResourceKeys.ERROR_ANTIPODE_LATITUDES_$2,
new Latitude(
Math.toDegrees( phi1 ) ),
new Latitude(
Math.toDegrees( phi2 ) ) ) );
}
final double cosphi = Math.cos( phi1 );
final double sinphi = Math.sin( phi1 );
final boolean secant = Math.abs( phi1 - phi2 ) > EPS;
if ( isSpherical ) {
if ( secant ) {
n = Math.log( cosphi / Math.cos( phi2 ) )
/ Math.log( Math.tan( ( Math.PI / 4 ) + 0.5 * phi2 )
/ Math.tan( ( Math.PI / 4 ) + 0.5 * phi1 ) );
} else
n = sinphi;
F = cosphi * Math.pow( Math.tan( ( Math.PI / 4 ) + 0.5 * phi1 ), n ) / n;
if ( Math.abs( Math.abs( centralLatitude ) - ( Math.PI / 2 ) ) >= EPS ) {
rho0 = F * Math.pow( Math.tan( ( Math.PI / 4 ) + 0.5 * centralLatitude ), -n );
} else
rho0 = 0.0;
} else {
final double m1 = msfn( sinphi, cosphi );
final double t1 = tsfn( phi1, sinphi );
if ( secant ) {
final double sinphi2 = Math.sin( phi2 );
final double m2 = msfn( sinphi2, Math.cos( phi2 ) );
final double t2 = tsfn( phi2, sinphi2 );
n = Math.log( m1 / m2 ) / Math.log( t1 / t2 );
} else
n = sinphi;
F = m1 * Math.pow( t1, -n ) / n;
if ( Math.abs( Math.abs( centralLatitude ) - ( Math.PI / 2 ) ) >= EPS ) {
rho0 = F * Math.pow( tsfn( centralLatitude, Math.sin( centralLatitude ) ), n );
} else
rho0 = 0.0;
}
}
/**
* Returns a human readable name localized for the specified locale.
*/
public String getName( final Locale locale ) {
return Resources.getResources( locale ).getString(
ResourceKeys.LAMBERT_CONFORMAL_PROJECTION );
}
/**
* Transforms the specified (<var>x</var>,<var>y</var>) coordinate
* and stores the result in <code>ptDst</code>.
*/
protected Point2D transform( double x, double y, final Point2D ptDst )
throws TransformException {
x -= centralMeridian;
double rho;
if ( Math.abs( Math.abs( y ) - ( Math.PI / 2 ) ) < EPS ) {
if ( y * n <= 0 ) {
throw new TransformException( Resources.format( ResourceKeys.ERROR_POLE_PROJECTION_$1,
new Latitude( Math.toDegrees( y ) ) ) );
}
rho = 0;
} else {
if ( isSpherical )
rho = Math.pow( Math.tan( ( Math.PI / 4d ) + 0.5 * y ), -n );
else
rho = Math.pow( tsfn( y, Math.sin( y ) ), n );
rho *= F;
}
x = n * x;
y = a * ( rho0 - rho * Math.cos( x ) ) + false_northing;
x = a * ( rho * Math.sin( x ) ) + false_easting;
if ( ptDst != null ) {
ptDst.setLocation( x, y );
return ptDst;
}
return new Point2D.Double( x, y );
}
/**
* Transforms the specified (<var>x</var>,<var>y</var>) coordinate
* and stores the result in <code>ptDst</code>.
*/
protected Point2D inverseTransform( double x, double y, final Point2D ptDst )
throws TransformException {
x -= false_easting;
y -= false_northing;
x /= a;
y = rho0 - y / a;
double rho = Math.sqrt( x * x + y * y );
if ( rho > EPS ) {
if ( n < 0 ) {
rho = -rho;
x = -x;
y = -y;
}
x = centralMeridian + Math.atan2( x, y ) / n;
if ( isSpherical ) {
y = 2.0 * Math.atan( Math.pow( F / rho, 1.0 / n ) ) - ( Math.PI / 2 );
} else {
y = cphi2( Math.pow( rho / F, 1.0 / n ) );
}
} else {
x = centralMeridian;
y = n < 0 ? -( Math.PI / 2 ) : ( Math.PI / 2 );
}
if ( ptDst != null ) {
ptDst.setLocation( x, y );
return ptDst;
}
return new Point2D.Double( x, y );
}
/**
* Returns a hash value for this projection.
*/
public int hashCode() {
final long code = Double.doubleToLongBits( F );
return ( (int) code ^ (int) ( code >>> 32 ) ) + 37 * super.hashCode();
}
/**
* Compares the specified object with
* this map projection for equality.
*/
public boolean equals( final Object object ) {
if ( object == this )
return true; // Slight optimization
if ( super.equals( object ) ) {
final LambertConformalProjection that = (LambertConformalProjection) object;
return Double.doubleToLongBits( this.n ) == Double.doubleToLongBits( that.n )
&& Double.doubleToLongBits( this.F ) == Double.doubleToLongBits( that.F )
&& Double.doubleToLongBits( this.rho0 ) == Double.doubleToLongBits( that.rho0 );
}
return false;
}
/**
* Impl�mentation de la partie entre crochets
* de la cha�ne retourn�e par {@link #toString()}.
*/
void toString( final StringBuffer buffer ) {
super.toString( buffer );
addParameter( buffer, "standard_parallel1", Math.toDegrees( phi1 ) );
addParameter( buffer, "standard_parallel2", Math.toDegrees( phi2 ) );
}
/**
* Informations about a {@link LambertConformalProjection}.
*
* @version 1.0
* @author Martin Desruisseaux
*/
static final class Provider extends MapProjection.Provider {
/**
* Construct a new provider.
*/
public Provider() {
super( "Lambert_Conformal_Conic_2SP", ResourceKeys.LAMBERT_CONFORMAL_PROJECTION );
put( "standard_parallel1", 30.0, LATITUDE_RANGE );
put( "standard_parallel2", 45.0, LATITUDE_RANGE );
}
/**
* Create a new map projection.
*/
protected Object create( final Projection parameters ) {
return new LambertConformalProjection( parameters );
}
}
}