/*---------------- 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.resources;
// OpenGIS dependencies (SEAGIS)
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import org.deegree.model.csct.cs.AxisInfo;
import org.deegree.model.csct.cs.AxisOrientation;
import org.deegree.model.csct.cs.CompoundCoordinateSystem;
import org.deegree.model.csct.cs.CoordinateSystem;
import org.deegree.model.csct.cs.GeographicCoordinateSystem;
import org.deegree.model.csct.cs.HorizontalCoordinateSystem;
import org.deegree.model.csct.cs.HorizontalDatum;
import org.deegree.model.csct.cs.TemporalCoordinateSystem;
import org.deegree.model.csct.cs.TemporalDatum;
import org.deegree.model.csct.cs.VerticalCoordinateSystem;
import org.deegree.model.csct.cs.VerticalDatum;
import org.deegree.model.csct.ct.CoordinateTransformation;
import org.deegree.model.csct.ct.CoordinateTransformationFactory;
import org.deegree.model.csct.ct.MathTransform;
import org.deegree.model.csct.ct.MathTransform2D;
import org.deegree.model.csct.ct.TransformException;
import org.deegree.model.csct.pt.AngleFormat;
import org.deegree.model.csct.pt.CoordinatePoint;
import org.deegree.model.csct.pt.Envelope;
import org.deegree.model.csct.pt.Latitude;
import org.deegree.model.csct.pt.Longitude;
import org.deegree.model.csct.pt.MismatchedDimensionException;
import org.deegree.model.csct.resources.css.ResourceKeys;
import org.deegree.model.csct.resources.css.Resources;
/**
* A set of static methods working on OpenGIS objects. Some of those methods
* are useful, but not really rigorous. This is why they do not appear in the
* "official" package, but instead in this private one. <strong>Do not rely on
* this API!</strong> It may change in incompatible way in any future version.
*
* @version 1.0
* @author Martin Desruisseaux
*/
public final class OpenGIS {
/**
* Do not allow creation of
* instances of this class.
*/
private OpenGIS() {
}
/**
* Returns the dimension of the first axis of a particular type.
* For example, <code>getDimensionOf(cs, AxisInfo.TIME)</code>
* would returns the dimension number of time axis.
*/
public static int getDimensionOf( final CoordinateSystem cs, final AxisInfo axis ) {
final int dimension = cs.getDimension();
final AxisOrientation orientation = axis.orientation.absolute();
for ( int i = 0; i < dimension; i++ )
if ( orientation.equals( cs.getAxis( i ).orientation.absolute() ) )
return i;
return -1;
}
/**
* Returns a two-dimensional coordinate system representing the two first dimensions
* of the specified coordinate system. If <code>cs</code> is already a two-dimensional
* coordinate system, then it is returned unchanged. Otherwise, if it is a
* {@link CompoundCoordinateSystem}, then the head coordinate system is examined.
*
* @param cs The coordinate system.
* @return A two-dimensional coordinate system that represents the two first
* dimensions of <code>cs</code>.
* @throws IllegalArgumentException if <code>cs</code> can't be reduced to
* a two-coordinate system.
*/
public static CoordinateSystem getCoordinateSystem2D( CoordinateSystem cs )
throws IllegalArgumentException {
if ( cs != null ) {
while ( cs.getDimension() != 2 ) {
if ( !( cs instanceof CompoundCoordinateSystem ) ) {
throw new IllegalArgumentException(
Resources.format(
ResourceKeys.ERROR_CANT_REDUCE_TO_TWO_DIMENSIONS_$1,
cs.getName( null ) ) );
}
cs = ( (CompoundCoordinateSystem) cs ).getHeadCS();
}
}
return cs;
}
/**
* Returns the first horizontal datum found in a coordinate system,
* or <code>null</code> if there is none. Note: in a future version,
* we may implement this method directly into {@link CoordinateSystem}
* (not sure yet if it would be a good idea).
*/
public static HorizontalDatum getHorizontalDatum( final CoordinateSystem cs ) {
if ( cs instanceof HorizontalCoordinateSystem ) {
return ( (HorizontalCoordinateSystem) cs ).getHorizontalDatum();
}
if ( cs instanceof CompoundCoordinateSystem ) {
HorizontalDatum datum;
final CompoundCoordinateSystem comp = (CompoundCoordinateSystem) cs;
if ( ( datum = getHorizontalDatum( comp.getHeadCS() ) ) != null )
return datum;
if ( ( datum = getHorizontalDatum( comp.getTailCS() ) ) != null )
return datum;
}
return null;
}
/**
* Returns the first vertical datum found in a coordinate system,
* or <code>null</code> if there is none. Note: if a future version,
* we may implement this method directly into {@link CoordinateSystem}
* (not sure yet if it would be a good idea).
*/
public static VerticalDatum getVerticalDatum( final CoordinateSystem cs ) {
if ( cs instanceof VerticalCoordinateSystem ) {
return ( (VerticalCoordinateSystem) cs ).getVerticalDatum();
}
if ( cs instanceof CompoundCoordinateSystem ) {
VerticalDatum datum;
final CompoundCoordinateSystem comp = (CompoundCoordinateSystem) cs;
if ( ( datum = getVerticalDatum( comp.getHeadCS() ) ) != null )
return datum;
if ( ( datum = getVerticalDatum( comp.getTailCS() ) ) != null )
return datum;
}
return null;
}
/**
* Returns the first temporal datum found in a coordinate system,
* or <code>null</code> if there is none. Note: if a future version,
* we may implement this method directly into {@link CoordinateSystem}
* (not sure yet if it would be a good idea).
*/
public static TemporalDatum getTemporalDatum( final CoordinateSystem cs ) {
if ( cs instanceof TemporalCoordinateSystem ) {
return ( (TemporalCoordinateSystem) cs ).getTemporalDatum();
}
if ( cs instanceof CompoundCoordinateSystem ) {
TemporalDatum datum;
final CompoundCoordinateSystem comp = (CompoundCoordinateSystem) cs;
if ( ( datum = getTemporalDatum( comp.getHeadCS() ) ) != null )
return datum;
if ( ( datum = getTemporalDatum( comp.getTailCS() ) ) != null )
return datum;
}
return null;
}
/**
* Transform an envelope. The transformation is only approximative.
*
* @param transform The transform to use.
* @param envelope Envelope to transform. This envelope will not be modified.
* @return The transformed envelope. It may not have the same number of dimensions
* than the original envelope.
* @throws TransformException if a transform failed.
*/
public static Envelope transform( final MathTransform transform, final Envelope envelope )
throws TransformException {
final int sourceDim = transform.getDimSource();
final int targetDim = transform.getDimTarget();
if ( envelope.getDimension() != sourceDim ) {
throw new MismatchedDimensionException( sourceDim, envelope.getDimension() );
}
int coordinateNumber = 0;
Envelope transformed = null;
CoordinatePoint targetPt = null;
final CoordinatePoint sourcePt = new CoordinatePoint( sourceDim );
for ( int i = sourceDim; --i >= 0; )
sourcePt.ord[i] = envelope.getMinimum( i );
loop: do {
// Transform a point and add the transformed
// point to the destination envelope.
targetPt = transform.transform( sourcePt, targetPt );
if ( transformed != null )
transformed.add( targetPt );
else
transformed = new Envelope( targetPt, targetPt );
// Get the next point's coordinate. The 'coordinateNumber' variable should
// be seen as a number in base 3 where the number of digits is equals to the
// number of dimensions. For example, a 4-D space would have numbers ranging
// from "0000" to "2222". The digits are then translated into minimal, central
// or maximal ordinates.
int n = ++coordinateNumber;
for ( int i = sourceDim; --i >= 0; ) {
switch ( n % 3 ) {
case 0:
sourcePt.ord[i] = envelope.getMinimum( i );
n /= 3;
break;
case 1:
sourcePt.ord[i] = envelope.getCenter( i );
continue loop;
case 2:
sourcePt.ord[i] = envelope.getMaximum( i );
continue loop;
}
}
break;
} while ( true );
return transformed;
}
/**
* Transform an envelope. The transformation is only approximative.
* Invoking this method is equivalent to invoking the following:
* <br>
* <pre>transform(transform, new Envelope(source)).toRectangle2D()</pre>
*
* @param transform The transform to use. Source and target dimension must be 2.
* @param source The rectangle to transform (may be <code>null</code>).
* @param dest The destination rectangle (may be <code>source</code>).
* If <code>null</code>, a new rectangle will be created and returned.
* @return <code>dest</code>, or a new rectangle if <code>dest</code> was non-null
* and <code>source</code> was null.
* @throws TransformException if a transform failed.
*/
public static Rectangle2D transform( final MathTransform2D transform, final Rectangle2D source,
final Rectangle2D dest )
throws TransformException {
if ( source == null ) {
return null;
}
double xmin = Double.POSITIVE_INFINITY;
double ymin = Double.POSITIVE_INFINITY;
double xmax = Double.NEGATIVE_INFINITY;
double ymax = Double.NEGATIVE_INFINITY;
final Point2D.Double point = new Point2D.Double();
for ( int i = 0; i < 8; i++ ) {
/*
* (0)----(5)----(1)
* | |
* (4) (7)
* | |
* (2)----(6)----(3)
*/
point.x = ( i & 1 ) == 0 ? source.getMinX() : source.getMaxX();
point.y = ( i & 2 ) == 0 ? source.getMinY() : source.getMaxY();
switch ( i ) {
case 5: // fallthrough
case 6:
point.x = source.getCenterX();
break;
case 7: // fallthrough
case 4:
point.y = source.getCenterY();
break;
}
transform.transform( point, point );
if ( point.x < xmin )
xmin = point.x;
if ( point.x > xmax )
xmax = point.x;
if ( point.y < ymin )
ymin = point.y;
if ( point.y > ymax )
ymax = point.y;
}
if ( dest != null ) {
dest.setRect( xmin, ymin, xmax - xmin, ymax - ymin );
return dest;
}
return new XRectangle2D( xmin, ymin, xmax - xmin, ymax - ymin );
}
/**
* Retourne une cha�ne de caract�res repr�sentant la r�gion g�ographique sp�cifi�e. La
* cha�ne retourn�e sera de la forme "45�00.00'N-50�00.00'N 30�00.00'E-40�00.00'E". Si
* une projection cartographique est n�cessaire pour obtenir cette repr�sentation, elle
* sera faite automatiquement. Cette cha�ne sert surtout � des fins de d�boguage et sa
* forme peut varier.
*/
public static String toWGS84String( final CoordinateSystem cs, Rectangle2D bounds ) {
StringBuffer buffer = new StringBuffer();
try {
if ( !GeographicCoordinateSystem.WGS84.equivalents( cs ) ) {
final CoordinateTransformation tr = CoordinateTransformationFactory.getDefault().createFromCoordinateSystems(
cs,
GeographicCoordinateSystem.WGS84 );
bounds = transform( (MathTransform2D) tr.getMathTransform(), bounds, null );
}
final AngleFormat fmt = new AngleFormat( "DD�MM.m'" );
buffer = fmt.format( new Latitude( bounds.getMinY() ), buffer, null );
buffer.append( '-' );
buffer = fmt.format( new Latitude( bounds.getMaxY() ), buffer, null );
buffer.append( ' ' );
buffer = fmt.format( new Longitude( bounds.getMinX() ), buffer, null );
buffer.append( '-' );
buffer = fmt.format( new Longitude( bounds.getMaxX() ), buffer, null );
} catch ( TransformException exception ) {
buffer.append( Utilities.getShortClassName( exception ) );
final String message = exception.getLocalizedMessage();
if ( message != null ) {
buffer.append( ": " );
buffer.append( message );
}
}
return buffer.toString();
}
}